color theory

Three State Toggle

A three state toggle is the control that surfaces system, light, and dark as first class choices so users never duplicate their OS preference inside your app. It reads the prefers color scheme query when set to system, applies the matching primitive values from your parallel token map, and stays silent in localStorage until the user picks an override. The system state becomes the default for most people because it respects macOS Appearance settings, Windows dark mode schedules, or Android dark theme timers without extra clicks. This toggle sits at the end of a real dark mode build that includes four tier surfaces, desaturated accents, and halo free text all authored in OKLCH. The UI can live in a top bar menu, command palette, or settings drawer but it always shows three clear options with icons that match native system controls. Implementation demands a blocking script in the head tag that checks localStorage then matchMedia before any CSS renders so the correct theme paints on first load. Anything less turns your carefully crafted dark palette into a flickering disappointment.

A three state toggle is not the binary light or dark switch that still ships in most products in 2024. Those two state versions generate support tickets the moment a user changes their laptop from light to dark at sunset and watches your app stay stuck in the wrong mode. It is not an auto only dropdown with vague labels or a setting buried in an account page. It is not the useEffect hook solution that waits for React hydration and flashes pure white text on a near black background. It is not an afterthought added six months after your light mode palette ships. Teams that treat the toggle as cosmetic instead of structural inevitably build theme inversion instead of a parallel system and then wonder why their dark mode looks like garbage at 2 a.m. The toggle must own the connection between user intent and your two layer token architecture or the whole exercise collapses.

Linear nailed this in their 2022 command bar refresh. The toggle appears with a sun icon for light, moon for dark, and a half split screen for system. Select system and it follows your macOS automatic schedule, swapping from oklch background 0.10 warm charcoal to surface elevated 0.18 without a single border or shadow. Their purple accent lifts from 0.62 to 0.72 lightness while dropping chroma so it pops at midnight without burning retinas. Vercel committed to a brutal binary surface model in 2023 yet still shipped the full three states in their dashboard menu because they knew developers switch machines constantly. GitHub added Dimmed as a third system default in 2021 after data showed cool blacks caused eye strain during late night code reviews. Arc browser layers per space hue tinting on top of the root three state control so each workspace can feel custom while the global preference still obeys the OS. Raycast ships the toggle in the menu bar with macOS native blur and it updates live when you flip your system setting. Stripe fixed their dashboard in late 2023 after multiple tickets from engineers whose new M3 laptops defaulted to dark while the Stripe UI stayed light until they hunted through settings. Figma finally matured their version in 2024 so the three states appear both in quick settings and account preferences with zero flicker thanks to the exact blocking script pattern. Each example works only because the underlying dark system uses deliberate lightness steps, warm tints, and calibrated accents instead of lazy inversion.

Ship the three state toggle the day you release a mature dark mode built on four surface tiers, desaturation rules, APCA contrast, and accent calibration. It belongs in every productivity app, design tool, code editor, documentation site, and developer dashboard where users cross between bright offices and dim bedrooms. The pattern cuts support volume because it treats the OS as the source of truth until the user explicitly overrides it. Pair it with semantic tokens that stay constant while primitives swap on the data theme attribute. The blocking script is non negotiable here. Without it your beautiful OKLCH map never reaches the user cleanly. Use the toggle when your team has maintained both palettes at equal fidelity and when your dark surfaces use warm 30 degree hues instead of dead vacuum black. Drop it when your dark mode is still an afterthought built with CSS filters or when one palette is visibly half finished. Mobile apps can sometimes hide the system option behind platform conventions but the settings screen must still expose all three for consistency. Never launch the control if your elevation model stops at two tiers because users will select dark, see flat grey porridge, and send screenshots.

A three state toggle turns theme preference from a daily support ticket into a set it once system citizen that stays out of the users way.

Related terms

Keep exploring