Skip to content

Theme switcher

JavaScripttheme.js Requires _defaults.js

Theme control is provided by theme.js. There are two kinds of control: a switcher (toggles light ⇄ dark) and setters (pick an explicit theme, including system). Both are wired automatically from data-* attributes.

Toggle vs. explicit setters
Open ↗
components/theme-switcher.html
<div class="flex flex-wrap items-center gap-6">
<!-- Simple toggle: flips between light and dark on click -->
<button type="button" class="button-secondary" data-theme-switcher>Toggle theme</button>
<!-- Explicit setters: each button sets one theme; the active one gets [data-active] -->
<div class="inline-flex gap-1 rounded-xl border border-border p-1">
<button type="button" class="button-ghost-foreground data-active:button-tertiary" data-theme-set="system">System</button>
<button type="button" class="button-ghost-foreground data-active:button-tertiary" data-theme-set="light">Light</button>
<button type="button" class="button-ghost-foreground data-active:button-tertiary" data-theme-set="dark">Dark</button>
</div>
</div>
<p class="mt-4 text-foreground-subtle">
The surface follows the current theme —
<span class="font-medium text-foreground-primary">background</span> and
<span class="font-medium text-foreground">text</span> tokens repaint automatically.
</p>
<!-- toggles between light and dark -->
<button type="button" data-theme-switcher>Toggle theme</button>
<!-- explicit setters; the active one receives [data-active] -->
<button type="button" data-theme-set="system">System</button>
<button type="button" data-theme-set="light">Light</button>
<button type="button" data-theme-set="dark">Dark</button>
Attribute On Meaning
data-theme-switcher any clickable Clicking flips light ⇄ dark.
data-theme-set="system|light|dark" any clickable Clicking sets that exact theme.
data-active a setter [auto-set] — present on the setter whose theme is active. Style with data-active:….
data-theme-switcher-created a switcher [auto-set] [internal] — idempotency guard.
data-theme-set-created a setter [auto-set] [internal] — idempotency guard.
Function Signature Purpose
setTheme(theme) 'system' | 'light' | 'dark' → void Apply a theme, persist it, update [data-active] on setters.
getTheme() { theme, current } theme is the saved choice; current is the resolved light/dark.
createThemeSwitchers() → void Wire any new [data-theme-switcher] elements.
createThemeSetters() → void Wire any new [data-theme-set] elements.
  • _defaults.js — the theme is stored under window._storage.theme (the key theme). Loading theme.js without _defaults.js will fail.
  • The pre-paint head script — applies the saved theme to <html> before first paint so there’s no flash. It reads the same localStorage key. Keep it in the head; see Vanilla setup.

setTheme toggles the light / dark class on <html>. All semantic and action tokens carry dark-mode values, so a single class change repaints the whole UI. Transitions are momentarily disabled during the swap to avoid a color-animation flash.