Tailwind CSS Advanced Patterns
Master Tailwind: custom design tokens, component extraction with CVA, dark mode patterns, animations, and plugin authoring.
Tailwind CSS v4 moves to a CSS-first configuration model. Advanced usage means extending the design system with custom tokens, building consistent component variants, and managing the balance between utility classes and component abstraction.
Use Tailwind for rapid UI development. Extract repeated utility patterns with `class-variance-authority` (CVA) rather than `@apply`. Configure design tokens in CSS variables for dynamic theming. Prefer dark mode via `prefers-color-scheme` first, with `dark:` utilities for overrides.
Workflow
- Configure design tokens in Tailwind v4Tailwind v4 uses CSS variables as the config layer - no more `tailwind.config.js` for most customization.
- Extract component variants with CVA`@apply` defeats Tailwind's purpose and is hard to maintain; CVA creates type-safe variant APIs without extraction.
- Implement dark mode correctlyDark mode via system preference is automatic; explicit toggle needs class strategy.
- Add custom animationsTailwind's default animations are minimal - custom ones should follow the same utility pattern.
- Organize Tailwind at scaleLong class strings become unmaintainable; conventions improve DX without abandoning utility-first.
Output Format
CSS-first design tokens in `@theme`, CVA for component variants, dark mode with class strategy, custom animations in CSS, prettier-plugin-tailwindcss for ordering, tailwind-merge for conflict resolution.
Examples
CVA button: `const btn = cva("rounded font-semibold transition-colors", { variants: { variant: { solid: "bg-blue-600 text-white hover:bg-blue-700", outline: "border-2 border-blue-600 text-blue-600 hover:bg-blue-50" }, size: { sm: "px-3 py-1 text-sm", md: "px-4 py-2", lg: "px-6 py-3 text-lg" } } }); <button className={btn({ variant: "solid", size: "md" })}>Click</button>`tailwind-merge: `import { twMerge } from "tailwind-merge"; function cx(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) }; // usage: cx("px-4 py-2", props.className) - user className overrides your defaults`Custom animation: `@theme { --animate-bounce-in: bounce-in 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); } @keyframes bounce-in { 0% { transform: scale(0.5); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }`Edge Cases & Gotchas
- CSS purging: Tailwind scans your files for class names; dynamic class construction like `text-${color}-500` won't work - use a safelist or complete class names in the template
- Conflicting utilities: `text-red-500` and `text-blue-500` both applied → last one wins (browser), but `tailwind-merge` resolves this correctly to the latter
- Content configuration in v4: Tailwind v4 auto-detects files; in monorepos you may need to add explicit content paths in the CSS file with `@source "../packages/ui"`
When to Break These Rules
- For email templates, use inline styles - email clients don't support external CSS. For highly dynamic styles (user-defined colors), CSS custom properties set via JS are more appropriate than Tailwind utilities.
Reference Files
Tailwind v4 docs: https://tailwindcss.com/docsclass-variance-authority: https://cva.style/docstailwind-merge: https://github.com/dcastil/tailwind-mergeprettier-plugin-tailwindcss: https://github.com/tailwindlabs/prettier-plugin-tailwindcss