Color mode
Light/dark/auto color mode with an SSR-safe, flash-free theme, plus the opt-in color palettes.
@adminlte/vue supports light, dark, and auto color modes built on Bootstrap's data-bs-theme
attribute. The preference is persisted to localStorage and applied before first paint in Nuxt, so
there is no flash of the wrong theme on reload.
Overview
Color mode is provided by LteDashboardLayout, which calls provideColorMode() in its setup().
Descendant components read and update the mode through the useColorMode() composable. The state is:
colorMode— the user's preference:'light' | 'dark' | 'auto'.resolvedMode— the concrete mode applied to the page:'light' | 'dark'. WhencolorModeis'auto', it follows the systemprefers-color-schemesetting.
The resolved mode is written to data-bs-theme on the <html> element, and the preference is saved
under the lte-theme localStorage key.
Usage
The toggle component
LteColorModeToggle is a ready-made dropdown (rendered as a navbar <li>) that lets the user pick
Light, Dark, or Auto. It takes no props and must live inside LteDashboardLayout — typically in the
topbar's navbar list. In Nuxt it is auto-registered, so no import is needed.
<template>
<ul class="navbar-nav ms-auto">
<ClientOnly>
<LteColorModeToggle />
</ClientOnly>
</ul>
</template>
Wrapping the toggle in <ClientOnly> avoids a hydration mismatch on the trigger glyph, which
depends on the persisted preference resolved on the client.
Reading and setting the mode
Use useColorMode() anywhere inside the layout to read or change the mode programmatically.
<script setup lang="ts">
const { colorMode, resolvedMode, setColorMode } = useColorMode()
</script>
<template>
<div>
<p>Preference: {{ colorMode }} (resolved: {{ resolvedMode }})</p>
<button @click="setColorMode('light')">Light</button>
<button @click="setColorMode('dark')">Dark</button>
<button @click="setColorMode('auto')">Auto</button>
</div>
</template>
colorMode is a writable Ref, so colorMode.value = 'dark' works too — setColorMode() is just a
convenience wrapper.
useColorMode() API
useColorMode() injects the color-mode state provided by LteDashboardLayout. Calling it outside
the layout throws an error.
| Name | Type | Description |
|---|---|---|
colorMode | Ref<'light' | 'dark' | 'auto'> | The user's color-mode preference. Writable. |
resolvedMode | ComputedRef<'light' | 'dark'> | The concrete mode applied to the page; resolves 'auto' against the system setting. |
setColorMode | (mode: 'light' | 'dark' | 'auto') => void | Set the preference. Persists to localStorage and updates data-bs-theme. |
import { useColorMode } from '@adminlte/vue'
const { colorMode, resolvedMode, setColorMode } = useColorMode()
Setting the initial mode
The initial preference defaults to 'auto'. In Nuxt, set it through the module's defaults:
export default defineNuxtConfig({
modules: ['@adminlte/nuxt'],
adminlte: {
defaults: {
initialColorMode: 'auto', // 'light' | 'dark' | 'auto'
},
},
})
This value is used as the fallback by the blocking head script and seeds the layout's initial state.
A persisted lte-theme value always takes precedence once the page loads.
How the flash is avoided in Nuxt
A flash of the wrong theme happens when the saved preference is only applied after Vue hydrates. To
prevent it, @adminlte/nuxt injects a small blocking inline <head> script (keyed
lte-theme-init) that runs before first paint. It reads lte-theme from localStorage (falling
back to initialColorMode), resolves auto against prefers-color-scheme, and sets data-bs-theme
on <html> immediately:
(function () {
try {
var k = localStorage.getItem('lte-theme') || 'auto'
var d = k === 'auto'
? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
: k
document.documentElement.setAttribute('data-bs-theme', d)
} catch (e) {}
})()
After hydration, useColorMode() owns all reactive updates — it reconciles with the persisted
preference, listens for system prefers-color-scheme changes (while in auto), and re-applies
data-bs-theme whenever the mode changes.
The script is on by default. You can disable it with the themeScript module option (for example if
you inject your own):
export default defineNuxtConfig({
modules: ['@adminlte/nuxt'],
adminlte: {
themeScript: false,
},
})
Extended color palettes
Color mode is about light vs. dark. If you also want more colors, AdminLTE core ships two
opt-in stylesheets, re-exported by this package. Neither is loaded by default — @adminlte/vue/css
is unchanged — and you load one or the other, never both, because they share class names.
| Export | Contents |
|---|---|
@adminlte/vue/css/colors | The designed palette (core 4.4.0) — 14 extra colors (orange, amber, olive, teal, sky, indigo, violet, fuchsia, pink, navy, steel, slate, graphite, midnight), each generated so white text on it clears WCAG AA. |
@adminlte/vue/css/colors/v3 | The AdminLTE 3 palette (core 4.5.0) — the original 18 colors with their v3 values and v3's text-color rule. |
Both add the same class families for their colors: .bg-*, .text-bg-*, .text-*, .border-*,
.link-*, .bg-gradient-*, .card-*, .callout-* and .direct-chat-*.
export default defineNuxtConfig({
modules: ['@adminlte/nuxt'],
css: ['@adminlte/vue/css/colors'], // or '@adminlte/vue/css/colors/v3'
})
import '@adminlte/vue/css'
import '@adminlte/vue/css/colors'
Making a palette color the primary
With one of the sheets loaded, data-lte-primary on <html> (or on any container) repoints
Bootstrap's primary at that color — buttons, links, pagination, .nav-pills, progress bars, list
groups, accordions and form-control / checkbox focus rings all follow, in both color modes:
export default defineNuxtConfig({
app: { head: { htmlAttrs: { 'data-lte-primary': 'teal' } } },
})
That means the components you already render with theme="primary" — LteCard, LteSmallBox,
LteButton, LteCallout, LteDirectChat, … — become teal without touching your markup. The typed
theme props stay the eight Bootstrap names; the palette colors are used as plain classes
(class="text-bg-teal", class="callout callout-olive") wherever you write markup yourself.
AA text on the v3 palette
Eight of the AdminLTE 3 colors fall short of 4.5:1 with the white text v3 gave them. Adding
data-lte-contrast="aa" alongside the v3 sheet flips exactly those eight to the ink that passes,
leaving every background unchanged. It is a no-op for the designed palette, which is AA by
construction.