System, light, dark - three options, not a toggle.
A two-state toggle cannot say "follow my computer", which is what most people want and what a fresh visit should do. So system is a real choice that keeps following the OS while it is selected, and the component writes both data-theme-mode (what was chosen) and data-theme (what is rendered).
A radiogroup with arrow-key movement and a roving tabindex, because these are three states of one setting rather than three independent buttons.
The MutationObserver keeps two switches on one page in step - the header's and the one that travels into the mobile drawer. Without it, changing the theme in the drawer leaves the header's switch showing the old value.
Every localStorage access is wrapped: in a private window it throws, and a theme switch that crashes the page is worse than one that forgets.
import { ThemeSwitch } from '@redrob-labs/ui';
| Prop | Type | Notes |
|---|---|---|
value |
ThemeMode |
|
defaultValue |
ThemeMode |
|
storageKey |
string |
Where to persist the choice. Omit it and the choice lasts one page. |
target |
HTMLElement | null |
Element the attributes are written to. Defaults to <html>. |
label |
string |
|
labels |
Partial<Record<ThemeMode, string>> |
|
size |
'sm' | 'md' |
|
className |
string |
|
onChange |
(mode: ThemeMode) => void |