styling-with-tailwind-cva
Use when creating design systems or styling components with Tailwind CSS v4 and Class Variance Authority (CVA) - provides type-safe component variants with CSS variables for design tokens
When & Why to Use This Skill
This Claude skill facilitates the development of robust, scalable design systems by integrating Tailwind CSS v4 with Class Variance Authority (CVA). It empowers developers to create type-safe UI components and manage design tokens through CSS variables, ensuring high maintainability, consistent styling, and seamless theme integration across modern web applications.
Use Cases
- Building a centralized design system with standardized design tokens for colors, typography, and spacing using CSS variables.
- Creating complex, reusable React components with multiple type-safe variants (e.g., primary, secondary, ghost) and sizes using CVA.
- Implementing responsive, mobile-first web interfaces that leverage Tailwind CSS v4's utility-first approach and inline theme integration.
- Developing themeable UI libraries where global styles can be easily updated via CSS variables without refactoring component logic.
- Ensuring accessibility and design consistency by enforcing a strict spacing scale and semantic color naming across a codebase.
| name | styling-with-tailwind-cva |
|---|---|
| description | Use when creating design systems or styling components with Tailwind CSS v4 and Class Variance Authority (CVA) - provides type-safe component variants with CSS variables for design tokens |
Styling with Tailwind CSS + CVA
Overview
Framework-agnostic design system pattern: Tailwind CSS v4 (utility-first) + CVA (type-safe variants) + CSS Variables (design tokens).
Tooling
pnpm add tailwindcss clsx class-variance-authority
pnpm add -D @tailwindcss/postcss
CSS Variables for Design Tokens
All design tokens as CSS variables for easy theming:
/* app/globals.css */
@import "tailwindcss";
:root {
/* Colors */
--color-background: #0A0A0A;
--color-surface: rgba(255,255,255,0.03);
--color-text-primary: #FFFFFF;
--color-text-secondary: rgba(255,255,255,0.7);
--color-accent: #00CED1;
--color-border: rgba(255,255,255,0.1);
/* Typography (1.25 ratio) */
--font-size-sm: 1rem;
--font-size-md: 1.25rem;
--font-size-lg: 1.563rem;
--font-size-xl: 1.953rem;
--font-size-2xl: 2.441rem;
/* Spacing (8px base) */
--space-2: 1rem; /* 16px */
--space-3: 1.5rem; /* 24px */
--space-4: 2rem; /* 32px */
--space-6: 3rem; /* 48px */
/* Border Radius */
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--radius-lg: 1rem;
}
Tailwind Theme Integration
@theme inline {
--color-brand-bg: var(--color-background);
--color-brand-surface: var(--color-surface);
--color-brand-accent: var(--color-accent);
}
Usage: bg-brand-surface, text-brand-accent
Component Variants with CVA
Basic pattern:
import { cva, type VariantProps } from 'class-variance-authority'
import { ButtonHTMLAttributes, forwardRef } from 'react'
const buttonVariants = cva(
// Base styles (always applied)
'inline-flex items-center justify-center font-medium transition-all',
{
variants: {
variant: {
primary: 'bg-brand-accent text-black hover:shadow-lg',
secondary: 'border border-brand-accent text-brand-accent',
ghost: 'text-brand-accent hover:bg-brand-accent/10',
},
size: {
sm: 'px-4 py-2 text-sm',
md: 'px-6 py-3 text-base',
lg: 'px-8 py-4 text-lg',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
}
)
export interface ButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, ...props }, ref) => {
return (
<button
className={buttonVariants({ variant, size, className })}
ref={ref}
{...props}
/>
)
}
)
export default Button
Usage:
<Button variant="primary" size="lg">Click me</Button>
<Button variant="secondary">Default size</Button>
Compound Variants
For complex interactions between variants:
const cardVariants = cva('rounded-lg border transition-all', {
variants: {
variant: {
default: 'bg-white border-gray-200',
primary: 'bg-blue-50 border-blue-200',
},
interactive: {
true: 'cursor-pointer hover:shadow-lg',
false: '',
},
},
compoundVariants: [
{
variant: 'primary',
interactive: true,
class: 'hover:bg-blue-100',
},
],
})
Conditional Classes with clsx
import { clsx } from 'clsx'
function Component({ isActive, hasError, className }) {
return (
<div
className={clsx(
'base-class',
isActive && 'active-class',
hasError && 'error-class',
className
)}
/>
)
}
Responsive Design (Mobile-First)
<div className="
flex flex-col // Mobile: column
md:flex-row // Tablet+: row
gap-4 // All: 1rem gap
md:gap-6 // Tablet+: 1.5rem
">
Best Practices
- CSS variables for all tokens - Easy theming
- Mobile-first responsive - Start small, enhance larger
- Type-safe variants with CVA - Catch errors at compile time
- Consistent spacing scale - 8px base unit
- Semantic color naming -
success,errornotgreen,red - Accessible contrast - 4.5:1 minimum for text
Common Patterns
Container component:
const containerVariants = cva('mx-auto px-4', {
variants: {
size: {
default: 'max-w-7xl',
content: 'max-w-4xl',
narrow: 'max-w-2xl',
},
},
})
Badge component:
const badgeVariants = cva('inline-flex items-center font-medium', {
variants: {
variant: {
default: 'bg-gray-100 text-gray-800',
success: 'bg-green-100 text-green-800',
error: 'bg-red-100 text-red-800',
},
size: {
sm: 'px-2 py-0.5 text-xs',
md: 'px-3 py-1 text-sm',
},
},
})
Quality Criteria
- All colors defined as CSS variables
- Typography follows consistent ratio
- Spacing uses 8px base unit
- Components have type-safe CVA variants
- Mobile-first responsive design
- Color contrast meets WCAG AA (4.5:1 minimum)
- Hover/focus states visible