Theme Toggle
betaA light/dark switch whose sun retracts its rays and is eclipsed into a crescent moon — as an icon button or a switch track.
- Forms
- 1 file
- 2 npm packages
- Accessibility notes
Loading the example…
pnpm dlx @dowel-ui/cli add theme-togglenpm packages installed: class-variance-authority, radix-ui.
import { ThemeToggle } from "@/components/ui/theme-toggle";Default
Switch
Sizes
Wired
Starts Dark
Disabled
<ThemeToggle>
| Prop | Type | Default |
|---|---|---|
defaultThemeInitial theme when uncontrolled. Default "light". | ThemeToggleTheme | "light" |
onThemeChangeCalled with the theme the toggle asks for. Apply it to the document yourself. | (theme: ThemeToggleTheme) => void | — |
size | "sm" | "md" | "lg" | "md" |
themeControlled theme. | ThemeToggleTheme | — |
variant | "icon" | "switch" | "icon" |
Every prop of Radix UI’s Switch.Root, plus className.
Built on the Radix switch primitive: role="switch" with aria-checked (on = dark), toggled by click, Space or Enter, with the shared focus ring. It is named "Dark mode" unless given aria-label or aria-labelledby. A switch rather than an aria-pressed button because it flips a setting that applies at once, and on/off says what is true now, where a pressed sun or moon is ambiguous. The glyph is aria-hidden. It never changes the document itself; the consumer applies the theme from onThemeChange. Under reduced motion the sun and moon swap instantly.
10/10
checks passed
- Tested — passes
- axe assertion — passes
- Keyboard tested — passes
- Storybook examples — passes
- Accessibility documented — passes
- Semantic tokens only — passes
- Motion from tokens — passes
- className merged — passes
- Visible focus — passes
- No fixed widths — passes