~ / mitul
$
Senior Full Stack & AI Engineer
Surat, IN - UTC+05:30
OPEN TO WORK
~30 hrs/wk

Mitul Jagad

Senior Full Stack Developer. 6+ years shipping production for SaaS and FinTech teams. Lately building AI agents and workflow automations that have to actually run, not just demo.

Resume
FOLLOW
ALL ARTICLESFrontend

Tailwind v4's @theme: one year in

Feb 20265 min
Frontend

Tailwind v4's @theme: one year in

The CSS-first @theme block was the biggest shift in Tailwind's history and the documentation barely sells it. After a year of building real apps with it, here's what changed and why I'm not going back.

Tailwind v4 dropped the `tailwind.config.js` file in favour of a CSS-first `@theme` block. After a year of using it on production sites, it's the single best change Tailwind has made - and most teams I talk to still don't realise how much it simplifies things.

What changed in one sentence

Your design tokens - colours, fonts, spacing, shadows - now live as CSS variables inside an `@theme` block in your CSS, not as a JavaScript object in a config file.

What that actually means in code

@import "tailwindcss";

@theme {
  --color-paper: #f0f2f5;
  --color-ink: #1a1f2c;
  --color-primary: #4770ff;
  --font-sans: "Instrument Sans", ui-sans-serif, system-ui;
}

Now `bg-paper`, `text-ink`, `bg-primary`, and `font-sans` are utility classes - and the variables themselves are real CSS custom properties you can also reference directly in styles, JS, or third-party components.

Why this matters

Themes become trivial

Multi-theme sites used to mean either rebuilding Tailwind with different configs or overriding utility classes by hand. Now you set `--color-primary` to one value at `:root` and another at `[data-theme="dark"]`, and every `bg-primary` in the app re-tints automatically. No re-build. No `dark:` prefix proliferation. Just CSS variables doing what they were designed to do.

Third-party libraries finally fit

Any library that accepts a CSS variable for a colour can now be wired into your design tokens directly - no token-mapping shim, no theme-provider gymnastics.

Less config noise

A 200-line `tailwind.config.js` extending the default theme, declaring custom plugins, and exporting types collapses into a 30-line `@theme` block in the CSS file you were already going to edit.

What's lost

If you were doing heavy programmatic theme generation in JavaScript (e.g. computing token scales from a base value), v4's @theme is less ergonomic. You can still do it - the tokens are just CSS, and CSS now has `color-mix()` and arithmetic - but if your design system was built around computed JS objects, the migration takes some thinking.

How to migrate

  1. Run the official upgrade tool - it handles the mechanical bits.
  2. Move each `theme.extend.colors` entry into `@theme` as `--color-<name>`.
  3. Replace any place you accessed tokens from JS (often custom plugins or shadcn-style components) with CSS variable references.
  4. Delete `tailwind.config.js`. You won't need it back.

I migrated three production sites to v4 over the last year. None of them have been moved back. The CSS-first model is genuinely better for the way design systems are built today.