packages/docs/src/pages/en/features/theme.md
Customize your application's default text colors, surfaces, and more. Easily modify your theme programmatically in real time. Vuetify comes with standard support for light and dark variants.
<PageFeatures /> <PromotedEntry />| Feature | Description |
|---|---|
| useTheme | The theme composable allows you to get information about, and modify the current theme |
| v-theme-provider | The theme provider component modifies the theme of all its children |
Vuetify includes three built-in themes: light, dark, and system. Use the defaultTheme option to specify your application's default theme. The following example sets the default theme to dark:
import { createApp } from 'vue'
import { createVuetify } from 'vuetify'
export default createVuetify({
theme: {
defaultTheme: 'dark', // 'system' | 'light' | 'dark'
},
})
The theme instance has 3 functions to change the theme:
<template>
<v-app>
<v-main>
<v-container>
<!-- Toggle between Light / Dark -->
<v-btn
@click="theme.toggle()"
text="Toggle Light / Dark"
></v-btn>
<!-- Change to a specific theme -->
<v-btn
@click="theme.change('dark')"
text="Change to Dark"
></v-btn>
<!-- Cycle between all themes -->
<v-btn
@click="theme.cycle()"
text="Cycle All Themes"
></v-btn>
<!-- Cycle between specific themes -->
<v-btn
@click="theme.cycle(['custom', 'light', 'system'])"
text="Cycle Specific Themes"
></v-btn>
</v-container>
</v-main>
</v-app>
</template>
<script setup>
import { useTheme } from 'vuetify'
const theme = useTheme()
</script>
You should keep in mind that most of the Vuetify components support the theme prop. When used a new context is created for that specific component and all of its children. In the following example, the v-btn uses the dark theme because it is applied to its parent v-card.
<template>
<v-app>
<v-card theme="dark">
<!-- button uses dark theme -->
<v-btn>foo</v-btn>
</v-card>
</v-app>
</template>
You can use the <v-theme-provider> component to dynamically apply different themes to larger sections of your application, without having to set the theme prop on each individual component. In the following example, we apply a custom theme named high-contrast.
<template>
<v-app>
<!-- uses the current default theme -->
<v-card>...</v-card>
<v-theme-provider theme="high-contrast">
<!-- uses the high-contrast theme -->
<v-card>...</v-card>
<v-btn>...</v-btn>
</v-theme-provider>
</v-app>
</template>
The system theme uses the user's system preference for 'light' or 'dark' mode, based on the prefers-color-scheme media query. It is evaluated at run-time and reacts to changes in the user's system preference.
import { createApp } from 'vue'
import { createVuetify } from 'vuetify'
export default createVuetify({
theme: {
defaultTheme: 'system',
},
})
The transition option enables an expanding circle animation when changing themes using the View Transition API. In browsers that do not support the View Transition API, the theme change is applied instantly.
| Browser | minimal version for View Transition |
|---|---|
| Chromium (Chrome, Edge) | 111 |
| Firefox | 144 |
| Safari | 18.0 |
To enable theme transitions, set the transition option to true or pass an options object with default origin of the expanding circle:
import { createVuetify } from 'vuetify'
export default createVuetify({
theme: {
transition: true, // default origin: top center
// or provide options:
transition: { origin: '100% 0%' }, // right corner
},
})
You can also override the transition per-call by passing a second argument to change, toggle, or cycle. This is useful for originating the circle from the click position:
<template>
<v-container class="d-flex justify-center">
<v-btn
icon="mdi-theme-light-dark"
@click="toggleTheme"
/>
</v-container>
</template>
<script setup>
import { useTheme } from 'vuetify'
const theme = useTheme()
function toggleTheme (e) {
// passing target element will make it work for both clicks and keyboard interactions
theme.setTransitionOrigin(e.target)
theme.toggle()
}
</script>
Adding new themes is as easy as defining a new property in the theme.themes object. A theme is a collection of colors and options that change the overall look and feel of your application. One of these options designates the theme as being either a light or dark variation.
This makes it possible for Vuetify to implement Material Design concepts such as elevated surfaces having a lighter overlay color the higher up they are. Find out more about dark themes on the official Material Design page.
import { createApp } from 'vue'
import { createVuetify } from 'vuetify'
const myCustomLightTheme = {
dark: false,
colors: {
background: '#FFFFFF',
surface: '#FFFFFF',
'surface-bright': '#FFFFFF',
'surface-light': '#EEEEEE',
'surface-variant': '#424242',
'on-surface-variant': '#EEEEEE',
primary: '#1867C0',
'primary-darken-1': '#1F5592',
secondary: '#48A9A6',
'secondary-darken-1': '#018786',
error: '#B00020',
info: '#2196F3',
success: '#4CAF50',
warning: '#FB8C00',
},
variables: {
'border-color': '#000000',
'border-opacity': 0.12,
'high-emphasis-opacity': 0.87,
'medium-emphasis-opacity': 0.60,
'disabled-opacity': 0.38,
'idle-opacity': 0.04,
'hover-opacity': 0.04,
'focus-opacity': 0.12,
'selected-opacity': 0.08,
'activated-opacity': 0.12,
'pressed-opacity': 0.12,
'dragged-opacity': 0.08,
'theme-kbd': '#212529',
'theme-on-kbd': '#FFFFFF',
'theme-code': '#F5F5F5',
'theme-on-code': '#000000',
}
}
export default createVuetify({
theme: {
defaultTheme: 'myCustomLightTheme',
themes: {
myCustomLightTheme,
},
},
})
The Vuetify theme system supports adding custom colors. When configuring the Vuetify theme settings, add your custom colors to the colors object and Vuetify will generate a number of CSS classes and variables for you to use in your application.
import { createApp } from 'vue'
import { createVuetify } from 'vuetify'
export default createVuetify({
theme: {
defaultTheme: 'myCustomTheme',
themes: {
myCustomTheme: {
dark: false,
colors: {
..., // We have omitted the standard color properties here to emphasize the custom one that we've added
something: '#00ff00',
},
},
},
},
})
Custom properties for colors are a list of red, green, blue, so the rgb() or rgba() function has to be used:
<template>
<div class="bg-something on-something">background color with appropriate text color contrast</div>
<div class="text-something">text color</div>
<div class="border-something">border color</div>
</template>
<style>
.custom-class {
background: rgb(var(--v-theme-something))
color: rgba(var(--v-theme-on-something), 0.9)
}
</style>
The Vuetify theme system can help you generate any number of variations for the colors in your theme. The following example shows how to generate 1 lighten and 2 darken variants for the primary and secondary colors.
import { createApp } from 'vue'
import { createVuetify } from 'vuetify'
export default createVuetify({
theme: {
defaultTheme: 'myCustomTheme',
variations: {
colors: ['primary', 'secondary'],
lighten: 1,
darken: 2,
},
themes: {
//
},
},
})
<template>
<div class="text-primary-lighten-1">text color</div>
<div class="text-primary-darken-1">text color</div>
<div class="text-primary-darken-2">text color</div>
</template>
The theme functionality can be disabled by setting the theme configuration property to false. This prevents the creation of the Vuetify stylesheet, and theme classes will not be applied to components.
import { createApp } from 'vue'
import { createVuetify } from 'vuetify'
export default createVuetify({
theme: false,
})
interface ThemeInstance {
/**
* Raw theme objects
* Can be mutated to add new themes or update existing colors
*/
themes: Ref<{ [name: string]: ThemeDefinition }>
/**
* Name of the current theme
* Inherited from parent components
*/
readonly name: Ref<string>
/** Processed theme object, includes automatically generated colors */
readonly current: Ref<ThemeDefinition>
readonly computedThemes: Ref<{ [name: string]: ThemeDefinition }>
readonly global: {
/** Name of the current global theme */
name: Ref<string>
/**
* Processed theme object of the current global theme
* Equivalent to `theme.computedThemes.value[theme.global.name.value]`
*/
readonly current: Ref<ThemeDefinition>
}
}
Pages with the script-src or style-src CSP rules enabled may require a nonce to be specified for embedded style tags.
<!-- Use with script-src -->
Content-Security-Policy: script-src 'self' 'nonce-dQw4w9WgXcQ'
<!-- Use with style-src -->
Content-Security-Policy: style-src 'self' 'nonce-dQw4w9WgXcQ'
import { createVuetify } from 'vuetify'
export const vuetify = createVuetify({
theme: {
cspNonce: 'dQw4w9WgXcQ',
},
})
Vuetify generates theme styles at runtime according to the given configuration. The generated styles are injected into the <head> section of the DOM in a <style> tag with a default id of vuetify-theme-stylesheet.
An application using microfrontends with multiple instances of Vuetify may need to define unique theme.stylesheetId values for each microfrontend in order to prevent conflicts between their generated stylesheets.
Further, such a scenario might require styles to be scoped to a specific microfrontend, which can be achieved by setting the theme.scope property.
For example, a microfrontend mounted in an element #my-app can define a theme.scope of #my-app to scope its styles to that element and its children instead of :root and global classes.