code-architecture-tailwind-v4-best-practices
Guides Tailwind CSS v4 patterns for buttons and components. Use this skill when creating components with variants, choosing between CVA/tailwind-variants, or configuring Tailwind v4's CSS-first approach.
Best use case
code-architecture-tailwind-v4-best-practices is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Guides Tailwind CSS v4 patterns for buttons and components. Use this skill when creating components with variants, choosing between CVA/tailwind-variants, or configuring Tailwind v4's CSS-first approach.
Teams using code-architecture-tailwind-v4-best-practices should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/code-architecture-tailwind-v4-best-practices/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How code-architecture-tailwind-v4-best-practices Compares
| Feature / Agent | code-architecture-tailwind-v4-best-practices | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Guides Tailwind CSS v4 patterns for buttons and components. Use this skill when creating components with variants, choosing between CVA/tailwind-variants, or configuring Tailwind v4's CSS-first approach.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Tailwind CSS v4: Best Practices
## Core Principle
**Use utilities directly in markup as the primary approach. Abstract with CVA/tailwind-variants only when you have 3+ variants.**
Tailwind v4's CSS-first configuration eliminates `tailwind.config.js` entirely. All configuration happens in CSS via `@theme` directive.
## The CSS-First Setup
```css
@import "tailwindcss";
@theme {
--color-brand-primary: oklch(0.65 0.24 354.31);
--color-brand-secondary: oklch(0.72 0.11 178);
--font-sans: "Inter", sans-serif;
--radius-button: 0.5rem;
}
```
Key v4 changes:
- Single `@import "tailwindcss"` replaces three `@tailwind` directives
- `--color-*` generates color utilities AND exposes as CSS variables
- Automatic template discovery (respects `.gitignore`)
- Oxide engine: 3.5x faster full builds, 8x faster incremental
## When to Abstract
### ✅ Use Pure Utilities When
- Component has 1-2 variants
- Prototyping or simple components
- Bundle size is critical (0KB overhead)
```tsx
// ✅ Simple button - no abstraction needed
<button className="
inline-flex items-center justify-center gap-2
px-4 py-2
bg-blue-500 hover:bg-blue-600 active:bg-blue-700
text-white text-sm font-medium
rounded-md transition-colors
focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500
disabled:opacity-50 disabled:pointer-events-none
">
Save Changes
</button>
```
### ✅ Use CVA When
- 3+ variants needed
- Type safety required
- Building component library
- ~1KB bundle cost acceptable
```typescript
import { cva, type VariantProps } from 'class-variance-authority';
const buttonVariants = cva(
// Base classes
"inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
primary: "bg-blue-500 text-white hover:bg-blue-600",
secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300",
outline: "border-2 border-blue-500 text-blue-500 hover:bg-blue-50",
ghost: "text-blue-500 hover:bg-blue-50"
},
size: {
sm: "h-8 px-3 text-xs",
md: "h-10 px-4 text-sm",
lg: "h-12 px-6 text-base"
}
},
defaultVariants: {
variant: "primary",
size: "md"
}
}
);
export type ButtonProps = VariantProps<typeof buttonVariants>;
```
### ✅ Use Tailwind-Variants When
- Responsive variants needed
- Multi-part/slot components (cards, accordions)
- Component composition via `extend`
- ~4KB bundle cost acceptable
```typescript
import { tv, type VariantProps } from 'tailwind-variants';
const card = tv({
slots: {
base: 'rounded-lg border bg-card shadow-sm',
header: 'flex flex-col space-y-1.5 p-6',
title: 'text-2xl font-semibold',
content: 'p-6 pt-0',
footer: 'flex items-center p-6 pt-0'
},
variants: {
variant: {
elevated: { base: 'shadow-xl' },
flat: { base: 'shadow-none border' }
}
}
});
const { base, header, title, content, footer } = card({ variant: 'elevated' });
```
### ❌ Don't Use @apply
The Tailwind team discourages `@apply` except in narrow circumstances. Use component abstractions instead.
```css
/* ❌ Avoid - hides styling decisions, breaks variant support */
.btn-primary {
@apply bg-blue-500 text-white px-4 py-2 rounded;
}
/* ✅ Use @utility for custom utilities if absolutely needed */
@utility btn-base {
display: inline-flex;
align-items: center;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
}
```
## Decision Matrix
| Approach | Bundle | Type Safe | Use Case |
|----------|--------|-----------|----------|
| Pure Tailwind | 0KB | ❌ | Simple, 1-2 variants, prototyping |
| CVA | ~1KB | ✅ | Component libraries, most projects |
| Tailwind-variants | ~4KB | ✅ | Complex design systems, slots |
## State Management with Data Attributes
V4 supports native data attributes for clean state management:
```tsx
export function Button({ isLoading, isDisabled, children }: ButtonProps) {
return (
<button
data-loading={isLoading ?? ""}
data-disabled={isDisabled ?? ""}
className="
bg-blue-500 text-white px-4 py-2 rounded
hover:bg-blue-600
data-loading:opacity-50 data-loading:cursor-wait
data-disabled:opacity-50 data-disabled:pointer-events-none
"
>
{isLoading && <Spinner className="mr-2" />}
{children}
</button>
);
}
```
Custom variants via `@custom-variant`:
```css
@custom-variant selected-not-disabled (&[data-selected]:not([data-disabled]));
```
## Modern React Pattern (shadcn/ui style)
```tsx
import { tv, type VariantProps } from 'tailwind-variants';
const buttonStyles = tv({
base: "inline-flex items-center justify-center rounded-md font-medium transition-colors",
variants: {
variant: {
primary: "bg-blue-500 text-white hover:bg-blue-600",
secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300"
},
size: {
sm: "h-8 px-3 text-xs",
md: "h-10 px-4 text-sm"
}
}
});
type ButtonProps = React.ComponentProps<"button"> &
VariantProps<typeof buttonStyles>;
export function Button({ variant, size, className, ...props }: ButtonProps) {
return (
<button
data-slot="button"
className={cn(buttonStyles({ variant, size }), className)}
{...props}
/>
);
}
```
## Accessibility Checklist
```tsx
<button
type="button"
disabled={disabled || loading}
aria-disabled={disabled || loading}
aria-busy={loading}
aria-label={ariaLabel}
className={buttonStyles({ variant, size })}
>
{loading && <Spinner aria-hidden="true" />}
{leftIcon && <span data-slot="icon">{leftIcon}</span>}
<span data-slot="label">{children}</span>
</button>
```
## Breaking Changes from v3
| v3 | v4 |
|----|-----|
| `shadow-sm` | `shadow-xs` |
| `rounded-sm` | `rounded-xs` |
| `bg-opacity-50` | `bg-black/50` |
| `bg-gradient-to-r` | `bg-linear-to-r` |
| `border` (gray-200 default) | `border` (currentColor) |
| `ring` (3px blue-500) | `ring-3` (currentColor) |
Automated migration: `npx @tailwindcss/upgrade`
## Quick Reference
### DO
- Use utilities directly for simple components
- Wait for **3+ variants** before using CVA/tailwind-variants
- Use **data attributes** for state management
- Follow **shadcn/ui patterns** for React components
- Use **@theme** for design tokens (generates utilities + CSS vars)
### DON'T
- Use `@apply` for component styles
- Abstract prematurely (same rule as code abstractions)
- Mix approaches inconsistently within a project
- Forget accessibility attributes on interactive elements
## Recommended Stack (2025)
- **React**: Next.js 15 + shadcn/ui + CVA + Tailwind v4
- **Vue**: Vue 3 + shadcn/vue + Tailwind v4
- **Bundle**: CVA (~1KB) + clsx (~0.2KB) + tailwind-merge (~7KB) ≈ 8KB total
## References
- [Tailwind CSS v4 Docs](https://tailwindcss.com/docs)
- [CVA (class-variance-authority)](https://cva.style/docs)
- [Tailwind Variants](https://www.tailwind-variants.org/)
- [shadcn/ui](https://ui.shadcn.com/)Related Skills
typescript-best-practices
Guides TypeScript best practices for type safety, code organization, and maintainability. Use this skill when configuring TypeScript projects, deciding on typing strategies, writing async code, or reviewing TypeScript code quality.
tailwind-v4-configuration
Configure Tailwind CSS v4 with CSS-first approach. Use when installing, migrating from v3, setting up build tools (Vite/PostCSS/CLI), customizing themes with @theme, or configuring plugins.
code-architecture-wrong-abstraction
Guides when to abstract vs duplicate code. Use this skill when creating shared utilities, deciding between DRY/WET approaches, or refactoring existing abstractions.
what-not-to-do-as-product-manager
Anti-patterns and mistakes to avoid as a product manager. Use when evaluating leadership behaviors, improving team dynamics, reflecting on management practices, or onboarding new product managers.
visual-cues-cta-psychology
Design effective CTAs using visual attention and gaze psychology principles. Use when designing landing pages, button hierarchies, conversion elements, or optimizing user attention flow through interfaces.
vercel-sandbox
Run agent-browser + Chrome inside Vercel Sandbox microVMs for browser automation from any Vercel-deployed app. Use when the user needs browser automation in a Vercel app (Next.js, SvelteKit, Nuxt, Remix, Astro, etc.), wants to run headless Chrome without binary size limits, needs persistent browser sessions across commands, or wants ephemeral isolated browser environments. Triggers include "Vercel Sandbox browser", "microVM Chrome", "agent-browser in sandbox", "browser automation on Vercel", or any task requiring Chrome in a Vercel Sandbox.
value-realization
Analyze if end users discover clear value. Use when evaluating product concepts, analyzing adoption, or uncertain about direction.
user-story-fundamentals
Capture requirements from user perspective with structured user stories. Use when writing backlog items, defining acceptance criteria, prioritizing features, or communicating requirements between product and development.
typescript-satisfies-operator
Guides proper usage of TypeScript's satisfies operator vs type annotations. Use this skill when deciding between type annotations (colon) and satisfies, validating object shapes while preserving literal types, or troubleshooting type inference issues.
typescript-interface-vs-type
Guides when to use interface vs type in TypeScript. Use this skill when defining object types, extending types, or choosing between interface and type aliases.
typescript-advanced-types
Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.
trust-psychology
Build trust signals that reduce perceived risk and enable user action. Use when designing landing pages, checkout flows, onboarding experiences, or any conversion point where user hesitation is a barrier.