skip to content

In a design system, why should dark mode remap semantic token values instead of adding dark-specific styles to every component?

level: juniorimportance: must knowfreq 60%

answer

  1. components know names, not colors
  2. a mode is another value set
  3. one mapping, every component
  4. N components times M modes
  5. high contrast is just one more

basics

~20 s

Components reference semantic names such as surface, primary text and border, and a mode supplies different values for those names. One mapping switches every component at once, while per-component dark styles multiply the work and drift apart.

solid answer

~40 s

In a recruiting app, the candidate card, pipeline column and stage chip reference **semantic tokens** like `surface.default`, `text.primary`, `border.subtle` and `status.rejected.surface`, never raw colors. A **mode** is a second value set for the same names: in dark mode `surface.default` resolves to a dark grey and `text.primary` to near-white. Flip the dark-mode toggle and every component changes at once, and a component built next month is dark-ready by default. Writing dark styles into each component instead means every component needs dark work, new components forget it, the same dark surface gets three slightly different values, and the effort grows with components times modes. A high-contrast mode then becomes one more value set rather than another pass over the library — although every mode still has to be verified.

go deeper

for a junior

Recall that components reference semantic roles such as surface and primary text, and that a mode is another set of values for those same roles.

for a middle

Explain why remapping scales where per-component dark styles do not, and what the remap leaves unsolved: images, shadows, uploaded content and verification.

for a senior

Show how you would guarantee that every semantic token has a value in every mode and that status meanings and contrast hold in each one.

for a principal

Weigh how many modes the system should support, since each one adds a full value set to design, check and maintain.

## Modes are value sets, not restyles A **design token** is a named design decision; a **semantic token** names a *role* — the default surface, the primary text, a subtle border, the surface of a rejected-candidate chip — rather than a specific color. In a recruiting app's applicant-tracking screens, components reference only those roles: - the **candidate card** uses `surface.raised`, `text.primary`, `text.secondary` and `border.subtle`; - the **pipeline column** uses `surface.sunken` for its background; - the **stage chip** uses a `status.*` surface and text pair for applied, interviewing, offer and rejected. A **mode** — light, dark, high contrast — is a separate **value set** for those same names. The components never change; the mapping underneath them does. The draft Design Tokens Community Group format does not itself define modes, so systems commonly express each mode as its own set of values keyed by the same token names. ## What the remap looks like | Semantic token | Light mode | Dark mode | High-contrast light | |---|---|---|---| | `surface.default` | White | Very dark grey | White | | `surface.raised` | White with a soft shadow | A lighter grey than the default surface | White with a strong border | | `text.primary` | Near-black | Near-white | Black | | `border.subtle` | Light grey | Mid grey | Dark grey | | `status.rejected.text` | Dark red | Light red | Very dark red | Notice that the mapping is not a mechanical inversion: a raised surface in dark mode is usually *lighter* rather than shadowed, and status colors shift so they still read against the new surface. Designing those values is palette work; the design-system decision is that the values live in the mapping, not in components. ## Why per-component dark styles fail - **Work multiplies.** Every one of forty components needs a dark variant, and a third mode means another forty. - **New components forget.** The interview-scheduling panel shipped last sprint has no dark styles, so it renders as a bright white block in a dark app. - **Values drift.** Three teams choose three slightly different dark greys for what should be the same surface. - **Fixes scatter.** A contrast fix for dark text must be found and applied in every component instead of once in the mapping. ## What the remap does not solve by itself - **Images and illustrations.** An empty-pipeline illustration drawn for white backgrounds may need a dark version or a transparent background. - **Logos and uploaded content.** A candidate's uploaded résumé preview or a client company's logo arrives with its own colors. - **Shadows and elevation.** Shadows barely show on dark surfaces, so elevation needs its own mode-aware values. - **Verification.** A remap makes a component dark-*ready*, not dark-*correct*; each mode's color pairs still need contrast checks. ## Making it work in practice 1. Give components access to **semantic names only**, so nothing can bypass the mapping. 2. Define **every mode for every semantic token** — a token missing from the dark set silently shows its light value. 3. Keep **status meanings stable across modes**: rejected still reads as rejected, with contrast rechecked against the new surface — under WCAG 2.2 Level AA, 4.5:1 for normal text and 3:1 for component and state indicators. 4. Treat a new mode as **a new value set plus verification**, never as a pass over the component library. In an interview, the short version is: components bind to roles, modes remap roles to values, and that turns a per-component chore into one mapping per mode.

  • What happens if the dark value set is missing a mapping for one semantic token?
    Components using that token keep resolving to its light value, so a pale surface or dark text appears inside an otherwise dark screen, often with unreadable contrast. It fails silently rather than breaking a build. A build check that every semantic token has a value in every mode catches it before release.
  • Why is a dark mode not simply an inversion of the light colors?
    Inversion gets relationships wrong: raised surfaces in dark mode are usually lighter rather than shadowed, saturated brand and status colors glare on dark backgrounds and need adjusted values, and pure black and white create harsh contrast. The mapping is designed per role and rechecked for contrast in each mode, which is why modes are a value-set design task rather than a formula.

It is like a theatre that relights the same stage set for a night scene. The furniture and the actors' blocking stay exactly where they are; only the lighting plot changes, and one plot relights everything on stage at once.

saying these in an interview costs you the question

  • Dark mode means giving each component its own dark-specific styles.
  • A dark palette can be generated by inverting every light color.
  • Once tokens are remapped, no mode needs its own contrast checks.
  • Components should reference raw color values and let the mode swap them.
  • Adding a high-contrast mode requires rewriting every component again.