Indonesia Singapore ไทย Pilipinas Việt Nam Malaysia မြန်မာ ລາວ
← Back to Blog

Dark Mode Toggle Wars: Two States vs Three Explained

Defaulting to a binary dark mode toggle silently overrides system preferences, breaking user trust and corrupting behavioural signal at the session level.

Two figures pulling opposite ends of a light switch in a dark room
Illustrated by Mikael Venne

Two states or three? The dark mode toggle debate reveals how a single UI decision ripples into UX trust, signal loss, and cross-platform parity for SEA brands.

A UI toggle with three options sounds like committee design. Turns out it might be the only honest one.

This week, two respected voices in the frontend community landed on opposite sides of a deceptively small question: should a dark mode toggle offer two states (light/dark) or three (light/dark/system)? The disagreement is worth unpacking — not because toggle design is exciting, but because the reasoning on each side exposes something real about how we think about user intent, system trust, and the hidden costs of seemingly simple UX decisions.

The Binary Case: Simplicity as Respect

CSS-Tricks, citing Lea Verou’s position, argues that two states are enough. The logic is clean: users who land on your site have already set a system preference. A toggle that mirrors that preference back at them as a third option — “System” — is just noise dressed up as nuance. From this angle, the tri-state toggle is UX theatre: it signals sophistication while actually adding cognitive load for a decision most users made once, in their OS settings, and forgot about.

There’s a practical implementation argument here too. A binary toggle is stateless in the simplest sense — you store one bit of preference, flip it, done. For teams building on Webflow or lightweight CMS stacks common across Southeast Asia’s mid-market brand landscape, that simplicity has real value. Less JavaScript, fewer edge cases, nothing to audit when something breaks at 11pm before a campaign goes live.

The Tri-State Case: Honouring the OS Contract

Bramus makes the stronger technical argument. The core issue isn’t design philosophy — it’s what happens when you only offer binary. If a user visits your site in dark mode because their OS is set to dark, and your toggle defaults to “Dark” without storing a “System” state, you’ve severed the connection between their site experience and their OS preference. Now, when they switch to light mode at sunrise because their phone does it automatically, your site doesn’t follow. You’ve quietly overridden their intent with a stored value they may not remember setting.

That’s not a minor UX quibble. It’s a silent contract breach. The “System” option in a tri-state toggle isn’t a third preference — it’s the escape hatch back to the user’s actual preference. Without it, a binary toggle that persists its state is, technically, always overriding the OS. The two-state toggle only works cleanly if you never persist the preference, which defeats the point of having a toggle at all.


What This Means for Southeast Asian Deployments

In the Southeast Asian context, this debate has an extra layer. Mobile-first usage is the default, not the exception — and Android’s adaptive brightness and scheduled dark mode are deeply embedded in how users in markets like Thailand, Indonesia, and Vietnam interact with their devices. A user commuting on BTS or MRT in Bangkok, phone set to auto dark mode, hitting a brand site that’s locked to a stale stored preference, is a small but real friction point. Multiply that across high-frequency mobile sessions and it starts to register in bounce metrics.

There’s also a signal hygiene angle worth flagging. Teams running behavioural analytics — session recordings, heatmaps, scroll depth — should be aware that a binary toggle that overrides system preferences can skew device-environment correlations. If your analytics assume dark mode sessions reflect genuine user preference rather than a stale localStorage value, your cohort segmentation gets fuzzy. It’s a small leak, but it compounds.

For implementation, the tri-state approach doesn’t require a dramatic lift. The pattern Bramus outlines relies on a prefers-color-scheme media query as the baseline, a localStorage value for explicit overrides, and a null state that signals “follow the OS.” The JavaScript payload is modest. The harder work is in the toggle UI itself — communicating three states without making the control feel like a settings panel. Icon-only toggles (sun / system / moon) with clear ARIA labels are the most defensible pattern, particularly for multilingual interfaces where label text length varies significantly across Thai, Bahasa, Vietnamese, and English.

Picking a Side Without Being Precious About It

Here’s where I land: the binary toggle is fine if you never persist the preference. The moment you store it — and most implementations do — you owe users the “System” option. Anything else is a quiet usurpation of a preference they set somewhere else, in an interface they trust more than yours.

The practical recommendation for most brand teams: implement tri-state, default to “System” on first visit, and only write to localStorage on an explicit user action. That approach respects the OS contract, keeps your behavioural signal clean, and adds roughly zero perceptible complexity for the end user. The toggle looks the same. The backend just handles one more state — and handles it correctly.

Key Takeaways

  • A binary toggle that persists state silently overrides OS preferences; tri-state with a “System” default is the only technically honest implementation.
  • In mobile-first markets like Indonesia and Thailand, scheduled dark mode is a mainstream OS behaviour — your toggle should follow it, not fight it.
  • Defaulting to localStorage without a null “System” state introduces cohort segmentation noise in behavioural analytics tools.

The deeper question this debate surfaces: how often do we treat “simplicity” as a design virtue when what we’re actually doing is offloading complexity onto the user — or onto the data? A toggle with three states isn’t complicated. A toggle with two states that quietly overrides system preferences, corrupts session data, and frustrates users who can’t figure out why their phone’s dark mode isn’t working on your site — that’s the complicated one.


At grzzly, we audit exactly these kinds of implementation decisions — the ones that look small in a Figma file but accumulate into real friction and signal loss at scale. If your team is building or refreshing a design system for Southeast Asian markets and wants a second opinion on how your UI choices interact with tracking, performance, and cross-platform behaviour, we’re worth talking to. Let’s talk

Stormy Grizzly

Written by

Stormy Grizzly

Stress-testing email open rates, dissecting Apple's Mail Privacy Protection, and auditing the JavaScript payloads quietly leaking signal. The analyst who reads the spec, not just the summary.

Enjoyed this?
Let's talk.

Start a conversation