Dark mode that doesn't hurt
Good dark themes aren't white themes with the colors flipped. Contrast, desaturation, and elevation done right.
A bad dark mode is a white site with the brightness inverted: pure #000 backgrounds, pure #fff text, and neon accents that scream at you. A good dark mode is a different design, tuned for a dim room.
Rule 1: Never pure black
Pure black next to pure white text is a contrast ratio of 21:1 — technically "accessible", practically uncomfortable. It causes halation on OLED screens and makes everything shimmer.
Use a very dark gray instead:
| Surface | Value |
|---|---|
| Page background | #0a0a0a → use #08080a |
| Raised cards | #101014 |
| Borders | rgba(255,255,255,0.08) |
Elevation in dark mode comes from lifting surfaces with lighter grays, not shadows.
Rule 2: Desaturate your accents
A #7c3aed purple on white feels calm. The same purple on near-black can feel like a laser. Knock the saturation down or bump the lightness a notch in dark mode — keep the hue, soften the volume.
Rule 3: Respect prefers-color-scheme, offer a choice
Default to the user's system setting, but let them override it. And store the choice so it survives a reload:
<ThemeProvider attribute="class" defaultTheme="system" enableSystem />
Rule 4: Don't flash
The classic dark-mode bug: the page loads light, then snaps dark. Prevent the flash-of-wrong-theme by setting the class on <html> before first paint — a small inline script or a provider that does it for you.
A quick checklist
- Backgrounds are dark grays, not
#000 - Text is off-white, not
#fff - Focus rings are clearly visible in both themes
- Images and shadows don't look broken in dark mode
- The toggle survives page loads
Dark mode is table stakes now. Do it properly and it's invisible; do it lazily and it's the first thing people complain about.