Theme
Tailwind CSS
Nuxt UI v3 uses Tailwind CSS v4 beta, you can read the prerelease documentation for more information.
@theme
Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your theme with CSS variables inside a @theme
directive:
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
--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;
}
@import "tailwindcss";
@import "@nuxt/ui-pro";
@theme {
--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.
@source
You can use the @source
directive to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected.
This can be useful when writing Tailwind classes in markdown files with @nuxt/content
:
@import "tailwindcss";
@import "@nuxt/ui";
@source "../content";
@import "tailwindcss";
@import "@nuxt/ui-pro";
@source "../content";
@plugin
You can use the @plugin
directive to import Tailwind CSS plugins.
@import "tailwindcss";
@import "@nuxt/ui";
@plugin "@tailwindcss/typography";
@import "tailwindcss";
@import "@nuxt/ui-pro";
@plugin "@tailwindcss/typography";
Design system
Nuxt UI extends Tailwind CSS's theming capabilities, providing a flexible design system with pre-configured color aliases and CSS variables. This allows for easy customization and quick adaptation of the UI to your brand's aesthetic.
Colors
Nuxt UI leverages Nuxt App Config to provide customizable color aliases based on Tailwind CSS colors:
Nuxt UI leverages Vite config to provide customizable color aliases based on Tailwind CSS colors:
Color | Default | Description |
---|---|---|
primary | green | Main brand color, used as the default color for components. |
secondary | blue | Secondary color to complement the primary color. |
success | green | Used for success states. |
info | blue | Used for informational states. |
warning | yellow | Used for warning states. |
error | red | Used for form error validation states. |
neutral | slate | Neutral color for backgrounds, text, etc. |
You can configure these color aliases at runtime in your app.config.ts
file under the ui.colors
key, allowing for dynamic theme customization without requiring an application rebuild:
export default defineAppConfig({
ui: {
colors: {
primary: 'blue',
neutral: 'zinc'
}
}
})
You can configure these color aliases at runtime in your vite.config.ts
file under the ui.colors
key:
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'
}
}
})
]
})
primary
and neutral
colors.These colors are used to style the components but also to generate the color
variants:
<template>
<UButton>Button</UButton>
</template>
app.config.ts
, you just have to make sure to define them in the ui.theme.colors
option in your nuxt.config.ts
file.export default defineAppConfig({
ui: {
colors: {
tertiary: 'indigo'
}
}
})
export default defineNuxtConfig({
ui: {
theme: {
colors: ['primary', 'secondary', 'tertiary', 'info', 'success', 'warning', 'error']
}
}
})
vite.config.ts
, you just have to make sure to also define them in the theme.colors
option of the ui
plugin.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']
}
})
]
})
text-primary-500 dark:text-primary-400
won't be available by default as in Nuxt UI v2. This approach provides more flexibility and prevents overwriting of user-defined Tailwind CSS colors.However, you can generate these classes using Tailwind's
@theme
directive, allowing you to use custom color utility classes while maintaining dynamic color aliases:@import "tailwindcss";
@import "@nuxt/ui";
@theme {
--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);
}
@import "tailwindcss";
@import "@nuxt/ui-pro";
@theme {
--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);
}
Tokens
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.
Color Shades
Nuxt UI automatically creates a CSS variable for each color alias you define which represent the default shade used in both light and dark modes:
: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);
}
.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);
}
text-[var(--ui-primary)]
, it will automatically adapt to the current color scheme.@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-primary: var(--ui-color-primary-700);
}
.dark {
--ui-primary: var(--ui-color-primary-200);
}
@import "tailwindcss";
@import "@nuxt/ui-pro";
:root {
--ui-primary: var(--ui-color-primary-700);
}
.dark {
--ui-primary: var(--ui-color-primary-200);
}
Neutral Palette
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:
:root {
/* Least prominent text */
--ui-text-dimmed: var(--ui-color-neutral-400);
/* Slightly muted text */
--ui-text-muted: var(--ui-color-neutral-500);
/* Moderately prominent text */
--ui-text-toned: var(--ui-color-neutral-600);
/* Default text color */
--ui-text: var(--ui-color-neutral-700);
/* Most prominent text */
--ui-text-highlighted: var(--ui-color-neutral-900);
/* Main background color */
--ui-bg: var(--color-white);
/* Subtle background */
--ui-bg-muted: var(--ui-color-neutral-50);
/* Slightly elevated background */
--ui-bg-elevated: var(--ui-color-neutral-100);
/* More prominent background */
--ui-bg-accented: var(--ui-color-neutral-200);
/* Inverted background color */
--ui-bg-inverted: var(--ui-color-neutral-900);
/* Default border color */
--ui-border: var(--ui-color-neutral-200);
/* Subtle border */
--ui-border-muted: var(--ui-color-neutral-200);
/* More prominent border */
--ui-border-accented: var(--ui-color-neutral-300);
/* Inverted border color */
--ui-border-inverted: var(--ui-color-neutral-900);
}
.dark {
/* Least prominent text */
--ui-text-dimmed: var(--ui-color-neutral-500);
/* Slightly muted text */
--ui-text-muted: var(--ui-color-neutral-400);
/* Moderately prominent text */
--ui-text-toned: var(--ui-color-neutral-300);
/* Default text color */
--ui-text: var(--ui-color-neutral-200);
/* Most prominent text */
--ui-text-highlighted: var(--color-white);
/* Main background color */
--ui-bg: var(--ui-color-neutral-900);
/* Subtle background */
--ui-bg-muted: var(--ui-color-neutral-800);
/* Slightly elevated background */
--ui-bg-elevated: var(--ui-color-neutral-800);
/* More prominent background */
--ui-bg-accented: var(--ui-color-neutral-700);
/* Inverted background color */
--ui-bg-inverted: var(--color-white);
/* Default border color */
--ui-border: var(--ui-color-neutral-800);
/* Subtle border */
--ui-border-muted: var(--ui-color-neutral-700);
/* More prominent border */
--ui-border-accented: var(--ui-color-neutral-700);
/* Inverted border color */
--ui-border-inverted: var(--color-white);
}
<body>
element of your app:body {
@apply antialiased text-[var(--ui-text)] bg-[var(--ui-bg)];
}
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-bg: var(--ui-color-neutral-50);
--ui-text: var(--ui-color-neutral-900);
}
.dark {
--ui-bg: var(--ui-color-neutral-950);
--ui-border: var(--ui-color-neutral-900);
}
@import "tailwindcss";
@import "@nuxt/ui-pro";
:root {
--ui-bg: var(--ui-color-neutral-50);
--ui-text: var(--ui-color-neutral-900);
}
.dark {
--ui-bg: var(--ui-color-neutral-950);
--ui-border: var(--ui-color-neutral-900);
}
Border Radius
Nuxt UI uses a global --ui-radius
CSS variable for consistent border rounding. Components use variations of this base value, like rounded-[calc(var(--ui-radius)*2)]
, to create different levels of roundness throughout the UI:
:root {
--ui-radius: var(--radius-sm);
}
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-radius: var(--radius-sm);
}
@import "tailwindcss";
@import "@nuxt/ui-pro";
:root {
--ui-radius: var(--radius-sm);
}
Container
Nuxt UI uses a global --ui-container
CSS variable to define the width of the container:
:root {
--ui-container: var(--container-7xl);
}
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
--container-8xl: 90rem;
}
:root {
--ui-container: var(--container-8xl);
}
@import "tailwindcss";
@import "@nuxt/ui-pro";
@theme {
--container-8xl: 90rem;
}
:root {
--ui-container: var(--container-8xl);
}
Components theme
Nuxt UI components are styled using the Tailwind Variants API, which provides a powerful way to create variants and manage component styles. Let's explore the key features of this API:
Slots
Components in Nuxt UI can have multiple slots
, each representing a distinct HTML element or section within the component. These slots allow for flexible content insertion and styling. Let's take the Card component as an example:
export default {
slots: {
root: 'bg-[var(--ui-bg)] ring ring-[var(--ui-border)] divide-y divide-[var(--ui-border)] rounded-[calc(var(--ui-radius)*2)] shadow-sm',
header: 'p-4 sm:px-6',
body: 'p-4 sm:p-6',
footer: 'p-4 sm:px-6'
}
}
<template>
<div :class="ui.root({ class: [props.class, props.ui?.root] })">
<div :class="ui.header({ class: props.ui?.header })">
<slot name="header" />
</div>
<div :class="ui.body({ class: props.ui?.body })">
<slot />
</div>
<div :class="ui.footer({ class: props.ui?.footer })">
<slot name="footer" />
</div>
</div>
</template>
Some components don't have slots, they are just composed of a single root element. In this case, the theme only defines the base
slot like the Container component for example:
export default {
base: 'max-w-[var(--ui-container)] mx-auto px-4 sm:px-6 lg:px-8'
}
<template>
<div :class="container({ class: props.class })">
<slot />
</div>
</template>
ui
prop, only the class
prop is available to override styles.Variants
Nuxt UI components use variants
to change the slots
styles based on props. Here's an example of the Avatar component:
export default {
slots: {
root: 'inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle bg-[var(--ui-bg-elevated)]',
image: 'h-full w-full rounded-[inherit] object-cover'
},
variants: {
size: {
sm: {
root: 'size-7 text-sm'
},
md: {
root: 'size-8 text-base'
},
lg: {
root: 'size-9 text-lg'
}
}
},
defaultVariants: {
size: 'md'
}
}
This way, the size
prop will apply the corresponding styles to the root
slot:
<template>
<UAvatar src="https://github.com/nuxt.png" size="lg" />
</template>
The defaultVariants
property specifies the default values for each variant. It determines how a component looks and behaves when no prop is provided.
app.config.ts
to adjust the standard appearance of components throughout your application.vite.config.ts
to adjust the standard appearance of components throughout your application.Customize theme
You have multiple ways to customize the appearance of Nuxt UI components, you can do it for all components at once or on a per-component basis.
tailwind-merge
under the hood to merge classes so you don't have to worry about conflicting classes.- Check the
Theme
section in the documentation of each individual component. - Browse the source code directly in the GitHub repository at
v3/src/theme
.
Config
You can override the theme of components globally inside your app.config.ts
by using the exact same structure as the theme object.
Let's say you want to change the font weight of all your buttons, you can do it like this:
export default defineAppConfig({
ui: {
button: {
slots: {
base: 'font-bold'
}
}
}
})
You can override the theme of components globally inside your vite.config.ts
by using the exact same structure as the theme object.
Let's say you want to change the font weight of all your buttons, you can do it like this:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
button: {
slots: {
base: 'font-bold'
}
}
}
})
]
})
font-bold
class will override the default font-medium
class on all buttons.Props
ui
prop
You can also override a component's slots using the ui
prop. This has priority over the global configuration and variants
resolution.
<template>
<UButton
trailing-icon="i-lucide-chevron-right"
color="neutral"
variant="outline"
:ui="{
trailingIcon: 'rotate-90 size-3'
}"
>
Button
</UButton>
</template>
trailingIcon
slot is overwritten with size-3
even though the md
size variant would apply a size-5
class to it.class
prop
The class
prop allows you to override the classes of the root
or base
slot. This has priority over the global configuration and variants
resolution.
<template>
<UButton class="font-bold rounded-full">Button</UButton>
</template>
font-bold
class will override the default font-medium
class on this button.