Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Implement and maintain Tailwind CSS design systems within a multi-agent Claude Code plugin ecosystem
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/details.md
1# tailwind-design-system — detailed patterns and worked examples23## Patterns45### Pattern 1: CVA (Class Variance Authority) Components67```typescript8// components/ui/button.tsx9import { Slot } from '@radix-ui/react-slot'10import { cva, type VariantProps } from 'class-variance-authority'11import { cn } from '@/lib/utils'1213const buttonVariants = cva(14// Base styles - v4 uses native CSS variables15'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 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',16{17variants: {18variant: {19default: 'bg-primary text-primary-foreground hover:bg-primary/90',20destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',21outline: 'border border-border bg-background hover:bg-accent hover:text-accent-foreground',22secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',23ghost: 'hover:bg-accent hover:text-accent-foreground',24link: 'text-primary underline-offset-4 hover:underline',25},26size: {27default: 'h-10 px-4 py-2',28sm: 'h-9 rounded-md px-3',29lg: 'h-11 rounded-md px-8',30icon: 'size-10',31},32},33defaultVariants: {34variant: 'default',35size: 'default',36},37}38)3940export interface ButtonProps41extends React.ButtonHTMLAttributes<HTMLButtonElement>,42VariantProps<typeof buttonVariants> {43asChild?: boolean44}4546// React 19: No forwardRef needed47export function Button({48className,49variant,50size,51asChild = false,52ref,53...props54}: ButtonProps & { ref?: React.Ref<HTMLButtonElement> }) {55const Comp = asChild ? Slot : 'button'56return (57<Comp58className={cn(buttonVariants({ variant, size, className }))}59ref={ref}60{...props}61/>62)63}6465// Usage66<Button variant="destructive" size="lg">Delete</Button>67<Button variant="outline">Cancel</Button>68<Button asChild><Link href="/home">Home</Link></Button>69```7071### Pattern 2: Compound Components (React 19)7273```typescript74// components/ui/card.tsx75import { cn } from '@/lib/utils'7677// React 19: ref is a regular prop, no forwardRef78export function Card({79className,80ref,81...props82}: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) {83return (84<div85ref={ref}86className={cn(87'rounded-lg border border-border bg-card text-card-foreground shadow-sm',88className89)}90{...props}91/>92)93}9495export function CardHeader({96className,97ref,98...props99}: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) {100return (101<div102ref={ref}103className={cn('flex flex-col space-y-1.5 p-6', className)}104{...props}105/>106)107}108109export function CardTitle({110className,111ref,112...props113}: React.HTMLAttributes<HTMLHeadingElement> & { ref?: React.Ref<HTMLHeadingElement> }) {114return (115<h3116ref={ref}117className={cn('text-2xl font-semibold leading-none tracking-tight', className)}118{...props}119/>120)121}122123export function CardDescription({124className,125ref,126...props127}: React.HTMLAttributes<HTMLParagraphElement> & { ref?: React.Ref<HTMLParagraphElement> }) {128return (129<p130ref={ref}131className={cn('text-sm text-muted-foreground', className)}132{...props}133/>134)135}136137export function CardContent({138className,139ref,140...props141}: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) {142return (143<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />144)145}146147export function CardFooter({148className,149ref,150...props151}: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) {152return (153<div154ref={ref}155className={cn('flex items-center p-6 pt-0', className)}156{...props}157/>158)159}160161// Usage162<Card>163<CardHeader>164<CardTitle>Account</CardTitle>165<CardDescription>Manage your account settings</CardDescription>166</CardHeader>167<CardContent>168<form>...</form>169</CardContent>170<CardFooter>171<Button>Save</Button>172</CardFooter>173</Card>174```175176### Pattern 3: Form Components177178```typescript179// components/ui/input.tsx180import { cn } from '@/lib/utils'181182export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {183error?: string184ref?: React.Ref<HTMLInputElement>185}186187export function Input({ className, type, error, ref, ...props }: InputProps) {188return (189<div className="relative">190<input191type={type}192className={cn(193'flex h-10 w-full rounded-md border border-border bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',194error && 'border-destructive focus-visible:ring-destructive',195className196)}197ref={ref}198aria-invalid={!!error}199aria-describedby={error ? `${props.id}-error` : undefined}200{...props}201/>202{error && (203<p204id={`${props.id}-error`}205className="mt-1 text-sm text-destructive"206role="alert"207>208{error}209</p>210)}211</div>212)213}214215// components/ui/label.tsx216import { cva, type VariantProps } from 'class-variance-authority'217218const labelVariants = cva(219'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'220)221222export function Label({223className,224ref,225...props226}: React.LabelHTMLAttributes<HTMLLabelElement> & { ref?: React.Ref<HTMLLabelElement> }) {227return (228<label ref={ref} className={cn(labelVariants(), className)} {...props} />229)230}231232// Usage with React Hook Form + Zod233import { useForm } from 'react-hook-form'234import { zodResolver } from '@hookform/resolvers/zod'235import { z } from 'zod'236237const schema = z.object({238email: z.string().email('Invalid email address'),239password: z.string().min(8, 'Password must be at least 8 characters'),240})241242function LoginForm() {243const { register, handleSubmit, formState: { errors } } = useForm({244resolver: zodResolver(schema),245})246247return (248<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">249<div className="space-y-2">250<Label htmlFor="email">Email</Label>251<Input252id="email"253type="email"254{...register('email')}255error={errors.email?.message}256/>257</div>258<div className="space-y-2">259<Label htmlFor="password">Password</Label>260<Input261id="password"262type="password"263{...register('password')}264error={errors.password?.message}265/>266</div>267<Button type="submit" className="w-full">Sign In</Button>268</form>269)270}271```272273### Pattern 4: Responsive Grid System274275```typescript276// components/ui/grid.tsx277import { cn } from '@/lib/utils'278import { cva, type VariantProps } from 'class-variance-authority'279280const gridVariants = cva('grid', {281variants: {282cols: {2831: 'grid-cols-1',2842: 'grid-cols-1 sm:grid-cols-2',2853: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',2864: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',2875: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-5',2886: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-6',289},290gap: {291none: 'gap-0',292sm: 'gap-2',293md: 'gap-4',294lg: 'gap-6',295xl: 'gap-8',296},297},298defaultVariants: {299cols: 3,300gap: 'md',301},302})303304interface GridProps305extends React.HTMLAttributes<HTMLDivElement>,306VariantProps<typeof gridVariants> {}307308export function Grid({ className, cols, gap, ...props }: GridProps) {309return (310<div className={cn(gridVariants({ cols, gap, className }))} {...props} />311)312}313314// Container component315const containerVariants = cva('mx-auto w-full px-4 sm:px-6 lg:px-8', {316variants: {317size: {318sm: 'max-w-screen-sm',319md: 'max-w-screen-md',320lg: 'max-w-screen-lg',321xl: 'max-w-screen-xl',322'2xl': 'max-w-screen-2xl',323full: 'max-w-full',324},325},326defaultVariants: {327size: 'xl',328},329})330331interface ContainerProps332extends React.HTMLAttributes<HTMLDivElement>,333VariantProps<typeof containerVariants> {}334335export function Container({ className, size, ...props }: ContainerProps) {336return (337<div className={cn(containerVariants({ size, className }))} {...props} />338)339}340341// Usage342<Container>343<Grid cols={4} gap="lg">344{products.map((product) => (345<ProductCard key={product.id} product={product} />346))}347</Grid>348</Container>349```350351For advanced animation and dark mode patterns, see [references/advanced-patterns.md](references/advanced-patterns.md):352353- **Pattern 5: Native CSS Animations** — dialog `@keyframes`, native popover API with `@starting-style`, `allow-discrete` transitions, and a full `DialogContent`/`DialogOverlay` implementation using Radix UI354- **Pattern 6: Dark Mode** — `ThemeProvider` context with `localStorage` persistence, `prefers-color-scheme` detection, meta `theme-color` update, and a `ThemeToggle` button component355356## Utility Functions357358```typescript359// lib/utils.ts360import { type ClassValue, clsx } from "clsx";361import { twMerge } from "tailwind-merge";362363export function cn(...inputs: ClassValue[]) {364return twMerge(clsx(inputs));365}366367// Focus ring utility368export const focusRing = cn(369"focus-visible:outline-none focus-visible:ring-2",370"focus-visible:ring-ring focus-visible:ring-offset-2",371);372373// Disabled utility374export const disabled = "disabled:pointer-events-none disabled:opacity-50";375```376377For advanced v4 CSS patterns, the full v3-to-v4 migration checklist, and complete best practices, see [references/advanced-patterns.md](references/advanced-patterns.md):378379- **Custom `@utility`** — reusable CSS utilities for decorative lines and text gradients380- **Theme modifiers** — `@theme inline` (reference other CSS vars), `@theme static` (always output), `@import "tailwindcss" theme(static)`381- **Namespace overrides** — clearing default Tailwind color scales with `--color-*: initial`382- **Semi-transparent variants** — `color-mix()` for alpha scale generation383- **Container queries** — `--container-*` token definitions384- **v3→v4 migration checklist** — 10-item checklist covering config, directives, colors, dark mode, animations, React 19 ref changes385- **Best practices** — full Do's and Don'ts list386