Theming
This module relies on Nuxt App Config file to customize the look and feel of the components at runtime with HMR (hot-module-replacement).
Colors
Configuration
Components are based on a primary
and a gray
color. You can change them in your app.config.ts
.
export default defineAppConfig({
ui: {
primary: 'green',
gray: 'cool'
}
})
primary
and gray
colors by clicking on the button in the header.As this module uses Tailwind CSS under the hood, you can use any of the Tailwind CSS colors or your own custom colors. By default, the primary
color is green
and the gray
color is cool
.
When using custom colors or adding additional colors through the extend
key in your tailwind.config.ts
, you'll need to make sure to define all the shades from 50
to 950
as most of them are used in the components config defined in ui.config/
directory. You can generate your colors using tools such as https://uicolors.app/ for example.
import type { Config } from 'tailwindcss'
import defaultTheme from 'tailwindcss/defaultTheme'
export default <Partial<Config>>{
theme: {
extend: {
colors: {
green: {
50: '#EFFDF5',
100: '#D9FBE8',
200: '#B3F5D1',
300: '#75EDAE',
400: '#00DC82',
500: '#00C16A',
600: '#00A155',
700: '#007F45',
800: '#016538',
900: '#0A5331',
950: '#052e16'
}
}
}
}
}
CSS Variables
To provide dynamic colors that can be changed at runtime, this module uses CSS variables. As Tailwind CSS already has a gray
color, the module automatically renames it to cool
to avoid conflicts (coolGray
was renamed to gray
when Tailwind CSS v3.0 was released).
Likewise, you can't define a primary
color in your tailwind.config.ts
as it would conflict with the primary
color defined by the module.
text-primary-500 dark:text-primary-400
, bg-gray-100 dark:bg-gray-900
, etc. so your app automatically adapts when changing your app.config.ts
.The primary
color also has a DEFAULT
shade that changes based on the theme. It is 500
in light mode and 400
in dark mode. You can use as a shortcut in your components and pages, e.g. text-primary
, bg-primary
, focus-visible:ring-primary
, etc.
Smart Safelisting
Components having a color
prop like Avatar, Badge, Button, Input (inherited in Select and SelectMenu), RadioGroup, Checkbox, Toggle, Range and Notification will use the primary
color by default but will handle all the colors defined in your tailwind.config.ts
or the default Tailwind CSS colors.
Variant classes of those components are defined with a syntax like bg-{color}-500 dark:bg-{color}-400
so they can be used with any color. However, this means that Tailwind will not find those classes and therefore will not generate the corresponding CSS.
The module uses the Tailwind CSS safelist feature to force the generation of all the classes for the primary
color only as it is the default color for all the components.
Then, the module will automatically detect when you use one of those components with a color and will safelist it for you. This means that if you use a red
color for a Button component, the red
color classes will be safelisted for the Button component only. This will allow to keep the CSS bundle size as small as possible.
There is one case where you would want to force the safelisting of a color. For example, if you've set the default color of the Button component to orange
in your app.config.ts
.
export default defineAppConfig({
ui: {
button: {
default: {
color: 'orange'
}
}
}
})
This will apply the orange color when using a default <UButton />
. You'll need to safelist this color manually in your nuxt.config.ts
ui options as we won't be able to detect it automatically. You can do so through the safelistColors
option which defaults to ['primary']
.
export default defineNuxtConfig({
ui: {
safelistColors: ['orange']
}
})
This can also happen when you bind a dynamic color to a component: <UBadge :color="color" />
, <UAvatar :chip-color="statuses[user.status]" />
, etc. In this case, you'll need to safelist the possible color values manually as well.
Components
app.config.ts
You can find the config of each component in the ui.config/
directory. You can override those classes in your own app.config.ts
.
export default defineAppConfig({
ui: {
container: {
constrained: 'max-w-5xl'
}
}
})
Thanks to tailwind-merge, the app.config.ts
is smartly merged with the default config. This means you don't have to rewrite everything.
You can change this behavior by setting strategy
to override
in your app.config.ts
:
export default defineAppConfig({
ui: {
strategy: 'override',
button: {
color: {
white: {
solid: 'bg-white dark:bg-gray-900'
}
}
}
}
})
ui
prop
Each component has a ui
prop that allows you to customize everything specifically.
<template>
<UContainer :ui="{ constrained: 'max-w-2xl' }">
<slot />
</UContainer>
</template>
Config
section.Thanks to tailwind-merge, the ui
prop is smartly merged with the config. This means you don't have to rewrite everything.
For example, the default preset of the FormGroup
component looks like this:
{
"label": {
"base": "block font-medium text-gray-700 dark:text-gray-200"
}
}
To change the font of the label
, you only need to write:
<UFormGroup name="email" label="Email" :ui="{ label: { base: 'font-semibold' } }" />
This will smartly replace the font-medium
by font-semibold
and prevent any class duplication and any class priority issue.
You can change this behavior by setting strategy
to override
inside the ui
prop:
<UButton
to="https://github.com/nuxt/ui"
:ui="{
strategy: 'override',
color: {
white: {
solid: 'bg-white dark:bg-gray-900'
}
}
}"
/>
class
attribute
You can also use the class
attribute to add classes to the component.
<template>
<UButton label="Button" class="rounded-full" />
</template>
Again, with tailwind-merge, this will smartly merge the classes with the ui
prop and the config.
Default values
Some component props like size
, color
, variant
, etc. have a default value that you can override in your app.config.ts
.
export default defineAppConfig({
ui: {
button: {
default: {
size: 'md',
color: 'gray',
variant: 'ghost'
}
}
}
})
Dark mode
All the components are styled with dark mode in mind.
Thanks to Tailwind CSS dark mode class strategy and the @nuxtjs/color-mode module, you literally have nothing to do.
You can disable dark mode by setting the preference
to light
instead of system
in your nuxt.config.ts
.
export default defineNuxtConfig({
colorMode: {
preference: 'light'
}
})
nuxt-color-mode
entry from your browser's local storage.You can easily build a color mode button by using the useColorMode
composable from @nuxtjs/color-mode
.
<script setup lang="ts">
const colorMode = useColorMode()
const isDark = computed({
get () {
return colorMode.value === 'dark'
},
set () {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
})
</script>
<template>
<ClientOnly>
<UButton
:icon="isDark ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
color="gray"
variant="ghost"
aria-label="Theme"
@click="isDark = !isDark"
/>
<template #fallback>
<div class="w-8 h-8" />
</template>
</ClientOnly>
</template>
Icons
You can use any icon (100,000+) from Iconify.
Some components have an icon
prop that allows you to add an icon to the component.
<template>
<UButton icon="i-heroicons-magnifying-glass" />
</template>
You can also use the Icon component to add an icon anywhere in your app by following this pattern: i-{collection_name}-{icon_name}
.
<template>
<UIcon name="i-heroicons-moon" />
</template>
Collections
By default, the module uses Heroicons but you can change it from the module options in your nuxt.config.ts
.
export default defineNuxtConfig({
ui: {
icons: ['mdi', 'simple-icons']
}
})
Thanks to @egoist/tailwindcss-icons plugin, only the icons you use in your app will be bundled in your CSS. However, you need to install the icon collections you specified in the ui.icons
key:
pnpm i @iconify-json/{collection_name}
If you choose to use the full @iconify/json
icon collection (50MB), you can specifiy icons: 'all'
or icons: {}
in your nuxt.config.ts
to use any icon in your app.
export default defineNuxtConfig({
ui: {
icons: {}
}
})
Custom config
If you have specific needs, like using a custom icon collection, you can use the icons
option in your nuxt.config.ts
as an object to override the config of the @egoist/tailwindcss-icons plugin.
import { getIconCollections } from '@egoist/tailwindcss-icons'
export default defineNuxtConfig({
ui: {
icons: {
// might solve stretch bug on generate, see https://github.com/egoist/tailwindcss-icons/issues/23
extraProperties: {
'mask-size': 'contain',
'mask-position': 'center'
},
collections: {
foo: {
icons: {
'arrow-left': {
// svg body
body: '<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18" />',
// svg width and height, optional
width: 24,
height: 24
}
}
},
...getIconCollections(['heroicons', 'simple-icons'])
}
}
}
})
Dynamic icons
The Icon
component also has a dynamic
prop to use the nuxt-icon module instead of the @egoist/tailwindcss-icons plugin.
Read more about this in the Icon component page.
Defaults
You can easily replace all the default icons of the components in your app.config.ts
.
export default defineAppConfig({
ui: {
button: {
default: {
loadingIcon: 'i-octicon-sync-24'
}
},
input: {
default: {
loadingIcon: 'i-octicon-sync-24'
}
},
select: {
default: {
loadingIcon: 'i-octicon-sync-24',
trailingIcon: 'i-octicon-chevron-down-24'
}
},
selectMenu: {
default: {
selectedIcon: 'i-octicon-check-24'
}
},
notification: {
default: {
closeButton: {
icon: 'i-octicon-x-24'
}
}
},
commandPalette: {
default: {
icon: 'i-octicon-search-24',
loadingIcon: 'i-octicon-sync-24',
selectedIcon: 'i-octicon-check-24',
emptyState: {
icon: 'i-octicon-search-24'
}
}
},
table: {
default: {
sortAscIcon: 'i-octicon-sort-asc-24',
sortDescIcon: 'i-octicon-sort-desc-24',
sortButton: {
icon: 'i-octicon-arrow-switch-24'
},
loadingState: {
icon: 'i-octicon-sync-24'
},
emptyState: {
icon: 'i-octicon-database-24'
}
}
},
pagination: {
default: {
firstButton: {
icon: 'i-octicon-chevron-left-24'
},
prevButton: {
icon: 'i-octicon-arrow-left-24'
},
nextButton: {
icon: 'i-octicon-arrow-right-24'
},
lastButton: {
icon: 'i-octicon-chevron-right-24'
}
}
},
accordion: {
default: {
openIcon: 'i-octicon-chevron-down-24'
}
},
breadcrumb: {
default: {
divider: 'i-octicon-chevron-right-24'
}
}
}
})