@redrob-labs/ui v1.0.2 · Redrob Group Design System 2026

ThemeSwitch

Frames · src/components/ThemeSwitch/ThemeSwitch.tsx

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

import { ThemeSwitch } from '@redrob-labs/ui';

Rendered

The design system's own preview case, run against this package's build. The parity gate compares this rendering to the reference bundle's.

Props

PropTypeNotes
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