molecule · stable · site, app, extension · since 2026-08
ThemeSwitch
Purpose
The choice of theme: automatic, light, dark. Following the system is a good default and not a sufficient answer; for whoever suffers from photophobia or visual fatigue, the choice is a reading condition. Three states rather than two, so that choosing does not mean giving up the system’s switch at sunset.
When to use, when not
Once per surface, in the header or the sidebar. A second instance on a page, as in a mobile menu, takes its own name.
Anatomy
- A fieldset with a visually hidden legend that names the group and says the current choice.
- Three labels, each wrapping a visually hidden radio and its word.
Props
| Name | Type | Default | What it does |
|---|---|---|---|
name? | string | 'theme' | The radio group's name. Two switches on one page each need their own, or the browser merges them into one group. |
onchange? | ((theme: Theme) => void) | undefined | Called with the choice once it is stored and applied: a surface that repaints something else with the theme (the extension's page furniture) hears it here. |
States
Automatic, light or dark checked.
named
A second switch on the same page takes its own group name, or the browser merges the two.
Light
Dark
320 px, light
320 px, dark
216 px, light
Forced colours, captured
plain
The group reads the stored choice at mount; on the docs site it is whatever the reader chose.
Light
Dark
320 px, light
320 px, dark
216 px, light
Forced colours, captured
Accessibility contract
- Role and name: a radio group named “Thème d’affichage”; each radio named Auto, Clair or Sombre.
- Keyboard: Tab enters on the checked radio, the arrows move the choice, nothing else.
- Focus: shown on the visible label, since the radio is out of the visual field; never
display: none, which would remove the radio from the tab order. - Forced colours: the checked option is drawn with a border, since a background fill is dropped.
- The choice is read, stored and applied by
theme/theme.ts, the same module on the three surfaces.
Content rules
The three French words: Auto, Clair, Sombre.
Do and don’t
- Do keep it a radio group; a cycling button makes the user guess what comes next.
- Do not hide the radios with
display: none.
Examples
<ThemeSwitch />
<ThemeSwitch name="theme-mobile" /> Surfaces
- Site: the header, and the mobile menu with its own name.
- App: the lab sidebar.
- Extension: the panel’s toggle cycles the same three choices through the same module.
Related
None.
Decisions
- 2026-08: native radios, so the state is announced without a line of script.
- 2026-09-24: reads and writes through the shared theme module.
- 2026-09-24: renamed from ChoixTheme; the legend says “actuellement” with a comma, no dash.
- 2026-09-27:
onchange, called once the choice is stored and applied. The extension’s panel keeps the theme in a store that also tells the audited page, so its highlights repaint; the switch stored and applied on its own, and the page never heard of the change.
Criteria held by construction
- 11.1, Chaque champ de formulaire a-t-il une étiquette ?
Three native radios in a fieldset whose legend names the group; each option is a label. - 11.5, Dans chaque formulaire, les champs de même nature sont-ils regroupés, si nécessaire ?
The three choices are grouped in a fieldset with a legend. - 10.7, Dans chaque page web, pour chaque élément recevant le focus, la prise de focus est-elle visible ?
The radio stays in the tab order and the focus shows on its visible label.
Tokens it reads
- Colour:
--rl-action,--rl-ink,--rl-ink-soft,--rl-line-control,--rl-on-fill,--rl-sunken,--rl-focus,--rl-focus-offset,--rl-focus-width - Typography:
--rl-weight-bold,--rl-text-xs - Layout:
--rl-radius,--rl-border,--rl-border-strong