Tailwind v4 Design Tokens: A Practical Guide
Tailwind v4 replaces the JavaScript config with CSS-native custom properties. Here is how we structure our design system on top of it.
Tailwind v4 is a ground-up rewrite. The biggest change for design systems: tokens now live in CSS, not in a JavaScript config file.
Before: tailwind.config.ts
extend: {
colors: {
brand: { DEFAULT: '#22C55E', hover: '#16A34A' }
}
}
After: globals.css
@theme {
--color-brand: #22C55E;
--color-brand-hover: #16A34A;
}
The tokens compile to CSS custom properties, which means you can read and override them in plain CSS, in media queries, or with JavaScript — no Tailwind config needed.
Our token structure
We group tokens into four layers:
- Primitives — raw hex values, never used directly in components
- Semantic —
--color-fg,--color-bg-elevated, mapped from primitives - Component — button variants, card shadows, etc.
- Motion — easing curves, durations
This separation means changing the brand color updates every semantic token that references it — one change, site-wide.
Dark mode
We force dark mode via next-themes with forcedTheme="dark". All semantic tokens are defined for dark only. If we ever add a light mode, we define the same token names inside a [data-theme="light"] block — zero component changes required.