| name | tailwind-v4 |
| description | Tailwind CSS v4 patterns, @utility/@custom-variant directives, dark mode, v3→v4 migration. Official docs source. |
| user-invocable | false |
Tailwind CSS v4 Patterns (Official Docs Source)
Based on Tailwind CSS v4 documentation pulled from Context7.
Setup (Next.js 16 + PostCSS)
@import "tailwindcss";
@theme {
--color-brand: #6366f1;
--color-brand-light: #818cf8;
--color-brand-dark: #4f46e5;
--font-display: "Inter", sans-serif;
}
v3 → v4 Breaking Changes (Critical)
CSS Import Change
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";
Ring Width Default Changed
<button class="ring ring-blue-500">
<button class="ring-3 ring-blue-500">
Shadow/Radius/Blur Scale Renamed
<div class="shadow-sm rounded-sm blur-sm">
<div class="shadow-xs rounded-xs blur-xs">
outline-none Changed
<button class="focus:outline-hidden">
Default Border Color Changed
<div class="border">
<div class="border border-gray-200">
CSS Variable Syntax Changed
<div class="bg-[--brand-color]">
<div class="bg-(--brand-color)">
Hover Variant Updated
<button class="hover:bg-blue-600">
Variant Stacking Order Changed
<div class="dark:group-hover:bg-blue-500">
@utility Directive (New in v4)
@utility text-shadow-sm {
text-shadow: 0 1px 2px rgb(0 0 0 / 0.15);
}
@utility text-shadow-md {
text-shadow: 0 2px 4px rgb(0 0 0 / 0.15);
}
@utility scrollbar-hidden {
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
Usage:
<h1 class="text-shadow-sm hover:text-shadow-md dark:text-shadow-md">
Title
</h1>
@custom-variant Directive (New in v4)
@custom-variant theme-midnight (&:where([data-theme="midnight"] *));
@custom-variant theme-ocean (&:where([data-theme="ocean"] *));
Usage:
<div data-theme="midnight">
<p class="theme-midnight:text-white theme-ocean:text-blue-100">
Themed text
</p>
</div>
Dark Mode
Option 1: Media Query (default)
<div class="bg-white dark:bg-gray-900">
<p class="text-gray-900 dark:text-gray-100">Content</p>
</div>
Option 2: Class-based Toggle
@import "tailwindcss";
@custom-variant dark (&:where(.dark *));
'use client'
import { useEffect, useState } from 'react'
export function ThemeToggle() {
const [isDark, setIsDark] = useState(false)
useEffect(() => {
document.documentElement.classList.toggle('dark', isDark)
}, [isDark])
return (
<button onClick={() => setIsDark(!isDark)}>
{isDark ? '☀️' : '🌙'}
</button>
)
}
@theme Block (Replaces tailwind.config.js)
@import "tailwindcss";
@theme {
--color-primary: #6366f1;
--color-primary-foreground: #ffffff;
--color-secondary: #f1f5f9;
--color-secondary-foreground: #0f172a;
--color-muted: #f1f5f9;
--color-muted-foreground: #64748b;
--color-destructive: #ef4444;
--color-border: #e2e8f0;
--color-background: #ffffff;
--color-foreground: #0f172a;
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
--spacing-page: 2rem;
--spacing-section: 4rem;
--radius-lg: 0.75rem;
--radius-md: 0.5rem;
--radius-sm: 0.25rem;
}
Usage:
<div class="bg-primary text-primary-foreground rounded-lg p-page">
<h2 class="font-sans">Heading</h2>
<code class="font-mono">Code</code>
</div>
@apply in v4
@reference "../../app/globals.css";
.custom-button {
@apply rounded-lg bg-primary px-4 py-2 text-primary-foreground;
}
cn() Utility Pattern
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
import { cn } from '@/lib/utils'
function Button({ variant, className, ...props }: ButtonProps) {
return (
<button
className={cn(
'rounded-lg px-4 py-2 font-medium',
variant === 'primary' && 'bg-primary text-primary-foreground',
variant === 'secondary' && 'bg-secondary text-secondary-foreground',
variant === 'ghost' && 'hover:bg-muted',
className // allow override from parent
)}
{...props}
/>
)
}
Common Patterns
Responsive Design
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
</div>
Animation
<div class="transition-colors duration-200 hover:bg-muted">
Hover me
</div>
<div class="animate-pulse rounded bg-muted h-4 w-32">
</div>
Container
<div class="container mx-auto px-4">
Content
</div>
Rules
- v4 syntax only — no
@tailwind directives, use @import "tailwindcss"
- Always specify border color — defaults to
currentColor in v4
- Use
ring-3 for v3-equivalent ring width
- Use
outline-hidden not outline-none for v3 focus behavior
- CSS variables use parentheses —
bg-(--my-var) not bg-[--my-var]
- Theme in CSS — use
@theme block, not tailwind.config.js
- cn() for conditionals — never string concatenation for class logic