CSS variables or a Tailwind theme for design tokens?
Should design tokens live in CSS custom properties or a Tailwind theme? What each is good at, how Tailwind v4 blurs the line, and a setup that uses both.
This question usually gets argued as a matter of taste. It is more practical than that. Tokens have two jobs: to be the single source of every value, and to change when the theme changes. CSS custom properties do both natively. A Tailwind theme does the first and needs help with the second.
What each is good at
| CSS custom properties | Tailwind theme | |
|---|---|---|
| Works without a build step | Yes | No |
| Changes at runtime (themes) | Yes | Only through variables |
| Usable in any stylesheet or inline style | Yes | Through generated classes |
| Autocomplete and constraint in markup | No | Yes |
| Readable by a person or model in one file | Yes | Yes |
The setup that gets both
Define tokens as custom properties, then have Tailwind read them. In Tailwind v4 that is an @theme block in your CSS; in v3, the theme in tailwind.config maps names to var(--token).
/* tokens.css: the source of truth */
:root {
--paper: oklch(97% 0.012 85);
--ink: oklch(22% 0.02 260);
--action: oklch(80% 0.15 75);
}
/* Tailwind v4 */
@theme inline {
--color-paper: var(--paper);
--color-ink: var(--ink);
--color-action: var(--action);
}Now bg-action works in markup, var(--action) works in plain CSS, and a dark theme redefines three variables instead of adding dark: to every class.
Where teams get it wrong
- Raw values in the Tailwind config and different raw values in CSS. Two sources of truth drift.
- Arbitrary values in markup, like
bg-[#f5a623]. Each one is a token that escaped the system. - Colour names instead of jobs.
bg-amber-400says what it looks like;bg-actionsays where it may go.
The last point matters more when Claude is writing the markup: a named job is a constraint it can follow, a colour name is not. See dark mode design tokens for how the job names are chosen.
Our design skills ship tokens this way, as CSS custom properties that work in plain CSS, React, Vue, Svelte, Astro or Tailwind. Warm Storefront UI, the system this site is built in, has its whole token file in OKLCH: a warm paper and charcoal ramp, five pastels, type, space and motion.
Questions
Should design tokens be CSS variables or Tailwind config?
Define them as CSS custom properties, then point Tailwind at those variables if you use Tailwind. The variables work everywhere, including outside Tailwind, and theme switching becomes a change of variable values.
Does Tailwind v4 use CSS variables?
Yes. Tailwind v4 configures the theme in CSS with an @theme block, and the theme values are exposed as CSS custom properties, so the two approaches now meet in the middle.
How do I do dark mode with CSS variables?
Redefine the semantic variables under a dark selector or a prefers-color-scheme media query. Components that use the variables change with no extra classes.