- 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 */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;
}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") }
);
});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>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; }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>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" })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>
);
}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 */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>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>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 stylesprose-sm / prose-lg / prose-xl-- adjust typography sizeprose-blue / prose-green-- modify link color themeprose-invert-- dark mode adaptationmax-w-none-- remove default max-width constraintnot-prose-- opt out of typography styles inside prose container
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 insteadtext-opacity-*removed, use text-black/75 slash syntax insteaddarkModeconfig removed, selector strategy is now default@tailwinddirectives replaced with @import "tailwindcss"tailwind.config.jsreplaced 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 */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.