Dark mode is a design decision, not a CSS variable

Why most dark modes look broken, and the contrast, depth, and color rules that separate good dark UI from inverted light UI.

Most dark modes are the light mode with the colors inverted, and they look it. True dark UI isn't a CSS toggle, it's a separate design language that obeys different rules about contrast, depth, and color.

Pure black is wrong

Don't use #000 as your background. Use a near-black with slight blue or warm tint, #0A0A0F, #0F0E12, #111111. The reason is twofold: pure black on OLED screens produces halation around bright text, making it harder to read, and pure black removes your ability to create depth with darker surfaces.

Your background is a darkish surface, not the bottom. Cards sit slightly above it. Modals sit further above. Each surface is brighter, not darker, than the one beneath.

Contrast is harder than it looks

In light mode, contrast is easy, dark text on white background. In dark mode, white text on near-black is *too much* contrast. The text vibrates. The solution is to dim the text: not pure white, but #E5E5E5 or thereabouts for body, with #FFFFFF reserved for headings and emphasis.

  • Body text: 85–92% white. Reduces vibration on dark backgrounds.
  • Headings: 95–100% white. Restores hierarchy.
  • Secondary text: 55–65% white. Don't drop below 55%, it crosses into illegible.
  • Borders: 10–15% white. Subtle structure without harsh lines.

Color saturation drops

Saturated colors that look great on white look poisonous on dark. Pure red, pure blue, pure green, all of them need to be desaturated for dark mode. Brand red #FF0000 becomes something like #FF6B6B; brand blue becomes a lighter, less-saturated #6FA8DC.

Build your color tokens with light- and dark-mode variants from the start. Don't try to compute one from the other at runtime, they're different palettes.

Shadows and depth

Shadows that work on white backgrounds disappear on near-black. In dark mode, depth comes from lighter surfaces (cards brighter than background) and from subtle borders, not from drop shadows.

If you must use shadows, they should be barely visible, opacity 30–50%, large blur, color matched to the background hue.

Defaulting

Default to the user's system preference, with a toggle. Don't default to dark globally, older users and bright-room readers still prefer light. Respect prefers-color-scheme and persist the user's explicit choice if they toggle.

Build one

The directory's design foundations entries cover the typography and color tokens that need dark/light variants. Pair with typography, dark mode shifts what type weights and sizes read best, so don't assume your light-mode type scale carries over unchanged.

Keep reading