DevToolBoxFREE
Blog

Tailwind CSS Advanced Guide: v4 Features, Custom Plugins, Dark Mode, CVA, Animation & Performance

20 min readby DevToolBox Team
TL;DRTailwind v4 introduces CSS-first configuration with @theme directives and Lightning CSS for faster builds. Build scalable design systems using design tokens, custom plugins, and component patterns like CVA. Use container queries for component-level responsive design, class-based dark mode for maximum control, and tailwind-merge to handle class conflicts. Optimize production bundles with proper content configuration and tree-shaking. The v3 to v4 migration requires switching from tailwind.config.js to CSS-based configuration.
Key Takeaways
  • v4 uses CSS-first config with @theme directives replacing JS config files
  • Lightning CSS engine provides significantly faster build times
  • Container queries enable true component-level responsive design
  • CVA + tailwind-merge is the best approach for type-safe component variants
  • Custom plugins extend the framework via addUtilities and matchUtilities
  • Proper content config and avoiding dynamic class names are key to performance
  • The official codemod tool automates most of the v3 to v4 migration

Tailwind CSS has become the most popular utility-first CSS framework in modern frontend development. This guide covers 13 advanced topics, from v4 new features to production optimization, helping you build scalable design systems and high-performance component libraries. Each section includes practical code examples and best practices.

Whether you are an intermediate user new to advanced Tailwind patterns or a veteran developer migrating from v3 to v4, this guide provides clear code examples and actionable advice. We will dive deep into CSS-first configuration, custom plugin development, responsive design strategies, component variant management, and more.

1. Tailwind v4 New Features

Tailwind v4 is a major rewrite. Configuration moves from JavaScript to CSS, the build engine switches to Lightning CSS, and automatic content detection replaces manual configuration. The @theme directive lets you define design tokens directly in CSS.

CSS-First Config and @theme

No more tailwind.config.js file. All customizations are defined via the @theme directive in CSS files, including colors, fonts, breakpoints, easing functions, and custom animations. This means your editor can provide CSS intellisense directly, with no extra JS config file needed.

/* tailwind v4 — CSS-first configuration */
@import "tailwindcss";

