Files
ui/docs/content/1.getting-started/3.theme.md
2025-05-07 17:23:27 +02:00

31 KiB

title, description, navigation.icon
title description navigation.icon
Theme Learn how to customize Nuxt UI components using Tailwind CSS v4, CSS variables and the Tailwind Variants API for powerful and flexible theming. i-lucide-swatch-book

Tailwind CSS

Nuxt UI uses Tailwind CSS v4, you can read the official upgrade guide 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 directive to define your project's custom design tokens, like fonts, colors, and breakpoints:

::module-only #ui :::div

@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

@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 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 for example:

::module-only #ui :::div

@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

@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. 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

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 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'
    }
  }
})

:::

#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

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

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 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'
      ]
    }
  }
})

:::

#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 option of the ui plugin:

::::module-only

#ui

:::::div

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

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

: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);
}

::

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

<template>
  <span class="text-primary">Primary</span>
  <span class="text-secondary">Secondary</span>
  <span class="text-success">Success</span>
  <span class="text-info">Info</span>
  <span class="text-warning">Warning</span>
  <span class="text-error">Error</span>
</template>

::

::note This is how the @theme is generated for each color alias:

:::code-collapse{class="[&>div]:!my-0"}

@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"}

@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"}

@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 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 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"}

@import "tailwindcss";
@import "@nuxt/ui";

:root {
  --ui-primary: black;
}

.dark {
  --ui-primary: white;
}

:::

#ui-pro :::div{class="*:!mb-0 *:!mt-2.5"}

@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

: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);
}
.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

<template>
  <span class="text-dimmed">Dimmed</span>
  <span class="text-muted">Muted</span>
  <span class="text-toned">Toned</span>
  <span class="text-default">Text</span>
  <span class="text-highlighted">Highlighted</span>
  <span class="text-inverted bg-inverted">Inverted</span>
</template>

::

::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

<template>
  <div class="bg-default">Default</div>
  <div class="bg-muted">Muted</div>
  <div class="bg-elevated">Elevated</div>
  <div class="bg-accented">Accented</div>
  <div class="bg-inverted text-inverted">Inverted</div>
</template>

::

::code-preview [Default]{class="border-2 border-default text-sm px-4 py-1.5 inline-block rounded-md mr-2"} [Muted]{class="border-2 border-muted text-sm px-4 py-1.5 inline-block rounded-md mr-2"} [Accented]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-md mr-2"} [Inverted]{class="border-2 border-inverted text-sm px-4 py-1.5 inline-block rounded-md"}

#code

<template>
  <div class="border border-default">Default</div>
  <div class="border border-muted">Muted</div>
  <div class="border border-accented">Accented</div>
  <div class="border border-inverted">Inverted</div>
</template>

::

::note This is how the @theme is generated for each design token:

:::code-collapse{class="[&>div]:!my-0"}

@theme default {
  --text-color-dimmed: var(--ui-text-dimmed);
  --text-color-muted: var(--ui-text-muted);
  --text-color-toned: var(--ui-text-toned);
  --text-color-default: var(--ui-text);
  --text-color-highlighted: var(--ui-text-highlighted);
  --text-color-inverted: var(--ui-text-inverted);
  --background-color-default: var(--ui-bg);
  --background-color-muted: var(--ui-bg-muted);
  --background-color-elevated: var(--ui-bg-elevated);
  --background-color-accented: var(--ui-bg-accented);
  --background-color-inverted: var(--ui-bg-inverted);
  --background-color-border: var(--ui-border);
  --border-color-default: var(--ui-border);
  --border-color-muted: var(--ui-border-muted);
  --border-color-accented: var(--ui-border-accented);
  --border-color-inverted: var(--ui-border-inverted);
  --border-color-bg: var(--ui-bg);
  --ring-color-default: var(--ui-border);
  --ring-color-muted: var(--ui-border-muted);
  --ring-color-accented: var(--ui-border-accented);
  --ring-color-inverted: var(--ui-border-inverted);
  --ring-color-bg: var(--ui-bg);
  --ring-offset-color-default: var(--ui-border);
  --ring-offset-color-muted: var(--ui-border-muted);
  --ring-offset-color-accented: var(--ui-border-accented);
  --ring-offset-color-inverted: var(--ui-border-inverted);
  --ring-offset-color-bg: var(--ui-bg);
  --divide-color-default: var(--ui-border);
  --divide-color-muted: var(--ui-border-muted);
  --divide-color-accented: var(--ui-border-accented);
  --divide-color-inverted: var(--ui-border-inverted);
  --divide-color-bg: var(--ui-bg);
  --outline-color-default: var(--ui-border);
  --outline-color-inverted: var(--ui-border-inverted);
  --stroke-color-default: var(--ui-border);
  --stroke-color-inverted: var(--ui-border-inverted);
  --fill-color-default: var(--ui-border);
  --fill-color-inverted: var(--ui-border-inverted);
}

:::

::

You can customize these CSS variables to tailor the appearance of your application in your main.css file:

