Dark mode is not an invert button. Done well, it is a second, parallel design pass: near-black surfaces with a lightness ladder, desaturated accents, contrast measured against real backgrounds, and shadows replaced by light. This guide is the complete method.
Every interface eventually meets a user in a dark room, and the difference between a dark mode that feels designed and one that feels inverted is a handful of decisions. Pure black backgrounds that vibrate against white text, light-mode brand colours glaring at full saturation, shadows that vanish because shadows need light to fall on — these are the signatures of a theme generated by a filter rather than by a person.
The encouraging news is that the work is mostly systematic once you accept the core principle: dark mode is a fresh assignment of your existing colour ramps to new roles, with a few physical adjustments — saturation comes down, elevation is expressed in lightness rather than shadow, and pure black is reserved for the specific job of OLED power saving and true video blacks.
Key takeaways
- Build surfaces as a lightness ladder from a near-black base (around #121212 to #1E1E28), one step lighter per elevation level.
- White text on pure black measures 21:1 and still reads worse than slightly softened pairs — aim high on contrast, avoid the extremes in both slots.
- Reduce saturation of brand and semantic colours for dark surfaces; the same chroma reads more intense against darkness, a phenomenon called the Hunt effect in reverse.
- Elevation in the dark is lightness, not shadow: higher surfaces are lighter surfaces.
Surfaces: the ladder that replaces shadow
In light mode, a card sits on a page and a shadow says so. In the dark, shadows are nearly invisible — there is no darker dark to fall into — so depth must be carried by the surfaces themselves: each layer up is a step lighter than the one beneath it. A typical ladder: page background #101014, raised card #17171F, popover #1E1E28, modal #262633 or one step beyond, each roughly 4–6% lighter than its parent. The steps should be close enough to feel like one material and far enough that boundaries read instantly, including for users with low vision.
Pure black (#000000) is a special case, not a default. It earns its place on OLED phones where black pixels literally switch off and save power, and behind video. Everywhere else it creates the harshest possible contrast against text and makes any coloured element appear to float in a void — sometimes wanted for media, rarely for reading. Most mature dark themes (Material Design's baseline is #121212) start a step above black, and the softness is the point.
Text and contrast in the dark
Contrast requirements do not change with theme — 4.5:1 for body text at AA — but the pairs that satisfy them do, because they must be measured against your actual dark surfaces, not against black. White (#FFFFFF) on #121212 gives roughly 18:1: compliant, and often uncomfortably glary for long reading, especially for users with astigmatism, who perceive bright text on black as halated and blurred. Seasoned dark themes soften the text a notch — an off-white around #E8E6E3 on the surface ladder keeps ratios in the 12–15:1 range, compliant several times over and markedly easier on the eye.
Secondary text is where dark modes fail audits: the mid-grey that passed on white is placed on #121212 and lands near 3:1. Recompute every text token against its dark background, and remember the direction of the walk has reversed — to gain contrast in the dark, lighten the text or darken the surface behind it, never the instinctive light-mode moves.
Colour: turn the chroma down, the lightness up
Colours appear more saturated against dark backgrounds, so light-mode accent values transplanted directly tend to shout. The standard correction is to take the dark-mode variant of each colour from a lighter, slightly less chromatic step of its ramp: a brand orange that sits at OKLCH lightness 0.60 with chroma 0.18 on white might take lightness 0.72 with chroma 0.14 on dark surfaces, and hold its perceived identity far better than the identical value would. Semantic colours follow the same rule — error reds and success greens in dark themes are pastel-shifted relatives of their light-mode selves, tested at 4.5:1 against dark surfaces before they are allowed near text.
Charts and data visualisation need their own pass: series chosen for light backgrounds can collapse together in the dark, particularly blues and purples. Reorder or re-pick series so adjacent data differs in lightness as well as hue — a rule that also serves colour-blind readers, making it doubly non-negotiable.
Images, borders and the finishing checks
- Photographs: full-brightness photos glare in a dark UI. A slight dim (a brightness filter around 0.9, or a design-level overlay) is common in mature products; apply it as a theme rule, not per-image edits.
- Borders: hairlines that separated surfaces in light mode disappear in the dark; rely on the surface ladder first, and where a border is needed, use a light line at low opacity rather than a dark one.
- Logos and icons: any mark with a dark variant problem — dark linework that vanishes on dark surfaces — needs a prepared dark asset or a light plate behind it. Silently inverting artwork is how brands end up negative by accident.
- Theme colour and chrome: set the browser theme colour per scheme so mobile chrome matches the theme, and respect
prefers-color-schemeby default while still offering a manual override — users' eyes outrank their OS setting. - Test at night, literally: review the dark theme in a dark room at low screen brightness. Halation, glare and vanishing borders are environmental bugs; a bright office hides all of them.
Mistakes and pitfalls
Shipping dark mode as inverted light mode
CSS filters that invert a page also invert photographs, logos and shadows into surreal negatives. There is no shortcut around assigning tokens per theme; the filter demos well and ships badly.
Pure black plus pure white 'for maximum contrast'
Maximum ratio is not maximum readability. The 21:1 extreme increases glare and halation for many readers; professional dark themes deliberately land their main text pair in the low-to-mid teens.
Forgetting the third-party surfaces
Embedded widgets, payment iframes, maps and email clients keep their own themes. A dark page with a blazing white checkout iframe is a dark mode that fails exactly where trust matters most — inventory your embeds and theme or frame them deliberately.
Common questions
Should dark mode be the default?
Default to the user's system preference — that is what prefers-color-scheme reports — and persist any manual override they choose. Forcing either theme against the system setting reads as the site not listening.
Does dark mode really save battery?
On OLED screens, yes, measurably — black pixels draw no power, and near-black draws little; savings scale with how dark and how much of the screen the theme covers. On LCD screens, brightness, not theme, dominates, and the benefit is comfort rather than energy.
How do I store the user's theme choice?
A line of local storage and a tiny script that applies it before first paint (to avoid a flash of the wrong theme) is the whole mechanism on a static site. The preference is the user's, kept on their device — no account needed.
To wrap up
Dark mode done properly is an act of respect performed twice: once for the physics of light — ladders instead of shadows, softened extremes, chroma turned down — and once for the reader, whose contrast is measured, whose system preference is honoured, and whose dark room is treated as a design context rather than an edge case.
Try it yourself
The colour studio helps you build the dark variants of your palette: read any light-mode colour back in OKLCH, walk its lightness up and chroma down, and check the result against your dark surfaces.