Skip to content

Theming

The theme of @luxeanor/ui = color family × light/dark mode, driven by an attribute contract on <html>:

html
<html data-theme="amethyst | obsidian | ink | emerald" data-mode="light | dark">
  • data-theme sets the primary hue (--lx-primary and its derived gradients, glows, containers)
  • data-mode sets the neutral scales (background ladder, text ladder, borders, shadows)

4 families × 2 modes

Familylight (primary / bg)dark (primary / bg)
amethyst (default)#6200EE#FEF7FF#9D8CFF#0B0A10
obsidian#8A6820#FDFAF3#D2B37B#0B0C0F
ink#1E5AE0#F7F9FD#7CA4F8#070C15
emerald#1E7A55#F6FAF7#46C08A#08110E

The LxThemeSwitcher sits in this site's navbar — try it, the whole site (demos included) re-themes instantly.

Runtime switching: useTheme

ts
import { useTheme } from '@luxeanor/ui'

const { family, mode, resolvedMode, setFamily, setMode } = useTheme()

setFamily('ink')     // 'amethyst' | 'obsidian' | 'ink' | 'emerald'
setMode('auto')      // 'light' | 'dark' | 'auto' (follow system)
resolvedMode.value   // 'light' | 'dark' (auto resolved)
  • Module-level singleton: every caller shares the same state; the first call initializes it
  • Persisted to localStorage (key lx-theme), survives reloads
  • auto follows the system prefers-color-scheme live
  • Under SSR it returns defaults and never touches the DOM

Ready-made UI: the ThemeSwitcher component wraps the whole interaction.

Flash-free first paint: themeBootScript

The attributes must land on <html> before CSS parses, or the first frame flashes the default theme. Inline the exported themeBootScript string into your index.html <head> (before any stylesheet):

html
<head>
  <script>/* themeBootScript contents */</script>
  <!-- stylesheets come after -->
</head>

Vite apps can inject it at build time; VitePress/Nuxt via their head config (this site does exactly that).

Migrating from 0.1.x (BREAKING)

Since v0.2.0 the meaning of data-theme changed: from dark | light to the family name; the light/dark axis moved to the new data-mode attribute.

0.1.x0.2.0+
Dark modedata-theme="dark"data-theme="<family>" + data-mode="dark"
Defaultdark black-goldamethyst light

Consumers that wrote data-theme="light|dark" in 0.1.x must adopt the new contract after upgrading, otherwise they fall back to the default family amethyst.