::module-only #ui :::div{class="*:!mb-0 *:!mt-2.5"}

@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);
}

:::

#ui-pro :::div{class="*:!mb-0 *:!mt-2.5"}

@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);
}

::: ::

::note Nuxt UI applies a text and background color on the <body> element of your app:

body {
  @apply antialiased text-default bg-default scheme-light dark:scheme-dark;
}

::

Radius

Nuxt UI provides a centralized border radius system through the --ui-radius CSS variable.

:root {
  --ui-radius: 0.25rem;
}

This CSS variable replaces Tailwind CSS's default rounded-* utilities so you can use the same class names:

::code-preview [xs]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-xs mr-2"} [sm]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-sm mr-2"} [md]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-md mr-2"} [lg]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-lg mr-2"} [xl]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-xl mr-2"} [2xl]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-2xl mr-2"} [3xl]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-3xl mr-2"}

#code

<template>
  <div class="rounded-xs">xs</div>
  <div class="rounded-sm">sm</div>
  <div class="rounded-md">md</div>
  <div class="rounded-lg">lg</div>
  <div class="rounded-xl">xl</div>
  <div class="rounded-2xl">2xl</div>
  <div class="rounded-3xl">3xl</div>
</template>

::

::note This is how the @theme is generated for each radius value:

:::code-collapse{class="[&>div]:!my-0"}

@theme default {
  --radius-xs: calc(var(--ui-radius) * 0.5);    /* 0.125rem */
  --radius-sm: var(--ui-radius);                /* 0.25rem */
  --radius-md: calc(var(--ui-radius) * 1.5);    /* 0.375rem */
  --radius-lg: calc(var(--ui-radius) * 2);      /* 0.5rem */
  --radius-xl: calc(var(--ui-radius) * 3);      /* 0.75rem */
  --radius-2xl: calc(var(--ui-radius) * 4);     /* 1rem */
  --radius-3xl: calc(var(--ui-radius) * 6);     /* 1.5rem */
}

:::

::

You can customize the base radius value in your main.css file:

::module-only #ui :::div{class="*:!mb-0 *:!mt-2.5"}

@import "tailwindcss";
@import "@nuxt/ui";

:root {
  --ui-radius: 0.5rem;
}

:::

#ui-pro :::div{class="*:!mb-0 *:!mt-2.5"}

@import "tailwindcss";
@import "@nuxt/ui-pro";

:root {
  --ui-radius: 0.5rem;
}

::: ::

::note Try the :prose-icon{name="i-lucide-swatch-book" class="text-primary"} theme picker in the header above to change the base radius value. ::

Container

Nuxt UI provides a --ui-container CSS variable that controls the maximum width of the Container component.

:root {
  --ui-container: var(--container-7xl);
}

You can customize this value in your main.css file to adjust container widths consistently throughout your application:

::module-only #ui :::div{class="*:!mb-0 *:!mt-2.5"}

@import "tailwindcss";
@import "@nuxt/ui";

@theme {
  --container-8xl: 90rem;
}

:root {
  --ui-container: var(--container-8xl);
}

:::

#ui-pro :::div{class="*:!mb-0 *:!mt-2.5"}

@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:

::code-group

export default {
  slots: {
    root: 'bg-default ring ring-default divide-y divide-default rounded-lg',
    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.ui?.root, props.class] })">
    <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:

::code-group

export default {
  base: 'max-w-(--ui-container) mx-auto px-4 sm:px-6 lg:px-8'
}
<template>
  <div :class="container({ class: props.class })">
    <slot />
  </div>
</template>

::

::warning Components without slots don't have a 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-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:

::component-code

ignore:


::

The defaultVariants property specifies the default values for each variant. It determines how a component looks and behaves when no prop is provided.

::framework-only #nuxt :::tip These default values can be customized in your app.config.ts to adjust the standard appearance of components throughout your application. :::

#vue :::tip These default values can be customized in your 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.

::note Tailwind Variants uses tailwind-merge under the hood to merge classes so you don't have to worry about conflicting classes. ::

::tip You can explore the theme for each component in two ways:

  • 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

::framework-only #nuxt

:::div 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'
      }
    }
  }
})

:::

#vue

:::div 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:

::::module-only

#ui

:::::div

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'
          }
        }
      }
    })
  ]
})

:::::

#ui-pro

:::::div

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uiPro from '@nuxt/ui-pro/vite'

export default defineConfig({
  plugins: [
    vue(),
    uiPro({
      ui: {
        button: {
          slots: {
            base: 'font-bold'
          }
        }
      }
    })
  ]
})

:::::

::::

:::

::

::note In this example, the 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.

::component-code

prettier: true ignore:

  • ui.trailingIcon

  • color

  • variant

  • size

  • icon props: trailingIcon: i-lucide-chevron-right size: md color: neutral variant: outline ui: trailingIcon: 'rotate-90 size-3' slots: default: |

    Button


::

::note In this example, the 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.

::component-code

props: class: 'font-bold rounded-full' slots: default: Button

::

::note In this example, the font-bold class will override the default font-medium class on this button. ::