Site audit, content writing, and competitor analysis for organic search rankings.
Design & media
Tailwind Design System
Try itBuild scalable, themable Tailwind CSS component libraries using CVA for variants, compound components, design tokens, dark mode, and responsive grids.
What it does
Build production-ready component libraries with Tailwind CSS using CVA, compound components, design tokens, and theming.
The skill document
Tailwind Design System
Build production-ready component libraries with Tailwind CSS using CVA, compound components, design tokens, and theming.
WHAT
Patterns for scalable Tailwind-based design systems:
- Class Variance Authority (CVA) for type-safe variants
- Compound component architecture
- CSS variable-based theming
- Dark mode implementation
- Responsive grid systems
- Animation utilities
WHEN
- Building a component library with Tailwind
- Implementing design tokens and theming
- Creating reusable UI components with variants
- Setting up dark mode
- Standardizing patterns across a codebase
KEYWORDS
tailwind, cva, design system, component library, variants, theming, dark mode, design tokens, shadcn, compound components, tailwind-merge
Related skills: tailwind-v4-shadcn for Tailwind v4 setup and migration
Installation
OpenClaw / Moltbot / Clawbot
npx clawhub@latest install tailwind-design-system
Core Setup
Utility Function
// lib/utils.ts
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Design Token Architecture
Primitive Tokens (abstract)
└── Semantic Tokens (purpose)
└── Component Tokens (specific)
Example:
slate-900 → foreground → card-title-color
Pattern 1: CVA Components
Class Variance Authority for type-safe, variant-based components:
// components/ui/button.tsx
import { cva, type VariantProps } from 'class-variance-authority'
import { forwardRef } from 'react'
import { cn } from '@/lib/utils'
const buttonVariants = cva(
// Base styles (always applied)
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes,
VariantProps {
asChild?: boolean
}
const Button = forwardRef(
({ className, variant, size, ...props }, ref) => {
return (
)
}
)
Button.displayName = 'Button'
export { Button, buttonVariants }
Usage:
Delete
Cancel
Pattern 2: Compound Components
Composable components with shared context:
// components/ui/card.tsx
import { cn } from '@/lib/utils'
import { forwardRef } from 'react'
const Card = forwardRef>(
({ className, ...props }, ref) => (
)
)
Card.displayName = 'Card'
const CardHeader = forwardRef>(
({ className, ...props }, ref) => (
)
)
CardHeader.displayName = 'CardHeader'
const CardTitle = forwardRef>(
({ className, ...props }, ref) => (
)
)
CardTitle.displayName = 'CardTitle'
const CardDescription = forwardRef>(
({ className, ...props }, ref) => (
)
)
CardDescription.displayName = 'CardDescription'
const CardContent = forwardRef>(
({ className, ...props }, ref) => (
)
)
CardContent.displayName = 'CardContent'
const CardFooter = forwardRef>(
({ className, ...props }, ref) => (
)
)
CardFooter.displayName = 'CardFooter'
export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter }
Usage:
Account Settings
Manage your account preferences
{/* form fields */}
Save Changes
Pattern 3: Form Components with Validation
// components/ui/input.tsx
import { forwardRef } from 'react'
import { cn } from '@/lib/utils'
export interface InputProps extends React.InputHTMLAttributes {
error?: string
}
const Input = forwardRef(
({ className, type, error, ...props }, ref) => {
return (
{error && (
{error}
)}
)
}
)
Input.displayName = 'Input'
export { Input }
Pattern 4: Grid System
// components/ui/grid.tsx
import { cn } from '@/lib/utils'
import { cva, type VariantProps } from 'class-variance-authority'
const gridVariants = cva('grid', {
variants: {
cols: {
1: 'grid-cols-1',
2: 'grid-cols-1 sm:grid-cols-2',
3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',
4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',
},
gap: {
none: 'gap-0',
sm: 'gap-2',
md: 'gap-4',
lg: 'gap-6',
xl: 'gap-8',
},
},
defaultVariants: {
cols: 3,
gap: 'md',
},
})
interface GridProps extends React.HTMLAttributes, VariantProps {}
export function Grid({ className, cols, gap, ...props }: GridProps) {
return
}
// Container component
const containerVariants = cva('mx-auto w-full px-4 sm:px-6 lg:px-8', {
variants: {
size: {
sm: 'max-w-screen-sm',
md: 'max-w-screen-md',
lg: 'max-w-screen-lg',
xl: 'max-w-screen-xl',
'2xl': 'max-w-screen-2xl',
full: 'max-w-full',
},
},
defaultVariants: {
size: 'xl',
},
})
interface ContainerProps extends React.HTMLAttributes, VariantProps {}
export function Container({ className, size, ...props }: ContainerProps) {
return
}
Usage:
{products.map(product => (
))}
Pattern 5: Dark Mode
Theme Provider
// providers/theme-provider.tsx
'use client'
import { createContext, useContext, useEffect, useState } from 'react'
type Theme = 'dark' | 'light' | 'system'
interface ThemeContextType {
theme: Theme
setTheme: (theme: Theme) => void
resolvedTheme: 'dark' | 'light'
}
const ThemeContext = createContext(undefined)
export function ThemeProvider({
children,
defaultTheme = 'system',
storageKey = 'theme',
}: {
children: React.ReactNode
defaultTheme?: Theme
storageKey?: string
}) {
const [theme, setTheme] = useState(defaultTheme)
const [resolvedTheme, setResolvedTheme] = useState<'dark' | 'light'>('light')
useEffect(() => {
const stored = localStorage.getItem(storageKey) as Theme | null
if (stored) setTheme(stored)
}, [storageKey])
useEffect(() => {
const root = window.document.documentElement
root.classList.remove('light', 'dark')
const resolved = theme === 'system'
? window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
: theme
root.classList.add(resolved)
setResolvedTheme(resolved)
}, [theme])
return (
{ localStorage.setItem(storageKey, t); setTheme(t) },
resolvedTheme,
}}>
{children}
)
}
export const useTheme = () => {
const context = useContext(ThemeContext)
if (!context) throw new Error('useTheme must be used within ThemeProvider')
return context
}
Theme Toggle
import { Moon, Sun } from 'lucide-react'
import { useTheme } from '@/providers/theme-provider'
import { Button } from '@/components/ui/button'
export function ThemeToggle() {
const { resolvedTheme, setTheme } = useTheme()
return (
setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}
>
Toggle theme
)
}
Animation Utilities
// lib/animations.ts
import { cn } from './utils'
export const fadeIn = 'animate-in fade-in duration-300'
export const fadeOut = 'animate-out fade-out duration-300'
export const slideInFromTop = 'animate-in slide-in-from-top duration-300'
export const slideInFromBottom = 'animate-in slide-in-from-bottom duration-300'
export const zoomIn = 'animate-in zoom-in-95 duration-300'
export const zoomOut = 'animate-out zoom-out-95 duration-300'
// Compound animations
export const modalEnter = cn(fadeIn, zoomIn, 'duration-200')
export const modalExit = cn(fadeOut, zoomOut, 'duration-200')
export const dropdownEnter = cn(fadeIn, slideInFromTop, 'duration-150')
Best Practices
Do
- Use CSS variables for theming (enables runtime switching)
- Compose variants with CVA (type-safe, explicit)
- Use semantic color names (
primarynotblue-500) - Forward refs for composition
- Add accessibility attributes (ARIA, focus states)
- Use
tailwind-mergeto handle class conflicts
Don't
- Use arbitrary values when you can extend the theme
- Nest
@applydeeply (hurts readability) - Skip focus states (keyboard users need them)
- Hardcode colors (use semantic tokens)
- Forget to test dark mode
NEVER
- Use hardcoded colors like
bg-blue-500for semantic purposes (usebg-primary) - Skip focus-visible styles on interactive elements
- Mix arbitrary values with design tokens inconsistently
- Forget
forwardRefon reusable components - Use
!importantto override styles (fix the cascade instead)
Related skills
Manage Stripe customers, subscriptions, invoices, products, prices, and payments through OAuth-authenticated API calls.
Post videos, photos, text, and documents to 10 social platforms through a single REST API call.
Search, read, and manage YouTube videos, playlists, channels, subscriptions, and comments via managed OAuth.
Join a video meeting as an AI bot with voice, avatar, and screenshare across four operating modes.
figma-generate-design
OfficialBuild or update a Figma screen from code or description, reusing the file's published design system.
More from wpank
Browse all skillsSystematic code review patterns covering security, performance, maintainability, correctness, and testing — with severity levels, structured feedback guidance, review process, and anti-patterns to avoid. Use when reviewing PRs, establishing review standards, or improving review quality.
Pragmatic coding standards for writing clean, maintainable code — naming, functions, structure, anti-patterns, and pre-edit safety checks. Use when writing new code, refactoring existing code, reviewing code quality, or establishing coding standards.
Build reliable, fast E2E test suites with Playwright and Cypress. Critical user journey coverage, flaky test elimination, CI/CD integration.
Create software diagrams using Mermaid syntax. Use when users need to create, visualize, or document software through diagrams including class diagrams, sequence diagrams, flowcharts, ERDs, C4 architecture diagrams, state diagrams, git graphs, and other diagram types. Triggers include requests to diagram, visualize, model, map out, or show the flow of a system.
Provides backend architecture patterns (Clean Architecture, Hexagonal, DDD) for building maintainable, testable, and scalable systems with clear layering and...
React and Next.js performance optimization guidelines from Vercel Engineering. 57 rules across 8 categories for writing, reviewing, and refactoring React code.