Skip to content

Documentation

Theming

Bootstrap the root theme before content, then synchronize the user's preference.

idle

Theme and font layout

Import the theme helpers from @mizen-labs/ui/theme. Emit ThemeScript synchronously in the server or static document head before body content. Wrap the app in ThemeProvider. Both default to system and the mizen-theme storage key.

The app owns the fonts. This example uses system fonts. Load custom families only when needed and apply your interface font to body. The package stylesheet supplies the default light and dark themes. Custom createTheme overrides require your own StyleX compiler setup.

app/layout.tsx

Choose a preference

useTheme returns theme, resolvedTheme, systemTheme, and setTheme. The preference is light, dark, or system. resolvedTheme is always light or dark. Read preference state inside a Client Component.

If you override storageKey or defaultTheme, use the same values on ThemeScript and ThemeProvider. ThemeScript accepts a CSP nonce. ThemeProvider accepts a storage adapter with getItem and setItem; the head bootstrap still reads browser localStorage.

Root theme and portals

ThemeProvider synchronizes data-theme, color-scheme, and the generated dark token classes on document.documentElement. Overlays portaled to document.body inherit that root theme.

There is no general nested theme provider. A local theme scope does not reach a portal outside its subtree. Use the overlay's documented portal container within that scope when local theming is necessary. Never hardcode generated theme classes.

System preferences

Components honor reduced motion and forced colors through shared conditions. Forced colors use the user's system palette rather than preserving wine and lavender. Keep keyboard focus visible.

Do not implement a second theme store or recolor component internals. Check theme changes with an open overlay, keyboard focus, and selected states. First-paint behavior needs a real browser check, not only a class assertion.