---
title: Theme
description: 'Learn how to customize Nuxt UI components using Tailwind CSS v4, CSS variables and the Tailwind Variants API for powerful and flexible theming.'
navigation.icon: i-lucide-swatch-book
---
## Tailwind CSS
Nuxt UI uses Tailwind CSS v4, you can read the official [upgrade guide](https://tailwindcss.com/docs/upgrade-guide#changes-from-v3) to learn about all the breaking changes.
### `@theme`
Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your theme with CSS variables inside a [`@theme`](https://tailwindcss.com/docs/functions-and-directives#theme-directive) directive to define your project's custom design tokens, like fonts, colors, and breakpoints:
::module-only
#ui
:::div
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
@theme static {
--font-sans: 'Public Sans', sans-serif;
--breakpoint-3xl: 1920px;
--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;
}
```
:::
#ui-pro
:::div
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui-pro";
@theme static {
--font-sans: 'Public Sans', sans-serif;
--breakpoint-3xl: 1920px;
--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;
}
```
:::
::
The `@theme` directive tells Tailwind to make new utilities and variants available based on these variables. It's the equivalent of the `theme.extend` key in Tailwind CSS v3 `tailwind.config.ts` file.
::note{to="https://tailwindcss.com/docs/theme" target="_blank"}
Learn more about customizing your theme in the theme variables documentation.
::
### `@source`
You can use the [`@source` directive](https://tailwindcss.com/docs/functions-and-directives#source-directive) to explicitly specify source files that aren't picked up by Tailwind's automatic content detection:
This can be useful when writing Tailwind CSS classes in markdown files with [`@nuxt/content`](https://github.com/nuxt/content) for example:
::module-only
#ui
:::div
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
@source "../../../content";
/* Use this if you're not using compatibilityVersion: 4: https://nuxt.com/docs/getting-started/upgrade#opting-in-to-nuxt-4 */
@source "../../content";
```
:::
#ui-pro
:::div
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui-pro";
@source "../../../content";
/* Use this if you're not using compatibilityVersion: 4: https://nuxt.com/docs/getting-started/upgrade#opting-in-to-nuxt-4 */
@source "../../content";
```
:::
::
::note{to="https://tailwindcss.com/docs/detecting-classes-in-source-files"}
Learn more about automatic content detection in the detecting classes in source files documentation.
::
## Design system
Nuxt UI extends Tailwind CSS's theming capabilities, providing a flexible design system with pre-configured color aliases based on [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference). This allows for easy customization and quick adaptation of the UI to your brand's aesthetic.
| Color | Default | Description |
| --- | --- | --- |
| `primary`{color="primary"} | `green` | Main brand color, used as the default color for components. |
| `secondary`{color="secondary"} | `blue` | Secondary color to complement the primary color. |
| `success`{color="success"} | `green` | Used for success states. |
| `info`{color="info"} | `blue` | Used for informational states. |
| `warning`{color="warning"} | `yellow` | Used for warning states. |
| `error`{color="error"} | `red` | Used for form error validation states. |
| `neutral` | `slate` | Neutral color for backgrounds, text, etc. |
These colors are used to style the components but also to generate the `color` props:
::component-code{slug="button"}
---
props:
color: primary
slots:
default: Button
---
::
::note
Try the :prose-icon{name="i-lucide-swatch-book" class="text-primary"} theme picker in the header above to change `primary` and `neutral` colors.
::
### Configuration
::framework-only
#nuxt
:::div
You can configure these color aliases at runtime in your [`app.config.ts`](https://nuxt.com/docs/guide/directory-structure/app-config#app-config-file) file under the `ui.colors` key, allowing for dynamic theme customization without requiring an application rebuild:
```ts [app.config.ts]
export default defineAppConfig({
ui: {
colors: {
primary: 'blue',
neutral: 'zinc'
}
}
})
```
:::
#vue
:::div
You can configure these color aliases at runtime in your `vite.config.ts` file under the `ui.colors` key:
::::module-only
#ui
:::::div
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
colors: {
primary: 'blue',
neutral: 'zinc'
}
}
})
]
})
```
:::::
#ui-pro
:::::div
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uiPro from '@nuxt/ui-pro/vite'
export default defineConfig({
plugins: [
vue(),
uiPro({
ui: {
colors: {
primary: 'blue',
neutral: 'zinc'
}
}
})
]
})
```
:::::
::::
:::
::
### Extend colors
::framework-only
#nuxt
:::div
You can add you own dynamic color aliases in your `app.config.ts`, you just have to make sure to define them in the [`ui.theme.colors`](/getting-started/installation/nuxt#themecolors) option in your `nuxt.config.ts` file:
```ts [app.config.ts]{4}
export default defineAppConfig({
ui: {
colors: {
tertiary: 'indigo'
}
}
})
```
```ts [nuxt.config.ts]{7}
export default defineNuxtConfig({
ui: {
theme: {
colors: [
'primary',
'secondary',
'tertiary',
'info',
'success',
'warning',
'error'
]
}
}
})
```
:::
#vue
:::div
You can add you own dynamic color aliases in your `vite.config.ts`, you just have to make sure to also define them in the [`theme.colors`](/getting-started/installation/vue#themecolors) option of the `ui` plugin:
::::module-only
#ui
:::::div
```ts [vite.config.ts]{11,18}
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
colors: {
tertiary: 'indigo'
}
},
theme: {
colors: [
'primary',
'secondary',
'tertiary',
'info',
'success',
'warning',
'error'
]
}
})
]
})
```
:::::
#ui-pro
:::::div
```ts [vite.config.ts]{11,18}
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uiPro from '@nuxt/ui-pro/vite'
export default defineConfig({
plugins: [
vue(),
uiPro({
ui: {
colors: {
tertiary: 'indigo'
}
},
theme: {
colors: [
'primary',
'secondary',
'tertiary',
'info',
'success',
'warning',
'error'
]
}
})
]
})
```
:::::
::::
:::
::
## CSS Variables
Nuxt UI leverages a robust system of CSS variables as design tokens to ensure consistent and flexible component styling. These tokens form the foundation of the theming system, offering smooth support for both light and dark modes.
### Colors
Nuxt UI provides a CSS variable for each color alias you define which represent the default shade used in both light and dark modes:
::code-group
```css [Light]
:root {
--ui-primary: var(--ui-color-primary-500);
--ui-secondary: var(--ui-color-secondary-500);
--ui-success: var(--ui-color-success-500);
--ui-info: var(--ui-color-info-500);
--ui-warning: var(--ui-color-warning-500);
--ui-error: var(--ui-color-error-500);
}
```
```css [Dark]
.dark {
--ui-primary: var(--ui-color-primary-400);
--ui-secondary: var(--ui-color-secondary-400);
--ui-success: var(--ui-color-success-400);
--ui-info: var(--ui-color-info-400);
--ui-warning: var(--ui-color-warning-400);
--ui-error: var(--ui-color-error-400);
}
```
::
These CSS variables are defined in Tailwind CSS's `@theme` so you can use them as classes:
::code-preview
[Primary]{class="text-primary text-sm px-4"}
[Secondary]{class="text-secondary text-sm px-4"}
[Success]{class="text-success text-sm px-4"}
[Info]{class="text-info text-sm px-4"}
[Warning]{class="text-warning text-sm px-4"}
[Error]{class="text-error text-sm px-4"}
#code
```vue
Primary
Secondary
Success
Info
Warning
Error
```
::
::note
This is how the `@theme` is generated for each color alias:
:::code-collapse{class="[&>div]:!my-0"}
```scss
@theme default {
--color-primary: var(--ui-primary);
--color-primary-50: var(--ui-color-primary-50);
--color-primary-100: var(--ui-color-primary-100);
--color-primary-200: var(--ui-color-primary-200);
--color-primary-300: var(--ui-color-primary-300);
--color-primary-400: var(--ui-color-primary-400);
--color-primary-500: var(--ui-color-primary-500);
--color-primary-600: var(--ui-color-primary-600);
--color-primary-700: var(--ui-color-primary-700);
--color-primary-800: var(--ui-color-primary-800);
--color-primary-900: var(--ui-color-primary-900);
--color-primary-950: var(--ui-color-primary-950);
--color-secondary: var(--ui-secondary);
--color-secondary-50: var(--ui-color-secondary-50);
--color-secondary-100: var(--ui-color-secondary-100);
--color-secondary-200: var(--ui-color-secondary-200);
--color-secondary-300: var(--ui-color-secondary-300);
--color-secondary-400: var(--ui-color-secondary-400);
--color-secondary-500: var(--ui-color-secondary-500);
--color-secondary-600: var(--ui-color-secondary-600);
--color-secondary-700: var(--ui-color-secondary-700);
--color-secondary-800: var(--ui-color-secondary-800);
--color-secondary-900: var(--ui-color-secondary-900);
--color-secondary-950: var(--ui-color-secondary-950);
--color-success: var(--ui-success);
--color-success-50: var(--ui-color-success-50);
--color-success-100: var(--ui-color-success-100);
--color-success-200: var(--ui-color-success-200);
--color-success-300: var(--ui-color-success-300);
--color-success-400: var(--ui-color-success-400);
--color-success-500: var(--ui-color-success-500);
--color-success-600: var(--ui-color-success-600);
--color-success-700: var(--ui-color-success-700);
--color-success-800: var(--ui-color-success-800);
--color-success-900: var(--ui-color-success-900);
--color-success-950: var(--ui-color-success-950);
--color-info: var(--ui-info);
--color-info-50: var(--ui-color-info-50);
--color-info-100: var(--ui-color-info-100);
--color-info-200: var(--ui-color-info-200);
--color-info-300: var(--ui-color-info-300);
--color-info-400: var(--ui-color-info-400);
--color-info-500: var(--ui-color-info-500);
--color-info-600: var(--ui-color-info-600);
--color-info-700: var(--ui-color-info-700);
--color-info-800: var(--ui-color-info-800);
--color-info-900: var(--ui-color-info-900);
--color-info-950: var(--ui-color-info-950);
--color-warning: var(--ui-warning);
--color-warning-50: var(--ui-color-warning-50);
--color-warning-100: var(--ui-color-warning-100);
--color-warning-200: var(--ui-color-warning-200);
--color-warning-300: var(--ui-color-warning-300);
--color-warning-400: var(--ui-color-warning-400);
--color-warning-500: var(--ui-color-warning-500);
--color-warning-600: var(--ui-color-warning-600);
--color-warning-700: var(--ui-color-warning-700);
--color-warning-800: var(--ui-color-warning-800);
--color-warning-900: var(--ui-color-warning-900);
--color-warning-950: var(--ui-color-warning-950);
--color-error: var(--ui-error);
--color-error-50: var(--ui-color-error-50);
--color-error-100: var(--ui-color-error-100);
--color-error-200: var(--ui-color-error-200);
--color-error-300: var(--ui-color-error-300);
--color-error-400: var(--ui-color-error-400);
--color-error-500: var(--ui-color-error-500);
--color-error-600: var(--ui-color-error-600);
--color-error-700: var(--ui-color-error-700);
--color-error-800: var(--ui-color-error-800);
--color-error-900: var(--ui-color-error-900);
--color-error-950: var(--ui-color-error-950);
--color-neutral-50: var(--ui-color-neutral-50);
--color-neutral-100: var(--ui-color-neutral-100);
--color-neutral-200: var(--ui-color-neutral-200);
--color-neutral-300: var(--ui-color-neutral-300);
--color-neutral-400: var(--ui-color-neutral-400);
--color-neutral-500: var(--ui-color-neutral-500);
--color-neutral-600: var(--ui-color-neutral-600);
--color-neutral-700: var(--ui-color-neutral-700);
--color-neutral-800: var(--ui-color-neutral-800);
--color-neutral-900: var(--ui-color-neutral-900);
--color-neutral-950: var(--ui-color-neutral-950);
}
```
:::
::
You can change which shade is used for each color on light and dark mode in your `main.css` file:
::module-only
#ui
:::div{class="*:!mb-0 *:!mt-2.5"}
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-primary: var(--ui-color-primary-700);
}
.dark {
--ui-primary: var(--ui-color-primary-200);
}
```
:::
#ui-pro
:::div{class="*:!mb-0 *:!mt-2.5"}
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui-pro";
:root {
--ui-primary: var(--ui-color-primary-700);
}
.dark {
--ui-primary: var(--ui-color-primary-200);
}
```
:::
::
::framework-only
#nuxt
:::p
You cannot set `primary: 'black'`{lang="ts-type"} in your [`app.config.ts`](#config) because this color has no shade, instead you can override the primary color in your `main.css` file to create a black & white theme:
:::
#vue
:::p
You cannot set `primary: 'black'`{lang="ts-type"} in your [`vite.config.ts`](#config) because this color has no shade, instead you can override the primary color in your `main.css` file to create a black & white theme:
:::
::
::module-only
#ui
:::div{class="*:!mb-0 *:!mt-2.5"}
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-primary: black;
}
.dark {
--ui-primary: white;
}
```
:::
#ui-pro
:::div{class="*:!mb-0 *:!mt-2.5"}
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui-pro";
:root {
--ui-primary: black;
}
.dark {
--ui-primary: white;
}
```
:::
::
### Neutral
Nuxt UI provides a comprehensive set of design tokens for the `neutral` color palette, ensuring consistent and accessible UI styling across both light and dark modes. These tokens offer fine-grained control over text, background, and border colors:
::code-group
```css [Light]
:root {
--ui-text-dimmed: var(--ui-color-neutral-400);
--ui-text-muted: var(--ui-color-neutral-500);
--ui-text-toned: var(--ui-color-neutral-600);
--ui-text: var(--ui-color-neutral-700);
--ui-text-highlighted: var(--ui-color-neutral-900);
--ui-text-inverted: var(--color-white);
--ui-bg: var(--color-white);
--ui-bg-muted: var(--ui-color-neutral-50);
--ui-bg-elevated: var(--ui-color-neutral-100);
--ui-bg-accented: var(--ui-color-neutral-200);
--ui-bg-inverted: var(--ui-color-neutral-900);
--ui-border: var(--ui-color-neutral-200);
--ui-border-muted: var(--ui-color-neutral-200);
--ui-border-accented: var(--ui-color-neutral-300);
--ui-border-inverted: var(--ui-color-neutral-900);
}
```
```css [Dark]
.dark {
--ui-text-dimmed: var(--ui-color-neutral-500);
--ui-text-muted: var(--ui-color-neutral-400);
--ui-text-toned: var(--ui-color-neutral-300);
--ui-text: var(--ui-color-neutral-200);
--ui-text-highlighted: var(--color-white);
--ui-text-inverted: var(--ui-color-neutral-900);
--ui-bg: var(--ui-color-neutral-900);
--ui-bg-muted: var(--ui-color-neutral-800);
--ui-bg-elevated: var(--ui-color-neutral-800);
--ui-bg-accented: var(--ui-color-neutral-700);
--ui-bg-inverted: var(--color-white);
--ui-border: var(--ui-color-neutral-800);
--ui-border-muted: var(--ui-color-neutral-700);
--ui-border-accented: var(--ui-color-neutral-700);
--ui-border-inverted: var(--color-white);
}
```
::
These CSS variables are defined in Tailwind CSS's `@theme` so you can use them as classes:
::code-preview
[Dimmed]{class="text-dimmed text-sm px-4 py-1.5 inline-block rounded-md"}
[Muted]{class="text-muted text-sm px-4 py-1.5 inline-block rounded-md"}
[Toned]{class="text-toned text-sm px-4 py-1.5 inline-block rounded-md"}
[Text]{class="text-default text-sm px-4 py-1.5 inline-block rounded-md"}
[Highlighted]{class="text-highlighted text-sm px-4 py-1.5 inline-block rounded-md"}
[Inverted]{class="text-inverted bg-inverted text-sm px-4 py-1.5 inline-block rounded-md"}
#code
```vue
Dimmed
Muted
Toned
Text
Highlighted
Inverted
```
::
::code-preview
[Default]{class="bg-default text-sm px-4 py-1.5 inline-block rounded-md mr-2"}
[Muted]{class="bg-muted text-sm px-4 py-1.5 inline-block rounded-md mr-2"}
[Elevated]{class="bg-elevated text-sm px-4 py-1.5 inline-block rounded-md mr-2"}
[Accented]{class="bg-accented text-sm px-4 py-1.5 inline-block rounded-md mr-2"}
[Inverted]{class="bg-inverted text-inverted text-sm px-4 py-1.5 inline-block rounded-md"}
#code
```vue