Dark mode is a first-class design challenge, not a CSS filter. Learn how to build adaptive color systems, manage shadows, and handle imagery for both themes.
A well-implemented dark mode is not simply white-on-black. Surface elevation, shadow direction, and color saturation all need rethinking from the ground up.
The Inversion Trap
The most common mistake teams make when adding dark mode is treating it as a visual inversion of the light theme. Run a filter: invert(1) on your entire UI and you will immediately see the problem — images look wrong, shadows disappear, and brand colors lose their meaning. Dark mode is a parallel design system, not a transformation of the existing one.
The good news is that if you already use a semantic color token system, the implementation work is far less than you think. The hard work is the design thinking that precedes it.
Semantic Tokens: The Foundation
Stop thinking in raw hex values and start thinking in roles. Instead of applying #131720 directly, name it color-surface-primary. In light mode it maps to white; in dark mode it maps to a deep navy. Every component consumes the token, never the raw value.
- Surface tokens — background, card, overlay, elevated surfaces
- Content tokens — heading, body text, caption, disabled text
- Brand tokens — primary, secondary, accent (may shift saturation in dark mode)
- State tokens — success, warning, error, info (ensure contrast in both modes)
Elevation Without Shadows
In light mode, elevation is communicated through drop shadows — a box-shadow getting stronger as a surface rises. In dark mode, shadows become nearly invisible against dark backgrounds. The solution is to use surface lightness instead: higher surfaces get slightly lighter backgrounds.
- Base surface: darkest — e.g. #101418
- Card surface: slightly lighter — e.g. #1A1F26
- Floating elements: lighter still — e.g. #252B34
- Tooltip / popover: lightest surface — e.g. #2F3640
In dark interfaces, light is the material. Use it with the same intention you would use shadow in a light theme.
— Material Design 3 Guidelines, Google
Handling Photography and Illustrations
User-generated photos and brand illustrations rarely look right at full opacity on dark backgrounds. The standard fix is to reduce the opacity of images in dark mode — typically to 85–90% — which softens the contrast shock without washing out the image. For illustrations, maintain separate dark-mode variants where the palette has been intentionally redesigned.
Avoiding Common Pitfalls
Even teams with good color systems make the same mistakes repeatedly. The most impactful errors to avoid are using pure black (#000000) as a background, which causes a harsh contrast often called halation on OLED screens, and using the same brand colors across both modes without adjusting saturation.
Prefer a dark navy or very dark cool gray over pure black for your darkest surface — typically in the range of #0C1117 to #161B22. Pure black on OLED creates blooming around bright text and feels harsher than real-world dark environments.
Testing Across Contexts
Your dark mode must be validated in real usage conditions — not just on a calibrated design monitor. Test on a phone in a dark room, on a low-brightness laptop outdoors, and with OS-level accessibility options like increased contrast enabled. The prefers-contrast: more media query is your signal to further increase the difference between foreground and background tokens.
