Skip to main content
The journal / Field notes

Tailwind CSS v4: What's New and Why It Matters

Exploring Tailwind CSS v4's new engine, CSS-first configuration, and how it improves the developer experience for modern web projects.

tailwindcssweb-developmentperformance
Cover for Tailwind CSS v4: What's New and Why It Matters

A New Engine Under the Hood

Tailwind CSS v4 introduces a redesigned build engine and CSS-first configuration. Build performance depends on the project, integrations, and cache state; measure those conditions in your own repository before assigning a speedup to an upgrade.

But speed is just the beginning. The real story of v4 is a philosophical shift: CSS-first configuration.

CSS-First Configuration

In v4, you can define design tokens directly in CSS using @theme. JavaScript configuration is still supported for compatibility when explicitly loaded with @config:

@import 'tailwindcss';

@theme {
  --color-bg-primary: #0d1117;
  --color-bg-secondary: #161b22;
  --color-accent-teal: #00adb5;
  --color-accent-rose: #e2725b;

  --font-heading: 'Poppins', system-ui, sans-serif;
  --font-body: 'Inter', system-ui, sans-serif;

  --shadow-card: 0 4px 24px rgba(0, 0, 0, 0.3);
}

This approach has several advantages:

  1. No context switching — Your design tokens are CSS custom properties, readable by any tool
  2. One place for tokens — Configuration and styles can be reviewed together
  3. Better IDE support — CSS files get syntax highlighting, autocomplete, and validation
  4. Composability — Tokens can reference other tokens using standard CSS var() syntax

If you're migrating from v3, the @tailwindcss/upgrade CLI handles most of the conversion automatically. It reads your JavaScript config and generates the equivalent @theme block.

How We Structure Design Tokens

At Rosecraft Studios, we organize our @theme block into semantic categories:

@theme {
  /* === Colors === */
  --color-bg-primary: #0d1117;
  --color-bg-secondary: #161b22;
  --color-bg-card: #131920;

  --color-accent-teal: #00adb5;
  --color-accent-rose: #e2725b;
  --color-text-primary: #e6edf3;
  --color-text-muted: #8b949e;

  /* === Typography === */
  --font-heading: 'Poppins', system-ui, sans-serif;
  --font-body: 'Inter', system-ui, sans-serif;

  /* === Shadows === */
  --shadow-card: 0 4px 24px rgba(0, 0, 0, 0.3);
  --shadow-teal-glow: 0 4px 16px rgba(0, 173, 181, 0.25);
}

Then in components, we use only the token names — never raw hex values:

<div className="bg-bg-card border border-border rounded-lg p-6">
  <h3 className="font-heading text-text-primary text-xl font-semibold">Card Title</h3>
  <p className="font-body text-text-muted">Card description</p>
</div>

This discipline pays off when a client requests a theme adjustment. Changing --color-accent-teal from #00adb5 to #0891b2 updates every component that uses it — instantly, with zero search-and-replace.

Automatic Content Detection

Tailwind v4 eliminates the content array configuration. The new engine automatically detects which files use utility classes:

/* v3: Required explicit content paths */
/* content: ['./src/**/*.{js,ts,jsx,tsx}'] */

/* v4: Just import and it works */
@import 'tailwindcss';

Automatic detection has exclusions, including ignored files and dependencies. Use @source to register additional directories, such as shared components in a monorepo:

@import 'tailwindcss';
@source "../shared/components";
@source "./src";

Container Queries Are Built In

Container queries — long a CSS wish-list feature — are first-class in v4:

<div className="@container">
  <div className="@sm:flex @sm:gap-4 @md:grid @md:grid-cols-3">
    {/* Layout responds to container width, not viewport */}
  </div>
</div>

Container queries are especially powerful for component libraries. A card component can adapt its layout based on the space available to it, regardless of where it's placed in the page.

Measuring an Upgrade

Record clean-build time, rebuild time after a representative edit, generated CSS size, and peak memory before and after the migration. Use the same machine and dependencies, repeat the measurements, and keep the output alongside the pull request. A faster compiler does not automatically mean a faster page for visitors.

Breaking Changes to Know About

The migration isn't entirely seamless. Key breaking changes:

Utility and Configuration Changes

Review the official upgrade guide for renamed shadow and radius utilities, ring defaults, and browser requirements. Use a representative set of pages for visual checks, including focus, hover, and responsive states.

Legacy Plugins and CSS Utilities

@plugin loads a legacy JavaScript plugin. It does not replace the JavaScript plugin() helper with a CSS file loader:

@import 'tailwindcss';
@plugin '@tailwindcss/typography';

For a utility defined in CSS, use @utility:

@utility content-auto {
  content-visibility: auto;
}

See the directive reference.

Dark Mode

The dark variant uses prefers-color-scheme by default. To control it through a .dark class, override the variant:

@import 'tailwindcss';
@custom-variant dark (&:where(.dark, .dark *));

See Tailwind's dark-mode documentation.

Planning a Migration

Check browser support and plugin compatibility first. Run the upgrade tool on a branch, review the generated changes, then compare key layouts and interaction states. Treat a migration as a code and visual review, with enough room to investigate regressions.

Need help with a Tailwind v4 migration or building a design system? Reach out — we've done this for multiple production applications and know where the gotchas are.

Keep the conversation going

Share this article

Corey Rosamond, Founder and Principal Engineer of Rosecraft Studios

Corey Rosamond

Founder & Principal Engineer

Learn more
Occasional notes

Stay in the loop.

Get notified when we publish new insights on web development and engineering.