Skip to main content
</>Rosecraft Studios
tailwindcssweb-developmentperformance

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

4 min read

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.

Share this article

Corey Rosamond, Founder and Principal Engineer of Rosecraft Studios

Corey Rosamond

Founder & Principal Engineer

Learn more

Enjoyed this article?

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