CODEXA.Technologiez
All insightsEngineering

Migrating to Tailwind v4: what actually changes

Codexa Engineering · Sep 19, 2026 · 3 min read

Tailwind v4 is not a routine upgrade. The configuration model changed, which means most of the advice and nearly all of the generators you will find online produce output that simply does not apply.

This site runs on v4. Here is what actually changes.

The config file is gone

In v3, theming lived in `tailwind.config.js` — a JavaScript object of colours, spacing and font families. In v4 it lives in your CSS, in an `@theme` block.

That sounds cosmetic and is not. Tokens declared this way become real CSS custom properties at runtime, which means they can change without a rebuild — the thing that makes runtime theming straightforward instead of a compile-time trick.

What @theme inline buys you

Declaring a token registers it with Tailwind so it generates utilities. Declare a colour and you get the background, text and border utilities for it.

The `inline` keyword matters: it makes Tailwind reference the variable rather than copying its value. That is what allows a single set of utility classes to resolve differently in light and dark mode, because the variable underneath them changes rather than the class.

Dark mode without duplicate classes

The pattern that works: declare your tokens once, then redeclare the values under a dark selector. Your components keep using the same utilities throughout.

The trap is the interaction between system preference and an explicit toggle. Scope the media query so that someone who has explicitly chosen light mode is not overridden by a dark system setting — this is the bug nearly every hand-rolled theme toggle ships with first.

Other things that moved

  • PostCSS setup changed: the plugin is now `@tailwindcss/postcss`.
  • `@tailwind base/components/utilities` is replaced by a single `@import "tailwindcss"`.
  • Some default values shifted, so a visual diff after upgrading is worth the time.
  • Arbitrary values, variants and the utility vocabulary are largely unchanged — your markup mostly survives.

How to approach the migration

  1. Upgrade on a branch and expect a visual diff rather than a clean swap.
  2. Move your theme into an `@theme` block before touching components.
  3. Check contrast after moving colours — derived dark-mode values are the easiest place to fail WCAG without noticing.
  4. Work through the visual diff page by page. Most of it is spacing and default-value drift, not breakage.

A shortcut for the theme

We built a Tailwind v4 theme generator while doing this ourselves. Give it your brand colours and it emits a complete `@theme` block with the light and dark declarations and the correctly scoped media query, and it flags any pair that fails WCAG contrast before you ship it.

If you want to check specific pairs by hand, the contrast checker grades a whole palette at once.

The migration is a day's work on a small codebase and worth doing. The CSS-native model is genuinely better, and the v3 config was always a slightly awkward place for design tokens to live.

What changed between Tailwind v3 and v4?

Theming moved out of tailwind.config.js and into CSS, in an @theme block. That sounds cosmetic and is not: tokens declared this way become real CSS custom properties at runtime, which is what makes runtime theming straightforward instead of a compile-time trick. The PostCSS plugin changed too, and the three @tailwind directives became a single @import.

How long does a Tailwind v4 migration take?

About a day on a small codebase. Most of the work is a visual diff rather than breakage — arbitrary values, variants and the utility vocabulary are largely unchanged, so your markup mostly survives. Move the theme into an @theme block before touching components, and check contrast afterwards, because derived dark-mode values are the easiest place to fail WCAG without noticing.

Enjoyed this? Let's work together.

Start a project