Design systems

Dark mode without a second design system

A dark theme is not a colour inversion. It is a second mapping of the same tokens. Here is how to avoid two products and two sets of components.

Key points

  • Dark mode is a token mapping, not a duplicated palette.
  • Contrast, images and charts are the first three things to break.
  • If you need two sets of components for two themes, the problem is in the tokens.

Dark mode looks like a colour change. It is really a test of the design system. If your components know the semantic roles, the theme switches by itself. If they know raw colours, you have to duplicate everything.

Tokens, not colours

A component should not know that text is #f5f5f5. It should know that text is --ui-fg. The dark theme changes the token value, not the component. A card with a hard-coded background: #111 stays black in light mode.

RoleDarkLight
CanvasNear blackWarm white
TextWhiteNear black
BorderDark greyLight grey
AccentBrighter blueDeeper blue

Contrast: the test that fails first

Secondary text is the first thing to vanish. Check 4.5:1 for body copy, 3:1 for large text and for the focus ring. Do not reuse the same grey for text and borders: they are different roles.

Images and charts

A bright image on a dark background can glare. Give it a border or a surface behind it. In charts, do not invert the palette: change the lightness of the series and keep the contrast between them.

The three-screen test

  • Open the same page in both themes and compare the secondary text.
  • Tab through with the keyboard: is the focus ring visible in both?
  • Open a dialog and a toast: are the surfaces distinguishable?
  • Look at an image that has both light and dark areas.
  • Check the system theme, not just the toggle.

The full token specification lives in EneUI. Start from a semantic layer, not from a second palette.

Frequently asked questions

Is dark mode just inverting the colours?

No. It is a mapping of semantic roles: canvas, surface, text, border, accent. Inversion produces the wrong contrast.

Do I have to duplicate the components?

No. When components use semantic tokens, the theme changes with no duplication at all.

How do I pick the background black?

A near-black reads better than #000 and leaves room for hierarchy. Avoid pure black on large surfaces.

Do images need special handling?

Yes. Give images that glare a border, a surface behind them, or a version with different luminance.

How do I test the system theme?

Change the operating system setting, not just the site toggle, and check that the preference is read on first paint.