CodePark/ questions / Design systems & theming
START PRACTICINGPRACTICE
Frontend question · Frontend system design › Design systems & theming

How do you implement theming (light, dark and brand themes) in a design system? Why are CSS custom properties a good fit?

:root { --color-bg: #fff; --color-text: #111; }
[data-theme="dark"] { --color-bg: #111; --color-text: #eee; }
[data-brand="acme"] { --color-accent: #e4572e; }
.card { background: var(--color-bg); color: var(--color-text); }

Make components use semantic CSS variables, and make each theme a different set of values for those variables. CSS custom properties (CSS variables) fit this well.

Components reference variables such as --color-bg and --color-text, never raw colours. A theme sets those values under an attribute on html or on a subtree. To switch theme, you change one attribute. The browser recomputes the styles without React re-rendering anything.

CSS variables fit because:

  • They cascade, so one section can use a different theme by scoping the attribute.
  • They work at runtime.
  • They need no JavaScript to read.
  • They work with server rendering.

CSS-in-JS theme providers that pass values through context are worse here. They re-render consumers, and they are harder to use with streaming SSR.

Avoid a flash of the wrong theme on load. There are two ways:

  • Read the saved preference or prefers-color-scheme in a tiny inline script, before the first paint.
  • Or set the attribute on the server, from a cookie.

Dark themes need their own tuned semantic values, not inverted colours. Each theme must pass contrast checks.

  • Components use semantic variables only, never raw colours.
  • A theme is a set of variable values under an attribute.
  • Switching theme needs no re-render, and a subtree can have its own theme.
  • Avoid a flash of the wrong theme with an inline script or a server cookie.
  • Check the contrast of each theme.