Web development

Tailwind CSS v4: what changed and how to migrate

David Morales VegaNovember 25, 202512 min read

Originally published on Bemorex Learn, our learning platform. · Leer en español

Tailwind CSS v4: a complete rewrite

Tailwind CSS v4 is not an incremental update: it's a complete rewrite from scratch. The new engine, based on the Oxide project written in Rust, offers up to 10x faster compilation, native CSS configuration, and a model more aligned with web standards.

The main changes

1. Configuration in CSS, not JavaScript

The most significant change is that configuration is done directly in CSS using the @theme directive, eliminating the tailwind.config.js file.

@import "tailwindcss";

@theme {
  --color-primary: #ffc400;
  --color-secondary: #e6286a;
  --font-sans: "Inter", system-ui, sans-serif;
  --radius-lg: 1rem;
}

This means:

  • Your configuration is standard CSS
  • Theme variables are real CSS custom properties
  • You can access theme values from any CSS

2. Automatic content detection

In v3, you needed to manually configure the file paths to scan:

// v3 - manual configuration
content: ['./src/**/*.{html,ts,tsx}']

In v4, Tailwind automatically detects your project's files by analyzing the repository structure. You only need to exclude files if necessary:

/* v4 - optional exclusion */
@source not "./src/legacy/**";

3. CSS-first, not utility-first

Tailwind v4 relies more on native CSS. Many features that previously required special configuration now use CSS standards:

/* Theme variables are real CSS custom properties */
.custom-element {
  background: var(--color-primary);
  font-family: var(--font-sans);
  border-radius: var(--radius-lg);
}

New utilities and syntax

Native container queries

<!-- Define a container -->
<div class="@container">
  <!-- Respond to the container size -->
  <div class="@sm:grid @sm:grid-cols-2 @lg:grid-cols-3">
    <!-- content -->
  </div>
</div>

You can also name containers:

<div class="@container/sidebar">
  <div class="@sm/sidebar:flex @md/sidebar:grid">
    <!-- content -->
  </div>
</div>

Open field variants

<!-- Styles based on the state of child inputs -->
<fieldset class="has-[:focus]:ring-2 has-[:invalid]:border-red-500">
  <input type="email" required />
</fieldset>

<!-- not variant -->
<div class="not-[:first-child]:border-t">
  <!-- all except the first child get a top border -->
</div>

Improved gradients

<!-- oklch interpolation for more natural gradients -->
<div class="bg-linear-to-r/oklch from-blue-500 to-green-500">
  <!-- gradient with perceptual interpolation -->
</div>

<!-- Custom angles -->
<div class="bg-linear-[135deg] from-amber-400 to-rose-500">
  <!-- 135-degree gradient -->
</div>

3D transforms

<div class="rotate-x-45 rotate-y-12 perspective-800">
  <!-- Native 3D transforms -->
</div>

Syntax changes: v3 vs v4

Concept v3 v4
Main import @tailwind base; @tailwind components; @tailwind utilities; @import "tailwindcss";
Configuration tailwind.config.js @theme { } in CSS
Custom colors theme.extend.colors --color-* in @theme
Custom spacing theme.extend.spacing --spacing-* in @theme
Custom breakpoints theme.screens --breakpoint-* in @theme
Custom fonts theme.fontFamily --font-* in @theme
Plugins plugins: [require('...')] @plugin "..." in CSS
Container queries Plugin required Native with @container
Content paths content: [...] Automatic detection

Renamed utilities

Some utilities were renamed for consistency:

v3 v4 Reason
shadow-sm shadow-xs More logical scale
shadow shadow-sm Consistency
drop-shadow-sm drop-shadow-xs Same scale as shadow
blur-sm blur-xs Consistency
rounded-sm rounded-xs Same logic
outline-none outline-hidden More descriptive
ring ring-3 Explicit width
flex-grow grow Simplification
flex-shrink shrink Simplification
bg-opacity-50 bg-black/50 Modifier syntax

Step-by-step migration guide

Step 1: Update dependencies

# Install Tailwind v4
npm install tailwindcss@4

# For Angular, install the PostCSS plugin
npm install @tailwindcss/postcss@4

Step 2: Migrate the configuration

Replace tailwind.config.js with a @theme block in your main CSS:

/* src/styles.css */
@import "tailwindcss";

@theme {
  /* Move your custom colors */
  --color-brand: #ffc400;
  --color-brand-hover: #ff9100;

  /* Move your fonts */
  --font-sans: "Inter", system-ui, sans-serif;

  /* Move your breakpoints if you customized them */
  --breakpoint-sm: 40rem;
  --breakpoint-md: 48rem;
  --breakpoint-lg: 64rem;
}

Step 3: Use the automatic migration tool

Tailwind v4 includes a codemod for automatic migration:

npx @tailwindcss/upgrade

This command:

  • Converts tailwind.config.js to @theme in CSS
  • Renames deprecated utilities
  • Updates the import syntax
  • Migrates compatible plugins

Step 4: Update renamed classes

Search and replace classes that changed names. The codemod handles most of them, but verify manually:

# Find potentially obsolete classes
grep -r "shadow-sm\|rounded-sm\|blur-sm\|ring " src/

Step 5: Verify dark mode

/* v4 uses the media query by default */
/* To use a class instead, configure: */
@variant dark (&:where(.dark, .dark *));

Performance: the numbers

Tailwind v4's Oxide (Rust) engine offers dramatic improvements:

Metric v3 v4 Improvement
Full build ~350ms ~35ms ~10x
Incremental build ~180ms ~8ms ~22x
CSS size Variable Smaller Better tree-shaking

Conclusion

Tailwind CSS v4 is a generational leap. CSS-native configuration, automatic content detection, and the Oxide engine make the workflow faster and more natural. The automatic migration tool eases the transition, but take time to familiarize yourself with the new theme syntax and renamed utilities. If you're starting a new project, there's no reason not to use v4.

Code examples

CSS (v4)

/* tailwind.css - Configuration in pure CSS */
@import "tailwindcss";

@theme {
  --color-brand: #ffc400;
  --color-brand-dark: #ff9100;
  --color-surface: #0a0a0f;

  --font-display: "Inter", sans-serif;
  --font-mono: "Fira Code", monospace;

  --breakpoint-xs: 30rem;
  --breakpoint-sm: 40rem;
  --breakpoint-md: 48rem;
  --breakpoint-lg: 64rem;
  --breakpoint-xl: 80rem;

  --spacing-18: 4.5rem;
  --spacing-88: 22rem;
}

JavaScript (v3)

// tailwind.config.js - NO longer needed in v4
module.exports = {
  content: ['./src/**/*.{html,ts}'],
  theme: {
    extend: {
      colors: {
        brand: '#ffc400',
        'brand-dark': '#ff9100',
      },
      fontFamily: {
        display: ['Inter', 'sans-serif'],
        mono: ['Fira Code', 'monospace'],
      },
    },
  },
  plugins: [],
};

Keep reading

Want to build something like this?

Tell us what you need: we look at it together and propose a way forward in writing, in English or Spanish.

[email protected]