Web platform

Your theme toggle does not need a System button

Your theme toggle does not need a System button

Your header has a sun icon, a moon icon, and a little monitor. Three buttons in a segmented control, or one that cycles Light → Dark → System with a click. Congratulations, you have shipped the data model as UI. Lea Verou's new post says the quiet part loud: that third button is doing work for the developer, not the user, and you can pull it.

The frame is disarmingly simple. Per the source, the user only reaches for a theme toggle when the current appearance is wrong for them right now. Verou states it flatly — "Users do not seek out solutions to problems they don't currently have." That single line is the whole verdict. If the page already looks the way the user wants, they will not open a menu to inform your storage layer of their long-term policy. The System option is you asking them to.

Where the tri-state actually comes from

Verou's diagnosis is that developers, having internalised the three-way model (light, dark, follow system), reflexively expose all three in the UI. The model has three states. The chooser must too. It is the same reflex that gives you a <select> full of every enum a backend can serialise, whether or not a human should ever need to pick one.

Per the source, the three-option pattern is out in the wild in Ant Design, Red Hat Design System, Web Awesome, Excalidraw, Taiga, Astro and Hero UI. Docusaurus is a special case — same three states, but rotated through one icon on click. The two-state approach shows up in Vitepress, Material Design, Adobe Spectrum, Radix and ShadCN. Two roughly equal-sized camps, no consensus, which is exactly the kind of split that gets a good essay written.

Here's the part I keep coming back to. The two-state camp is not hiding a state. The underlying model still has three: no stored preference (follow the OS), stored light, stored dark. What Verou is arguing is that only one of those matters to the user in any given moment — the one they can see. So expose the flip. Keep the other transitions implicit.

What "two-state done right" actually stores

The post does not just wave at this — it prescribes the storage behaviour. Per the source, a proper two-state toggle should:

  • Store nothing initially, so the site follows the OS.
  • On toggle, write light or dark only when the user is asking for the opposite of the current appearance.
  • Toggling back to the state that matches the OS clears the stored value, returning to "follow system".
  • Never proactively clear an override when the OS preference changes to align with it. Per the source, "This evaluation must only happen at user interaction."

That last rule is the one worth tattooing on your palm. If a user clicked Dark at noon and their OS flips to dark at sunset, do not silently downgrade their explicit choice to a default just because the two happen to agree tonight. Tomorrow morning the OS goes light, and now your user is squinting at a page they thought they had pinned.

When three states earn the pill

Verou is not banning the tri-state — she is banning it from the header and the footer. Per the source, an explicit three-option control is appropriate in a dedicated settings panel, where the user has arrived to make policy decisions and has the screen space for them. It is also appropriate when the color scheme genuinely branches on system state — say, a page that renders differently under the two OS modes and needs a distinct "let the OS drive" option to reach either. That case, the source notes, is rare.

Neither of those situations describes the button in your top-right chrome. That button exists to fix the appearance right now. Give the user the fix.

Verdict

The tri-state theme toggle is a beautifully specific example of the front-end's oldest bug — we accidentally ship the schema instead of the interaction. (I said the same thing about time-zone pickers last year. I will say it about locale switchers next year. It never stops being true.) Verou has done the field a favour by naming this one out loud, with receipts on both sides of the split and a storage recipe that is short enough to fit in a PR description.

Rip the System button out of your header. Move it to /settings if you love it that much. Score: a solid 8/10 for a piece that finally makes the two-state camp defensible in a design review.

Source: Lea Verou (lea.verou.me)

Related
Web platform

Lea Verou argues polyfills are a net positive for the web platform

In a new post responding to a WHATWG discussion, Lea Verou pushes back on the framing that polyfilling is harmful, and defends it as the mechanism that lets browsers ship first.

July 15, 2026
Web platform

Your site does not need to function the same on every platform

Bramus argues authors have swapped one cross-browser obsession for a new one — that sites must behave identically across mouse, touch, keyboard and Vision Pro. The piece reframes input-specific affordances as progressive enhancement on the input axis.

June 25, 2026
CSS & layout

::highlight() with a text-shadow safety net

The CSS Custom Highlight API lets you paint arbitrary text ranges from JavaScript, no wrapper spans required. A text-shadow fallback keeps the effect intact when the API is not there.

August 6, 2026