Choosing Your Dark Mode Colors
How to actually choose your dark-theme colors, not just how to switch to them.
The gap most guides skip
Plenty of tutorials explain how to implement a dark mode toggle. Very few explain how to actually choose the dark-theme colors themselves, beyond "make it dark." Here's a practical framework, using this site's own light and dark token set as a real, working example rather than a hypothetical.
Don't just invert lightness
Mechanically flipping a light theme's lightness values (swap white for black, keep everything else the same) tends to produce muddy, low-saturation dark themes — colors that read as vivid against white often go flat and grayish against black. The fix is to adjust lightness and slightly reduce saturation deliberately, rather than running a pure inversion.
Preserve brand hue
Your dark-theme accent should still read as the same brand color, not a different one. This site's accent shifts from a deepened teal (#0891B2) in light mode to a bright cyan (#00FFFF) in dark mode — same hue family, adjusted lightness and saturation for each background, not a hue swap.
Check contrast in both themes independently
A text/background pairing that clears WCAG AA in light mode is not guaranteed to clear it in dark mode, and vice versa — they're two separate color pairs, not one pair with the lightness flipped. Check both with the contrast checker rather than assuming one pass covers both themes.
Surface elevation works backwards in dark mode
In a light theme, "higher" surfaces like cards and modals typically get subtle drop shadows to read as elevated above the page. In a dark theme, shadows barely show up against an already-dark background — elevated surfaces usually read better with a slightly lighter background than the base, the opposite direction from the light-theme convention.
Put it into practice
Once you've worked out your light and dark values for each token using the framework above, build the actual light-dark() CSS pairs on the light-dark() generator. That page now includes an "Auto-suggest dark value" button on every token row, implementing the exact framework above as working code — it isn't just prose here anymore, it computes a starting suggestion by checking whether your light value looks like a background, a text color, or an accent, then adjusts lightness and saturation accordingly rather than mechanically inverting. Treat its suggestion as a starting point, not a final answer — you can still override any value manually. If your token set is growing past a handful of names, Design Tokens Explained covers the primitive-vs-semantic naming structure that keeps it organized as it scales.
Frequently Asked Questions
Should dark mode just be light mode with inverted colors?
No — a pure inversion tends to produce muddy, low-saturation results. Adjust lightness and saturation deliberately for each theme instead of mechanically flipping values.
How do I keep my brand color consistent across light and dark themes?
Stay within the same hue family and adjust lightness/saturation per theme, rather than switching to a different hue for dark mode.
Do I need separate contrast checks for light and dark mode?
Yes. A pairing passing WCAG in one theme doesn't guarantee it passes in the other — check both independently.
What's "surface elevation" in a dark theme?
It's how a design signals that one surface (like a card or modal) sits "above" another. In light themes, shadows do this job; in dark themes, a slightly lighter background color usually reads better than a shadow.
Sources
- WCAG contrast requirement applying independently to each theme — W3C Web Content Accessibility Guidelines 2.2
- The lightness-inversion, hue-preservation, and surface-elevation principles aboveThis page's own synthesized practical framework, illustrated with this project's own token set as a worked example — not a claim of external authority.