Crash Courses · PhD
Tailwind v4 & Production
Understand Tailwind v4 CSS-first config and production optimization.
10 min read+200 XP on completionCert: Tailwind CSS Crash Course
Tap any word in the text below to start reading from there.
Tailwind v4 & Production
Tailwind v4 shifts to CSS-first configuration. The JIT engine is rewritten in Rust via Lightning CSS â€" significantly faster builds.
Tailwind v4: CSS Config
/* app/globals.css */
@import "tailwindcss";
@theme {
--color-brand-50: #fffbeb;
--color-brand-500: #f59e0b;
--color-brand-700: #b45309;
--font-sans: var(--font-inter), system-ui, sans-serif;
--radius-4xl: 2rem;
--animate-fade-in: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}No tailwind.config.ts needed for basic theming. Custom tokens become utilities: bg-brand-500, text-brand-700.
v3 vs v4 Key Differences
v3 v4
tailwind.config.ts @theme in CSS
content: [...] auto-detected
PostCSS Lightning CSS (faster)
darkMode: 'class' @variant dark (&:where(.dark, .dark *))
require() plugins @plugin directivestwMerge for Class Conflicts
import { twMerge } from 'tailwind-merge'
import { clsx, type ClassValue } from 'clsx'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
// Without twMerge: both px-4 and px-6 would be in the class string (conflict)
// With twMerge: the LAST one wins correctly
<Button className="px-6"> // overrides Button's default px-4Production Checklist
✅ All content paths in tailwind.config.ts content array (v3)
✅ No dynamic class construction: bg-${color}-500 (JIT can't scan)
✅ Safelist for dynamic classes: safelist: ['bg-red-500', 'bg-green-500']
✅ Purge working: check final CSS size (should be under 20KB)
✅ twMerge in cn() utility for overrideable components
✅ No @apply for anything you can do with a React componentReading progress
0% read
In this module
Tailwind v4
@theme
CSS cascade layers
Purging
0%