Skip to content
Career & process

Dark mode is a design system

Dark mode is not a filter you flip — it is a second system with its own luminance logic, elevation language and colour roles. The differences that break naive implementations, and the token architecture that makes dark a first-class citizen.

Answers How do I keep a dark theme from looking like an inverted light theme?dark mode is a token decision, not a colour swap — the veil is designed for a dark ground from the start.

Intermediate2 min read (computed · recorded 11)updated 2026-09-10dark modethemingtokensdesign systems

Why the filter fails

The naive dark mode inverts the colours and calls it done. It fails in the same four places every time: shadows (a dark surface cannot cast a convincing dark shadow — elevation must be expressed as *lighter* surfaces, not deeper ones), saturation (brand colours that glow on light become neon on dark), text contrast (the 'muted grey' that passed on white dies on near-black), and imagery (photos shot for light backgrounds arrive as glowing rectangles). Dark mode is not the same design with a colour swap; it is a different physical metaphor — light on dark is a screen emitting light, and the system must be designed for that physics.

elevation-flips.md
Light mode:  surface rises = DARKER + shadow
             --surface-1: #fff    --surface-2: #f6f6f4
             shadow: 0 2px 8px rgb(0 0 0 / .10)

Dark mode:   surface rises = LIGHTER + border
             --surface-1: #121318  --surface-2: #1a1c23
             border: 1px solid rgb(255 255 255 / .08)
             (shadow becomes almost useless; the border
              does the separating)

The token architecture that survives

  • Role tokens, not colour tokens: --surface-1, --text-primary, --text-dim, --border — components reference roles, and the theme provides the values; a component that references --ink directly has already broken in one of the two themes.
  • Dark is a luminance problem first: text hierarchy in dark mode comes from luminance steps, not opacity alone — 'dim' text needs to be a lighter grey with a *defined* ratio, because opacity-on-dark compounds differently than opacity-on-light.
  • Saturation discipline: the dark palette desaturates brand colours 15–25% — the same hue at full saturation on near-black vibrates; the dark theme's brand is the brand, tuned for its medium.
  • Semantic tokens carry the system: --danger, --success, --focus-ring are the same roles in both themes with different values — the component never knows which theme it is in, which is exactly the point.

The second-system checklist

A dark mode done as a system ships with its own audit: contrast pairs recomputed (not inherited — a pair that passed on light can fail on dark), imagery strategy decided (dark-mode imagery, or a scrim treatment for light photos), reduced-motion respected in both themes, and the switch itself saved and system-aware (prefers-color-scheme as the default, manual override on top). The tell of a real system: a designer can theme a new component in both modes from the token sheet alone, without asking what the dark 'version' of a colour should be — because there is no dark version. There is one component and two themes.

Practise the lesson

Theory sticks when you ship it. These original Motif assets put this guide's lesson to work — open one and copy it into your own page.