ThemeToggle
Import
Section titled “Import”import { ThemeToggle } from '@delightstack/components';Basic Usage
Section titled “Basic Usage”The live demos on this page are display-only: they cycle their icon and label but leave this site’s theme alone (each passes apply_theme={false}). Use the switch in the site header to change the theme.
View code
<ThemeToggle />Examples
Section titled “Examples”Bindable Theme State
Section titled “Bindable Theme State”Bind to the theme prop to read or control the current theme preference.
<script> import { ThemeToggle } from '@delightstack/components';
let theme = $state<'light' | 'dark' | 'auto'>('auto');</script>
<ThemeToggle bind:theme /><p>Current theme: {theme}</p>Two-State Toggle (No Auto)
Section titled “Two-State Toggle (No Auto)”Disable the system/auto option to cycle only between light and dark.
View code
<ThemeToggle disable_auto />With a Text Label
Section titled “With a Text Label”Set show_label to display the current mode name (“Light” / “Dark” / “Auto”) beside the icon. Pass label to override the text with your own.
View code
<ThemeToggle show_label /><ThemeToggle show_label label="Appearance" />Detecting Effective Dark Mode
Section titled “Detecting Effective Dark Mode”Bind to is_dark to get the resolved dark state (taking system preference into account when theme is 'auto').
<script> import { ThemeToggle } from '@delightstack/components';
let theme = $state<'light' | 'dark' | 'auto'>('auto'); let is_dark = $state(false);</script>
<ThemeToggle bind:theme bind:is_dark />
{#if is_dark} <p>Dark mode is active</p>{:else} <p>Light mode is active</p>{/if}In a Header / Navbar
Section titled “In a Header / Navbar”Use a smaller size to fit in navigation bars.
<script> import { ThemeToggle } from '@delightstack/components';</script>
<header> <nav><!-- nav items --></nav> <ThemeToggle size="0" /></header>Reacting to Changes
Section titled “Reacting to Changes”Use the onchange callback to run logic when the theme changes.
<ThemeToggle onchange={(newTheme) => { console.log('Theme changed to:', newTheme); }}/>Custom Tooltip
Section titled “Custom Tooltip”Override the default tooltip text.
View code
<ThemeToggle tooltip="Change appearance" />| Prop | Type | Default | Description |
|---|---|---|---|
theme | 'light' | 'dark' | 'auto' | 'auto' | Current theme preference ($bindable()) |
is_dark | boolean | false | Computed resolved dark state ($bindable(), read-only). True when effective appearance is dark. |
size | '0' | '1' | '2' | '1' | Component size ('0' = 18px icon, '1' = 24px icon, '2' = 32px icon) |
disable_auto | boolean | false | Hide the auto/system option (two-state toggle only) |
apply_theme | boolean | true | Apply the theme to <html> as data-theme. Set false for a display-only toggle (a demo or preview): it still cycles, stores the choice and updates its own icon and label, but never touches <html>. |
show_label | boolean | false | Show a text label beside the icon (“Light” / “Dark” / “Auto”) |
label | string | undefined | Custom label text override. Falls back to the current mode name when show_label is set. |
tooltip | string | '' | Override the default tooltip text. Defaults to the aria-label value. |
id | string | undefined | Element ID |
class | string | '' | Additional CSS classes |
onchange | (theme: 'light' | 'dark' | 'auto') => void | undefined | Called when the theme changes |
Events
Section titled “Events”| Event | Detail | Description |
|---|---|---|
onchange | 'light' | 'dark' | 'auto' | Fires after cycling to a new theme state |
Theme Cycle Order
Section titled “Theme Cycle Order”| Mode | Cycle |
|---|---|
| Three-state (default) | light -> dark -> auto -> light |
Two-state (disable_auto) | light -> dark -> light |
Theme Application
Section titled “Theme Application”When the theme changes, the component:
- Sets
data-theme="light"ordata-theme="dark"on<html>for an explicit choice, and removes the attribute for'auto'so the OS preference applies.@delightstack/stylesmaps the attribute tocolor-scheme. Skipped whenapply_themeisfalse. - Saves the preference to
localStorageunder the key'delightstack:theme'. - Watches for system preference changes via
matchMedia('(prefers-color-scheme: dark)')and updatesis_darkreactively when theme is'auto'.
It does not set an inline color-scheme. A production build that lowers light-dark() (Lightning CSS, Vite’s default CSS minifier, does) replaces each call with custom properties that only the stylesheet’s color-scheme rules set, so an inline style would leave every token unchanged.
Anti-Flash Script
Section titled “Anti-Flash Script”To prevent a flash of wrong theme on page load, add the following inline script to your HTML <head> (runs synchronously before the body renders):
<head> <script> try { var saved = localStorage.getItem('delightstack:theme'); if (saved === 'light' || saved === 'dark') document.documentElement.dataset.theme = saved; } catch (e) {} </script></head>Accessibility
Section titled “Accessibility”- Renders as a
<button>element with a descriptivearia-label(e.g., “Theme: dark”, “Theme: auto (system preference)”) - Keyboard toggle via Space and Enter keys
- Focus ring on
:focus-visible - Tooltip via
{@attach tooltip()}showing the current state - The SVG icon uses
aria-hidden="true"since the button’saria-labelconveys the meaning - When theme is
'auto', a small “A” indicator appears on the button