
Nuxt UI is wired to embrace your branding. The library layers Tailwind CSS, semantic design tokens, and a powerful variant system so you can evolve colors, typography, spacing, and component styles without forking core styles. This guide walks through the most important levers—covering the Nuxt design system, semantic colors, slot-level overrides, simple and compound variants, and dynamic theme switching—so you can ship a cohesive experience.
Nuxt UI leans on Tailwind CSS’s @theme directive. That keeps design decisions portable and versionable alongside the rest of your styles. It also means you can access values as CSS variables ( ie. var(--color-gray-950)) and use the Tailwind CSS classes you're already familiar with ( ie. bg-gray-950).
@themeStart by importing Tailwind and Nuxt UI styles, then declare your baseline tokens. Everything downstream pulls from these values.
/* app/assets/css/main.css */
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
/* Override Tailwind's fonts */
--font-sans: "Public Sans", system-ui, sans-serif;
--font-mono: "JetBrains Mono", monospace;
/* Define Custom Fonts
(ie. font-heading)
*/
-- font-heading: "Poppins", sans-serif;
/* Override Tailwind's breakpoints */
--breakpoint-3xl: 1920px;
--breakpoint-4xl: 2560px;
--breakpoint-5xl: 3840px;
/* Override tailwind colors */
--color-green-50: #effdf5;
--color-green-100: #d9fbe8;
--color-green-200: #b3f5d1;
--color-green-300: #75edae;
--color-green-400: #00dc82;
--color-green-500: #00c16a;
--color-green-600: #00a155;
--color-green-700: #007f45;
--color-green-800: #016538;
--color-green-900: #0a5331;
--color-green-950: #052e16;
/*
Define custom colors
(ie. bg-brand-500, text-brand-200, border-brand-300, etc.)
*/
--color-brand-50: #fef2f2;
--color-brand-100: #fee2e2;
--color-brand-200: #fecaca;
--color-brand-300: #fca5a5;
--color-brand-400: #f87171;
--color-brand-500: #ef4444;
--color-brand-600: #dc2626;
--color-brand-700: #b91c1c;
--color-brand-800: #991b1b;
--color-brand-900: #7f1d1d;
--color-brand-950: #450a0a;
}Besides the Tailwind colors, Nuxt UI also provides a set of semantic colors that you can use to style your components. These colors are mapped to the Tailwind colors or your custom colors defined in @theme in app.config.ts.
// app.config.ts
export default defineAppConfig({
ui: {
colors: {
primary: "brand",
secondary: "green",
// also: neutral, success, warning, error
},
},
});Now you can use the semantic colors as classes in your templates.
<template>
<div class="bg-primary">Primary</div>
<div class="bg-secondary">Secondary</div>
</template>Plus, most Nuxt UI components expose a color prop. These props map to the semantic colors.
<template>
<UButton color="primary">Save Changes</UButton>
<UAlert color="warning" title="Heads up" />
</template>Just like the design tokens in @theme, you can define your own semantic colors too and map them to design tokens.
// app.config.ts
export default defineAppConfig({
ui: {
colors: {
tertiary: "pink",
},
},
});<template>
<!-- As a prop -->
<UButton color="tertiary">Save Changes</UButton>
<!-- Or as a class-->
<div class="bg-tertiary-500">Tertiary</div>
</template>Just make sure you register the new semantic color in nuxt.config.ts otherwise it won't work.
// nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
// ...
ui: {
theme: {
colors: [
// NOTE: if you customize this you should include the defaults too!
"primary",
"secondary",
"tertiary", // 👈 new color added
"info",
"success",
"warning",
"error",
],
},
},
});The semantic colors point to shade-specific CSS variables (by default the 500 shade).
Customize the shade used by overriding the --ui-color-* tokens or the derived shortcuts like --ui-primary inside your CSS.
/* main.css */
:root {
--ui-primary: var(--ui-color-primary-300);
}💡 TIP! You can also override border, background, and text helper variables (
--ui-border,--ui-bg-elevated,--ui-text-muted, etc.) to keep surfaces and typography consistent across contexts.
Nuxt UI ships each component with a documented slot structure. The class list on these slots can be targeted with the ui prop at the global level in app.config.ts and overridden with your own Tailwind classes.
// app.config.ts
export default defineAppConfig({
ui: {
// styles ALL UCard components globally
card: {
slots: {
root: "bg-gradient-to-b from-brand-50 to-white ring ring-brand-100",
header: "p-6 font-semibold text-primary",
footer: "p-6 bg-muted flex justify-end gap-2",
},
},
},
});For one-off adjustments, the ui component prop works exactly the same at the component level.
<template>
<UCard
:ui="{
header: 'p-8 text-xl',
body: 'p-8 space-y-4',
footer: 'p-6 border-t border-accented',
}"
>
...
</UCard>
</template>NOTE that because Nuxt UI uses Tailwind Variants under the hood, your new classes override core styles without wrestling cascade order. It's not a total takeover, but a smart merge of classes (read about tailwind-merge for more details.).
👉 Watch a video lesson covering this topic in our complete course: Nuxt UI - Build a Dashboard Template.
You've just seen how to style slots. "Variants" are the next logical step. Variants control the class list of component slots based on the value of props passed. Override the applied classes globally with the variants property.
// app.config.ts
export default defineAppConfig({
ui: {
// target the card component
card: {
// target the size variant (ie when size prop is set to sm)
variants: {
size: {
sm: {
// applies these classes to the root slot
// when the size prop is set to sm
root: "size-7 text-sm",
},
},
},
},
},
});NOTE, this approach isn't available at the component level. Why? It wouldn't make sense. Just use the approach descibed in the last section.
This terminology is a bit confusing but we're not talking about class customization here. I only mention it because it's related to "variants".
Now we're talking about setting default prop values.
This is done with the defaultVariants property in app.config.ts.
// app.config.ts
// button size usually defaults to md
// but you can override it to sm
export default defineAppConfig({
ui: {
button: {
defaultVariants: {
size: "sm",
},
},
},
});Ok back to class customization. Once variants make sense, it's not a big leap to compound variants.
Compound variants kick in when MULTIPLE props are combined--perfect for adapting outlines, ghosts, or subtle variants across colors, sizes, and more.
// app.config.ts
export default defineAppConfig({
ui: {
button: {
compoundVariants: [
// customizes the classes when
// 1. the color prop is set to tertiary
// 2. AND the size prop is set to sm
{
color: "tertiary",
size: "sm",
class: "ring-2 ring-tertiary/60 text-tertiary hover:bg-tertiary/10",
},
],
},
},
});At this point, you've learned how to define semantic colors and style component slots with custom classes. This can all be hardcoded to static values BUT since app.config is reactive, you can adjust any of these values at runtime.
<script setup lang="ts">
const appConfig = useAppConfig();
function changeTheme() {
updateAppConfig({
ui: {
...appConfig.ui,
...{
// new theme config
},
},
});
}
</script>Tie the same handler to user profile settings or A/B experiments to ship per-tenant theming without redeploys.
Watch a more robust example of this in action in our complete course: Nuxt UI - Build a Dashboard Template.
Customizing the Nuxt UI design system boils down to managing a few layers: Tailwind tokens, semantic color mappings, component slots, and Variants. With these pieces in place, you can launch an on-brand interface with easy to use and accessible Vue components that look nothing like the next guy's.



Our goal is to be the number one source of Vue.js knowledge for all skill levels. We offer the knowledge of our industry leaders through awesome video courses for a ridiculously low price.
More than 200.000 users have already joined us. You are welcome too!
© All rights reserved. Made with ❤️ by BitterBrains, Inc.