UnoCSS with Vuetify preset
Generate Vuetify’s built-in utility classes on demand with unocss-preset-vuetify, maintained by the Vuetify team.
No class-naming convention change required — use the same Vuetify class names you already know, generated on demand instead of shipped in full.
# generate working project for reference
npx @vuetify/cli@latest init --type=vuetify --css=unocss-vuetify
Establish CSS layer order
Create a layers.css file that declares the cascade layers in order. uno goes above component styles but below vuetify-final, where Vuetify keeps its transitions:
@layer vuetify-core;
@layer vuetify-components;
@layer vuetify-overrides;
@layer vuetify-utilities;
@layer uno;
@layer vuetify-final;
This file must be loaded before any other styles. In a Vite project, save it as src/styles/layers.css and import it at the top of src/plugins/vuetify.ts, before vuetify/styles.
Setup dependencies
Vite
Import the layers file at the top of src/plugins/vuetify.ts, before vuetify/styles:
import '../styles/layers.css'
import 'vuetify/styles'
// ...
Install UnoCSS and the Vuetify preset:
pnpm add -D unocss unocss-preset-vuetify
Register the UnoCSS Vite plugin in vite.config.ts and create uno.config.ts:
import UnoCSS from 'unocss/vite'
export default defineConfig({
plugins: [
UnoCSS(),
// ...
],
})
import { defineConfig } from 'unocss'
import { presetVuetify } from 'unocss-preset-vuetify'
export default defineConfig({
presets: [
presetVuetify({
typography: 'md3', // accepts 'md2' or custom object
elevation: 'md3', // accepts 'md2' or custom object
font: { // your custom fonts
heading: 'K2D, sans-serif',
body: '"Work Sans", sans-serif',
},
}),
],
outputToCssLayers: {
cssLayerName: (layer) => layer === 'properties' ? null : `uno.${layer}`,
},
})
Add the UnoCSS virtual import to your entry point:
import 'virtual:uno.css'
Nuxt
pnpm add -D unocss unocss-preset-vuetify @unocss/nuxt
Register the module in nuxt.config.ts. The css array controls load order — layers.css must come first, followed by vuetify/styles. Set disableVuetifyStyles: true — otherwise the module injects styles automatically and the order above is ignored:
import { presetVuetify } from 'unocss-preset-vuetify'
export default defineNuxtConfig({
modules: [
'@unocss/nuxt',
'vuetify-nuxt-module',
// ...
],
css: [
'assets/styles/layers.css',
'vuetify/styles',
],
vuetify: {
moduleOptions: {
disableVuetifyStyles: true,
styles: { configFile: 'assets/styles/settings.scss' },
},
vuetifyOptions: {
theme: {
defaultTheme: 'dark', // 'system' requires ssr: false
},
},
},
unocss: {
presets: [
presetVuetify(),
],
outputToCssLayers: {
cssLayerName: (layer) => layer === 'properties' ? null : `uno.${layer}`,
},
},
})
Disable Vuetify’s built-in utilities
Turn off Vuetify’s built-in utility classes and the Material color palette so UnoCSS handles them on demand instead.
@use 'vuetify/settings' with (
$color-pack: false,
$utilities: false,
);
How on-demand generation works
UnoCSS scans your source files statically, looking for class name strings. A class like elevation-4 in class="elevation-4" gets picked up and its CSS is generated. The problem arises with props like elevation, rounded and border: when you write <v-card elevation="4">, Vuetify’s component logic converts the prop value into the class elevation-4 internally — the string elevation-4 never appears literally in your source, so UnoCSS won’t generate it.
The safelist option solves this: it specifies classes (or patterns) that UnoCSS should always generate regardless of whether they appear in scanned files.
Safelist prop-driven classes
Add safelist entries for convenience props (e.g. elevation and rounded) that just add CSS classes. Because these class names are generated at runtime by Vuetify components, UnoCSS cannot detect them by scanning source files.
{
// presets: ...
// outputToCssLayers: ...
safelist: [
...Array.from({ length: 6 }, (_, i) => `elevation-${i}`),
['', '-0', '-sm', '-lg', '-xl', '-pill', '-circle', '-shaped'].map(suffix => `rounded${suffix}`),
],
}