@theme {
  --color-primary: #3b82f6;
  --color-secondary: #8b5cf6;
  --font-display: "Inter", sans-serif;
  --breakpoint-3xl: 1920px;
  --ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

/* Use in HTML: class="text-primary font-display" */
/* Container queries built-in: @container, @sm, @md */
Tip: Lightning CSS is 100x+ faster than PostCSS, supports automatic browser target downleveling, CSS nesting, and color functions without extra plugins.

v4 Core Changes at a Glance

  • Build Engine -- switched from PostCSS to Lightning CSS, 100x+ build speed improvement
  • Configuration -- migrated from tailwind.config.js to CSS @theme directives
  • Content Detection -- automatic file discovery and scanning, no manual content array needed
  • Container Queries -- built-in support for @container and @sm/@md/@lg prefixes
  • P3 Color Gamut -- wide-gamut P3 color support for more vibrant colors on modern displays
  • Composable Variants -- composable variant system supporting chained modifiers like group-hover:focus:

2. Design System with Tailwind

The core of a design system is design tokens -- reusable values for colors, spacing, typography scales, and border radii. Tailwind's @theme lets you define semantic tokens that map to concrete values, enabling theme switching and consistency.

Token Hierarchy

Use a three-layer token structure: raw values (raw colors), semantic tokens (primary, surface), and component tokens (button-bg, card-border). This layering means theme switching only requires changing the semantic layer.

@theme {
  /* Spacing scale (4px base) */
  --spacing-xs: 0.25rem;  /* 4px */
  --spacing-sm: 0.5rem;   /* 8px */
  --spacing-md: 1rem;     /* 16px */
  --spacing-lg: 1.5rem;   /* 24px */
  --spacing-xl: 2rem;     /* 32px */

  /* Typography scale */
  --text-xs: 0.75rem;  --text-sm: 0.875rem;
  --text-base: 1rem;   --text-lg: 1.125rem;
  --text-xl: 1.25rem;  --text-2xl: 1.5rem;
  --text-3xl: 1.875rem;

  /* Semantic colors */
  --color-surface: #ffffff;
  --color-on-surface: #1e293b;
}
Tip: Avoid using too many arbitrary values in your design system. Sticking to predefined tokens maintains visual consistency and makes design audits easier.

3. Custom Plugins

Tailwind's plugin system provides three core APIs: addUtilities for static utilities, addComponents for component classes, and matchUtilities for dynamic value-based utilities. In v4, register plugins with the @plugin directive.

Plugin API Details

  • addUtilities -- register static utilities like .text-shadow-sm
  • addComponents -- register component-level styles like .btn, .card
  • matchUtilities -- create utilities that accept dynamic values, like .grid-cols-fill-[200px]
  • addVariant -- register custom variant modifiers
// my-plugin.js
const plugin = require("tailwindcss/plugin");

module.exports = plugin(function({
  addUtilities, addComponents, matchUtilities, theme
}) {
  // Static utility: .text-shadow-sm
  addUtilities({
    ".text-shadow-sm": {
      textShadow: "0 1px 2px rgb(0 0 0 / 0.1)"
    }
  });
  // Dynamic utility: .grid-cols-fill-[200px]
  matchUtilities(
    { "grid-cols-fill": (v) => ({
        gridTemplateColumns: `repeat(auto-fill, minmax(\${v}, 1fr))`
    })},
    { values: theme("spacing") }
  );
});
Tip: In v4, use @plugin "./my-plugin.js" in your CSS file to register plugins, replacing the plugins array in tailwind.config.js from v3.

4. Responsive Design

Tailwind's responsive design uses a mobile-first approach. v4 has built-in container query support and fluid typography, enabling responsive layouts at the component level rather than the viewport level.

Container Queries vs Viewport Breakpoints

Viewport breakpoints (sm:, md:, lg:) are based on browser window width. Container queries (@sm:, @md:, @lg:) are based on parent container width, letting components auto-adapt in different layout positions regardless of viewport size.

<!-- Viewport breakpoints (mobile-first) -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3
            gap-4 md:gap-6 lg:gap-8">
  <!-- items -->
</div>

<!-- Container queries (v4 built-in) -->
<div class="@container">
  <div class="flex flex-col @md:flex-row @lg:grid
              @lg:grid-cols-3">
    <!-- Responds to container width, not viewport -->
  </div>
</div>

<!-- Fluid typography with clamp -->
<h1 class="text-[clamp(1.5rem,4vw,3rem)]">Title</h1>
Tip: Use named containers @container/sidebar to let child elements respond to a specific ancestor container, not just the nearest one.

5. Dark Mode

The class strategy (selector strategy in v4) provides maximum control. Combining localStorage for user preference and prefers-color-scheme for system detection, you can implement a tri-state toggle (light/dark/system).

Multi-Theme Support

Go beyond simple light/dark switching by using CSS custom properties with data attributes to support any number of themes. Each theme defines its own set of color variables, and switching themes only requires changing the data-theme attribute value.

<!-- Dark mode with class strategy -->
<html class="dark">
  <body class="bg-white dark:bg-gray-950
               text-gray-900 dark:text-gray-100">
    <div class="border border-gray-200
                dark:border-gray-800
                shadow-sm dark:shadow-gray-900/50">
      <h2 class="text-gray-800 dark:text-gray-200">
        Adaptive Card
      </h2>
    </div>
  </body>
</html>

/* Custom themes via data attributes */
[data-theme="ocean"] { --color-primary: #0ea5e9; }
[data-theme="forest"] { --color-primary: #22c55e; }
Tip: Use JavaScript to detect system preference on page load: window.matchMedia("(prefers-color-scheme: dark)").matches, then combine with the user's choice in localStorage to determine the initial theme.

6. Animation & Transitions

Tailwind provides rich transition and animation utilities. Define custom keyframe animations via @theme, and use motion-safe and motion-reduce to respect the user's reduced motion preferences.

Transition Utilities

Tailwind provides fine-grained transition control: transition watches common properties, transition-colors watches only color changes, transition-transform watches only transforms. Combine with duration-*, ease-*, and delay-* utilities for precise transition behavior control.

Accessible Animations

Always wrap animation classes with the motion-safe: prefix to ensure users who prefer reduced motion do not see unnecessary animation effects. This is an important web accessibility practice.

@theme {
  --animate-fade-in: fade-in 0.5s ease-out;
  --animate-slide-up: slide-up 0.3s ease-out;
}

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes slide-up {
  from { transform: translateY(10px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

<!-- Usage with motion-safe -->
<div class="motion-safe:animate-fade-in
            transition-all duration-300 ease-out
            hover:scale-105 hover:shadow-lg">
</div>
Tip: transition-all watches for changes in all CSS properties, which can impact performance. If you only need to transition specific properties (like colors and transform), use transition-colors or transition-transform instead.

7. Component Patterns

CVA (class-variance-authority) lets you define component variants in a type-safe way. Combined with tailwind-merge to automatically resolve class conflicts, you build robust component APIs.

Why Use CVA

As projects grow, manually managing class name combinations for component variants (size, color, state) becomes unmaintainable. CVA centralizes variant definitions, provides a type-safe API, and integrates perfectly with TypeScript to automatically infer variant types.

Compound Variants

CVA's compoundVariants lets you define additional styles for specific variant combinations. For example, adding extra padding or font weight when intent is primary and size is lg.

import { cva } from "class-variance-authority";
import { twMerge } from "tailwind-merge";

const button = cva(
  "inline-flex items-center rounded-lg font-medium",
  {
    variants: {
      intent: {
        primary: "bg-blue-600 text-white hover:bg-blue-700",
        secondary: "bg-gray-100 text-gray-800 hover:bg-gray-200",
        danger: "bg-red-600 text-white hover:bg-red-700",
      },
      size: {
        sm: "px-3 py-1.5 text-sm",
        md: "px-4 py-2 text-base",
        lg: "px-6 py-3 text-lg",
      },
    },
    defaultVariants: { intent: "primary", size: "md" },
  }
);
// Usage: button({ intent: "danger", size: "lg" })
Tip: tailwind-merge intelligently resolves class conflicts (e.g., when both p-2 and p-4 are present, it keeps only the latter), which is critical when components accept external className overrides.

8. Tailwind with React

When using Tailwind with React, clsx and the cn utility function are best practices for conditional class names. The cn function combines clsx with tailwind-merge, handling both conditional logic and class conflict resolution.

The cn Utility Function

The cn function has become the standard pattern in React + Tailwind projects, widely adopted by popular component libraries like shadcn/ui. It first processes conditional logic via clsx, then resolves conflicts via tailwind-merge.

import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";

// The cn utility — used everywhere in modern React
function cn(...inputs: (string | undefined | boolean)[]) {
  return twMerge(clsx(inputs));
}

// Conditional classes in a React component
function Badge({ variant, children }) {
  return (
    <span className={cn(
      "inline-flex rounded-full px-2 py-0.5 text-xs",
      variant === "success" && "bg-green-100 text-green-700",
      variant === "error" && "bg-red-100 text-red-700",
      variant === "info" && "bg-blue-100 text-blue-700"
    )}>
      {children}
    </span>
  );
}
Tip: shadcn/ui uses the cn function as its core utility. When installing shadcn/ui components, it automatically creates the cn function in lib/utils.ts, which you can reuse throughout your project.

9. Performance Optimization

Tailwind v4 automatically detects content sources and tree-shakes unused utilities. Avoiding dynamic class name construction is the most important optimization principle -- always use complete class name strings so the compiler can correctly identify and retain needed utilities.

Optimization Checklist

  • Use complete class name strings, never string concatenation
  • Use @source not to exclude directories that do not need scanning
  • Use @source inline() to safelist dynamically referenced class names
  • Enable CSS minification in your build pipeline
  • Prefer design tokens over arbitrary values
/* v4 automatic content detection — no config needed */
@import "tailwindcss";

/* Exclude files from scanning if needed */
@source not "./src/legacy/**";

/* DO: Use complete class names */
const color = isError ? "text-red-500" : "text-green-500";

/* DON'T: Dynamic class construction breaks purging */
/* const color = `text-\${err ? "red" : "green"}-500`; */

/* Safelist classes that are only used dynamically */
@source inline("text-red-500 text-green-500
  bg-blue-100 bg-blue-200 bg-blue-300");

/* Analyze bundle size */
/* npx tailwindcss --output dist/tw.css --minify */
Tip: In v4, @source inline() replaces the v3 safelist config. Place dynamically referenced class names in @source inline() to ensure they are not removed by tree-shaking.

10. Arbitrary Values & Groups

Arbitrary values allow you to inject custom CSS values using bracket syntax. The group and peer modifiers let you style elements based on parent or sibling state without JavaScript.

Named Groups and Peers

When there are nested groups or multiple peer elements, use named groups (group/name) and named peers (peer/name) to precisely target elements and avoid ambiguity.

The arbitrary value bracket syntax supports almost all CSS property values. Use underscores to replace spaces (e.g., grid-cols-[1fr_2fr_1fr]), and use the arbitrary property syntax [property:value] to access CSS properties without utility classes.

<!-- Arbitrary values -->
<div class="top-[117px] bg-[#1da1f2]
            grid-cols-[1fr_2fr_1fr]
            [mask-type:luminance]">
</div>

<!-- Group hover — parent controls child -->
<a class="group block rounded-lg p-6 hover:bg-gray-50">
  <h3 class="group-hover:text-blue-600">Title</h3>
  <p class="group-hover:text-gray-600">Desc</p>
</a>

<!-- Peer — sibling controls sibling -->
<input class="peer" type="email" />
<p class="hidden peer-invalid:block text-red-500">
  Invalid email address
</p>
Tip: The arbitrary property syntax [property:value] lets you use CSS properties that Tailwind does not yet have utilities for, like [mask-type:luminance] or [writing-mode:vertical-rl].

11. Grid & Flexbox Layouts

Tailwind provides complete Grid and Flexbox utilities. auto-fill and auto-fit create adaptive grids, and subgrid lets nested grids align to parent grid tracks.

Adaptive Grid Patterns

auto-fill creates as many columns as possible to fill space, while auto-fit stretches existing columns when there are not enough items. Neither requires media queries, enabling truly adaptive layouts.

Subgrid is a powerful CSS Grid extension that lets nested grids inherit their parent grid's track definitions. This is very useful for complex card layouts that require cross-column alignment, such as keeping card titles, content, and buttons vertically aligned across different cards.

<!-- Auto-fill responsive grid (no breakpoints needed) -->
<div class="grid grid-cols-[repeat(auto-fill,
            minmax(280px,1fr))] gap-6">
  <div>Card 1</div>
  <div>Card 2</div>
  <div>Card 3</div>
</div>

<!-- Complex flexbox layout -->
<div class="flex flex-wrap items-start gap-4">
  <aside class="w-64 shrink-0">Sidebar</aside>
  <main class="min-w-0 flex-1">Content</main>
</div>

<!-- Subgrid for aligned children -->
<div class="grid grid-cols-4 gap-4">
  <div class="col-span-2 grid grid-cols-subgrid">
    <div>Aligned to parent track</div>
  </div>
</div>
Tip: Use min-w-0 on flex children to prevent content overflow. By default, flex children have a minimum width of auto (content width), which can cause layout overflow.

12. Typography Plugin

The @tailwindcss/typography plugin provides the prose class that automatically adds beautiful typographic styles to Markdown or CMS-rendered HTML content. It supports deep customization and dark mode.

Prose Modifiers

Use prose-sm, prose-lg, prose-xl to control typography size, prose-blue, prose-green to modify link colors, prose-invert for dark mode adaptation. max-w-none removes the default max-width constraint.

<!-- Basic prose usage -->
<article class="prose prose-lg prose-blue
                dark:prose-invert max-w-none">
  <h1>Article Title</h1>
  <p>Content with <a href="#">links</a> and
     <code>inline code</code>.</p>
  <pre><code>code blocks styled</code></pre>
</article>

/* Customize typography in CSS (v4) */
@theme {
  --prose-body: #374151;
  --prose-headings: #111827;
  --prose-links: #2563eb;
  --prose-code: #dc2626;
  --prose-pre-bg: #1e293b;
}

Common Prose Classes Reference

  • prose -- base typography styles
  • prose-sm / prose-lg / prose-xl -- adjust typography size
  • prose-blue / prose-green -- modify link color theme
  • prose-invert -- dark mode adaptation
  • max-w-none -- remove default max-width constraint
  • not-prose -- opt out of typography styles inside prose container
Tip: Use the not-prose class to opt specific elements out of typography styles within a prose container. Useful for embedding custom components inside blog posts.

13. Migration Guide: v3 to v4

The core change migrating from v3 to v4 is the configuration shift from JavaScript to CSS. The official codemod tool automates most migration work. Below are the key migration steps and breaking changes.

Migration Steps Overview

Migration consists of four main steps: run the official upgrade tool, replace @tailwind directives, move configuration into CSS @theme, and update PostCSS config. The official codemod handles most simple changes automatically, but complex custom plugins and configurations may require manual adjustment.

It is recommended to create a comprehensive visual regression test suite (using tools like Chromatic or Percy) before migrating. This allows you to quickly spot style differences during migration and ensure all components maintain consistent visual appearance.

Breaking Changes Checklist

  • bg-opacity-* removed, use bg-black/50 slash syntax instead
  • text-opacity-* removed, use text-black/75 slash syntax instead
  • darkMode config removed, selector strategy is now default
  • @tailwind directives replaced with @import "tailwindcss"
  • tailwind.config.js replaced by CSS @theme
  • PostCSS plugin changes from tailwindcss to @tailwindcss/postcss
/* Step 1: Run the official upgrade tool */
/* npx @tailwindcss/upgrade */

/* Step 2: Replace tailwind directives */
/* Before (v3): */
/* @tailwind base; @tailwind components; @tailwind utilities; */
/* After (v4): */
@import "tailwindcss";

/* Step 3: Move config to CSS @theme */
@theme {
  --color-brand: #3b82f6;
  --font-sans: "Inter", sans-serif;
}

/* Step 4: Update PostCSS config */
/* v4 uses @tailwindcss/postcss instead of tailwindcss */
/* module.exports = { plugins: ["@tailwindcss/postcss"] } */

/* Breaking: bg-opacity-X → bg-black/50 syntax */
/* Breaking: darkMode config → selector strategy default */
Tip: Run npx @tailwindcss/upgrade --dry-run before migrating to preview changes. It is recommended to perform the migration on a new branch and verify each component's styling incrementally.

Summary

Tailwind CSS v4 represents a major evolution of the framework. CSS-first configuration simplifies setup, Lightning CSS dramatically improves build speeds, and built-in container queries make component-level responsive design a reality. Combined with tools like CVA, tailwind-merge, and clsx, you can build type-safe, high-performance, and maintainable component systems.

From custom plugins to @theme design tokens, from dark mode to the typography plugin, mastering these advanced features will help you unlock the full potential of Tailwind and build professional-grade frontend projects. Whether starting from scratch or migrating from v3, the patterns and best practices in this guide will help you work more efficiently.

Recommended Learning Path

  • Step 1 -- familiarize yourself with @theme config and CSS-first workflow
  • Step 2 -- learn the cn utility function and conditional class name patterns
  • Step 3 -- master container queries and advanced responsive design techniques
  • Step 4 -- build type-safe component variant systems with CVA
  • Step 5 -- dive into custom plugin development and design system architecture

Start by learning the most commonly used features, then gradually dive into advanced topics like custom plugins and advanced animations. Keep practicing -- applying these patterns in real projects is the best way to master them.

𝕏 Twitterin LinkedIn
Was this helpful?

Stay Updated

Get weekly dev tips and new tool announcements.

No spam. Unsubscribe anytime.

Try These Related Tools

🌈CSS Gradient Generator🎨Color Converter{ }JSON Formatter

Related Articles

React Design Patterns Guide: Compound Components, Custom Hooks, HOC, Render Props & State Machines

Complete React design patterns guide covering compound components, render props, custom hooks, higher-order components, provider pattern, state machines, controlled vs uncontrolled, composition, observer pattern, error boundaries, and module patterns.

Vue Composition API Guide: Reactivity, Composables, Pinia, Vue Router & Performance Optimization

Complete Vue Composition API guide covering reactive system, composables, props and emits, provide/inject, Vue Router 4, Pinia state management, script setup, teleport, custom directives, transitions, testing with Vitest, and performance optimization.

Clean Code Guide: Naming Conventions, SOLID Principles, Code Smells, Refactoring & Best Practices

Comprehensive clean code guide covering naming conventions, function design, SOLID principles, DRY/KISS/YAGNI, code smells and refactoring, error handling patterns, testing, code review, design by contract, and clean architecture.