A one-button theme switcher that cycles through the system, light and dark modes, showing the icon of the current mode and saving the choice.
.theme-switcherwithdata-theme-switcherholds three buttons; only the one of the current mode (data-theme-modeon the switcher) is visible.- Each button's
data-actionis the mode it switches to: system → light → dark → system.
- Each button's
aria-labelsays what it does ("Switch to dark mode"), not what's active, so the action is clear. After a click, the focus moves to the newly visible button, which announces the next action. - The icons are decorative (
aria-hidden="true"). - If the three-state cycle is confusing for your audience, use the Theme Switcher Select, which lists the options.
- Use it more than once on a page (in the header and the footer): every
[data-theme-switcher]stays in sync. - It sets
data-theme-modeon thehtmlelement, the default dark mode selector of Spruce (see thedark-modesettings), and stores the choice inlocalStorage(preferred-theme). In "system" mode, it follows the operating system live. - The
no-transitionclass turns off the transitions for a frame, so the colors don't animate while switching.
The switcher only changes the mode. To avoid a flash of the wrong colors, set the mode before the page renders with an inline script in the head:
<script>
(() => {
const preferred = localStorage.getItem('preferred-theme') ?? 'system';
const system = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme-mode', preferred === 'system' ? system : preferred);
})();
</script>With Spruce's dark-mode settings, prefers-color-scheme: true can follow the system without JavaScript; the switcher then stores an explicit choice. See Themes and Dark Mode.


