For years, design token pipelines looked roughly the same: a JSON source of truth, a build tool like Style Dictionary, and platform-specific outputs — CSS custom properties for web, Swift constants for iOS, XML resources for Android. Tailwind v4 just lobbed a grenade at that entire middle layer. Your tokens are the CSS now, and the implications for design system teams are bigger than the changelog suggests.
The old pipeline, briefly
Most mature design systems settled on a three-tier architecture. You'd have raw values (#1a73e8), semantic aliases (color-primary), and component-level tokens (button-bg). These lived in JSON or YAML, got processed by Style Dictionary or Theo, and spat out platform artifacts. The web output was always CSS custom properties.
The problem wasn't the output — CSS variables are great. The problem was everything between the source file and the browser. Every token rename meant updating a JSON file, running a build, checking that the transform resolved correctly, and hoping your token naming convention didn't collide with some platform-specific override. For a team with one designer and two frontend devs, that overhead was absurd. For an enterprise team, it was "we need a dedicated tokens engineer" territory.
Style Dictionary earned its place. But if your system only targets web — and plenty of product teams only ship web — the build step was ceremony, not substance.
What Tailwind v4 actually changed
Tailwind v4 flipped the configuration model. Instead of tailwind.config.js, your design tokens live in CSS using @theme:
@theme {
--color-brand: oklch(0.65 0.24 265);
--color-surface: oklch(0.98 0.005 265);
--color-surface-raised: oklch(0.95 0.01 265);
--color-text: oklch(0.15 0.02 265);
--color-text-muted: oklch(0.45 0.03 265);
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2.5rem;
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--radius-lg: 1rem;
--font-body: "Inter Variable", system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
}
That's it. Those variables are simultaneously your Tailwind config and your runtime CSS custom properties. bg-brand works because --color-brand exists. p-md works because --spacing-md exists. No build transforms, no JSON parsing, no platform adapters. The token definition is the implementation.
Where this gets interesting for design systems
Here's what most "Tailwind v4 migration" posts skip: this CSS-first approach changes how your design system team collaborates, not just how your build runs.
Designers can read the source of truth. A tokens.json with nested namespaces and $value / $type annotations is legible only to the engineer who set it up. A CSS file with --color-brand: oklch(0.65 0.24 265) is legible to anyone who's opened a browser inspector. Your designer doesn't need to learn the W3C Design Token Format spec to contribute a token change — they open the CSS file, change a value, and see the result in the browser. The feedback loop shrinks from "file a ticket → engineer updates JSON → build runs → deploy preview" to "edit CSS → refresh."
Dark mode becomes a cascade concern, not a build concern. Instead of generating separate token sets per theme, you override at the CSS level:
@theme dark {
--color-surface: oklch(0.15 0.01 265);
--color-surface-raised: oklch(0.22 0.015 265);
--color-text: oklch(0.92 0.01 265);
--color-text-muted: oklch(0.65 0.02 265);
}
No theme toggle logic in your token build. No conditional imports. The browser's own cascade handles it. This is how CSS was supposed to work all along — we just spent five years routing around it.
The tradeoff nobody wants to talk about
If you ship native mobile apps from the same token source, this model doesn't help you. CSS-first tokens are web-first tokens. Android and iOS can't consume @theme blocks. You still need Style Dictionary or a custom pipeline for multi-platform output.
That's the honest calculus: if your product is web-only, Tailwind v4's approach eliminates real complexity. If you're cross-platform, you either maintain two sources of truth (CSS for web, JSON for native) or you keep the JSON pipeline and generate both CSS and native outputs from it.
Some teams are experimenting with a hybrid — JSON remains the canonical source, but the web pipeline uses a thin script that dumps variables into a @theme block instead of raw custom properties. It's not zero-build, but it's a simpler build.
Practical migration: what to do this week
If you're on Tailwind v3 with a tailwind.config.js theme, the migration path is mechanical but worth planning:
Audit your config. List every custom value in
theme.extend. These become CSS variables.Pick a namespace. Tailwind v4 expects specific prefixes:
--color-*,--spacing-*,--font-*,--radius-*. If your existing tokens use different conventions (--ds-color-primary), you'll need to decide whether to adopt Tailwind's naming or use@themewith custom mappings.Move gradually. You can run v4 alongside existing custom properties. Start by migrating color tokens, then spacing, then typography. No need for a big-bang rewrite.
Update your Figma variables. If you're using Figma Variables synced to code, update the code-syntax annotations to point to the new CSS variable names. Tools like Token Studio can export directly to CSS now.
What about the W3C Design Token spec?
The W3C Design Tokens Community Group format (DTCG) isn't going anywhere, and it solves a real problem for cross-platform teams. But its adoption has been glacial. Two years after reaching "community group report" status, tool support remains patchy. Figma's Variables export doesn't produce DTCG-compliant JSON by default. Style Dictionary v4 supports it, but most teams I've talked to still use their own JSON schema.
Tailwind's move sidesteps the format question entirely for web teams. You don't need a universal token format if your only platform speaks CSS natively. That's a pragmatic bet, not a principled one — and it'll age poorly if your product goes cross-platform later. But for the 80% of product teams building SPAs and marketing sites, pragmatism wins.
The uncomfortable conclusion
Design token infrastructure has been over-engineered for most teams. A startup with a Next.js app doesn't need Style Dictionary, DTCG-compliant JSON, and a CI pipeline that generates platform artifacts. They need a CSS file with well-named variables and a Tailwind config that reads them. Tailwind v4 made that the default path, and it's hard to argue the old way was better for these teams.
The design systems community has a tendency to optimize for the enterprise case — the ten-platform, hundred-engineer, thousand-component scenario. That's valid engineering. But most of us are building for smaller contexts, and it's okay to let the CSS do what CSS does best: hold your design decisions in a format the browser already understands.