israel.olaleye
All posts

March 2, 2026

Design Tokens That Scale with Tailwind CSS v4

csstailwinddesign-systems

Tailwind CSS v4 moved configuration into CSS itself, and that changes how you should think about design tokens. Instead of a JavaScript config file, your tokens live where they belong — in the cascade.

Semantic tokens over raw values

The mistake most teams make is exposing raw palette values (zinc-100, amber-600) directly to components. Instead, define semantic tokens and map them per theme:

:root {
  --background: #fdfdfc;
  --foreground: #18181b;
  --accent: #b45309;
}
 
.dark {
  --background: #0a0a0a;
  --foreground: #f4f4f5;
  --accent: #fbbf24;
}
 
@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-accent: var(--accent);
}

Now bg-background, text-foreground, and text-accent work everywhere — and dark mode is a single class toggle, not a dark: variant sprinkled across every component.

Custom variants for class-based dark mode

If you drive dark mode with a class (via next-themes or similar), tell Tailwind about it:

@custom-variant dark (&:where(.dark, .dark *));

This keeps dark: variants working for the rare cases where a semantic token isn't enough.

The rule of thumb

If you find yourself writing the same dark: override twice, it should be a semantic token. Components should describe roles — surface, border, muted — never colors.