PaletteGen

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.

1

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.

2

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.

Light: #0891B2
Dark: #00FFFF
3

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.

4

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.

Light: shadow signals elevation
↔
Dark: lighter surface signals elevation

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. The same lightness/saturation adjustment principle applies to gradient stops too, not just solid colors — see building dark-mode-aware gradients for the reliable way to carry a token pair into a gradient declaration.

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

By , PaletteGen

About this page

PaletteGen is built and maintained by Cedrick Reese. Formulas and technical claims on this page are checked against the primary sources listed above. Reviewed and updated: August 30, 2026. See the full About page for more.