Every Tailwind major version has been an implementation detail until now. Version 4 changes where your design tokens actually live — out of a JavaScript config file and into CSS itself — and that one move changes a few real habits, not just the install command.
Config moved from JS to CSS
tailwind.config.js is no longer the source of truth. Design tokens — colors, spacing, fonts — are now declared directly in CSS with the @theme directive, which means your design system lives in the same file type a browser actually understands, and tools outside the JS build chain can read it too.
Where tokens live, v3 vs v4
The new rules that actually change your habits
- Arbitrary values (
bg-[#f7f9f7]) now compile through the same engine as named tokens — there's less pressure to name every one-off color, but naming still wins for anything reused more than once. - Container queries are a first-class utility (
@container,@lg:) — components can now respond to their own container's width, not just the viewport, which matters for anything reused inside a sidebar versus full-width. - The JIT engine is the only engine — there's no "just-in-time flag" to remember anymore, because there's nothing else it could be.
Custom utilities defined via the old theme() function in plain CSS need rewriting to reference the new CSS variables directly (var(--color-brand)) — the official upgrade tool catches most of these, but review anything hand-written before shipping.
Why this matters for design systems specifically
A CSS-native token file means a design tool, a style-lint rule, or a non-JS build step can read your actual color and spacing scale without parsing JavaScript. For teams running a design system across multiple frameworks — a marketing site in one stack, a product in another — that's the difference between one source of truth and two that drift.
“The best part of v4 isn't a new utility class. It's that our design tokens finally live in a file a browser can read on its own.”
Key Takeaways
- Design tokens now live in CSS via @theme, not tailwind.config.js — that's the real headline change, not any single new utility.
- Container queries are first-class (@container, @lg:) — components can respond to their own box, not just the viewport.
- There's exactly one engine now (JIT) — no flag to remember, no legacy mode to accidentally fall back into.
- A CSS-native token file is readable by non-JS tooling, which matters most for design systems spanning more than one framework.
Where to start
Run the official upgrade tool on a branch, then specifically audit any custom utilities that used the old theme() function — that's the one spot the automated migration doesn't fully catch on its own.








