Theme toggle
A three-state choice of light, dark or system appearance as a native radio group, with the choice remembered and the change announced.
Demo
The switch in the header of this page is the live demo. Try it with the keyboard: Tab to it, arrow keys choose.
Why it is accessible
Dark mode isn't just aesthetics: for people with light sensitivity or migraines, it is accessibility. This switch has three states, so rather than being a button that has to invent its own semantics, it is a plain group of radio buttons (fieldset, legend and three input type="radio"). The browser itself handles the grouping, the arrow-key movement and the “selected” state.
The choice is stored in the browser without cookies and applied before the page's first render, so nothing flashes. A status message announces the change: “Appearance: dark”. Every colour on the website is defined once for both modes, and in both it meets a contrast of 4.5:1 for text and 3:1 for controls.
Test it yourself
Keyboard
- Tab to the switch in the header (the current choice is focused); ← and → change the appearance immediately.
Screen reader
- The group is announced as “Appearance”, each option as a radio button named Light, Dark or System, and after the change you hear a status message.
Zoom and reflow
- The switch has three targets sized 44×44px, and at 400% zoom it moves onto its own line in the header.
Without JavaScript
The switch isn't shown, and the operating system's setting applies via prefers-color-scheme. Both appearances therefore remain available.
Code to copy
<ad-theme-toggle data-label="Appearance" data-light="Light" data-dark="Dark" data-system="System" data-announce="Appearance: {mode}"></ad-theme-toggle>
<script src="/assets/js/theme-init.js"></script>
<link rel="stylesheet" href="/assets/js/components/ad-theme-toggle/ad-theme-toggle.css">
<script type="module" src="/assets/js/components/ad-theme-toggle/ad-theme-toggle.js"></script>