How to Ship CSS Themes Without Specificity Wars

Theme systems collapse when every new style requires a stronger selector. It feels like patching leaks in a pipe. The fix is to move from selector power to token ownership.

1) Expose design tokens as CSS variables

:root {
  --bg: #0e1116;
  --text: #f4f6f8;
  --accent: #20b2aa;
}

2) Scope themes by attribute, not nested selectors

[data-theme="light"] {
  --bg: #ffffff;
  --text: #111827;
  --accent: #0ea5e9;
}

3) Keep components consuming tokens only

.card {
  background: var(--bg);
  color: var(--text);
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
}

Failure pattern

  • Component files redefining color constants directly.
  • Theme overrides chained with long selector paths.
  • No visual regression checks when theme tokens change.

What to verify

  • Component CSS has no hard-coded theme colors.
  • Switching theme flips tokens, not selector precedence.
  • Theme snapshots pass for key layouts.

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.