All posts

1 min read

Migrating to Tailwind CSS v4: What Actually Changes

No more tailwind.config.ts, a CSS-first theme, and a handful of renamed utilities. Notes from migrating a production Next.js site to Tailwind v4.

Tailwind CSSCSS

Tailwind v4 moves configuration out of JavaScript and into your stylesheet. The first time you see it, the diff is jarring: the config file is gone, and the theme lives next to your tokens.

CSS-first configuration

css
@import "tailwindcss";

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

@theme inline {
  --color-background: hsl(var(--background));
  --color-foreground: hsl(var(--foreground));
  --font-sans: var(--font-geist-sans), system-ui, sans-serif;
}

Design tokens become first-class CSS variables under the @theme block, and every utility is generated from them. If you were already running a CSS-variable design system (the shadcn/ui pattern), the migration is mostly mechanical.

The gotchas that bit me

  • Preflight sets buttons to cursor: default — add a base rule if your UI was designed around pointer cursors.
  • shadow-sm became shadow-xs, and the ring default dropped from 3px to 1px.
  • Class-based dark mode now needs an explicit @custom-variant declaration.

The payoff is real: faster builds via the new engine, no PostCSS plugin chain to babysit, and one less config file drifting out of sync with your CSS.