shadcn-ui-designer
SKILL.md
Shadcn UI Designer
Build production-ready UI components using shadcn/ui principles: minimal, accessible, composable, and beautiful by default.
Core Philosophy
Design modern, minimal interfaces with:
- Clean typography (Inter/system fonts, 2-3 weights max)
- Ample whitespace (4px-based spacing: p-1 through p-8)
- Subtle shadows (shadow-sm/md/lg only)
- Accessible contrast (WCAG AA minimum)
- Smooth micro-interactions (200-300ms transitions)
- Professional neutrals (slate/zinc scale) with subtle accents
Build composable components that work together seamlessly.
Quick Start Pattern
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
export function MyComponent() {
return (
<div className="container mx-auto p-6 space-y-6">
<div className="space-y-2">
<h1 className="text-2xl font-semibold">Title</h1>
<p className="text-sm text-muted-foreground">Description</p>
</div>
<Card className="shadow-sm">
<CardHeader>
<CardTitle>Section</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{/* Content here */}
</CardContent>
</Card>
</div>
)
}
Design System Rules
Typography
- Hierarchy:
text-2xl(headings) →text-base(body) →text-sm(secondary) - Weights:
font-semibold(600) for emphasis,font-medium(500) for labels,font-normal(400) for body - Colors:
text-foreground(primary),text-muted-foreground(secondary)
<h1 className="text-2xl font-semibold">Page Title</h1>
<p className="text-muted-foreground">Supporting text</p>
Spacing
Use consistent spacing scale:
- Micro:
space-y-2(8px) - within sections - Small:
space-y-4(16px) - between elements - Medium:
space-y-6(24px) - between sections - Large:
space-y-8(32px) - major divisions
<div className="container mx-auto p-6 space-y-6">
<section className="space-y-4">
<div className="space-y-2">
{/* Related elements */}
</div>
</section>
</div>
Colors
Use semantic color tokens:
- Background:
bg-background,bg-card,bg-muted - Foreground:
text-foreground,text-muted-foreground - Borders:
border-border,border-input - Primary:
bg-primary,text-primary-foreground - Destructive:
bg-destructive,text-destructive-foreground
<Card className="bg-card text-card-foreground border-border">
<Button className="bg-primary text-primary-foreground">
Primary Action
</Button>
<div className="bg-muted/50 text-muted-foreground">
Subtle highlight
</div>
</Card>
Shadows & Elevation
Three levels only:
shadow-sm: Cards, raised sections (0 1px 2px)shadow-md: Dropdowns, popovers (0 4px 6px)shadow-lg: Modals, dialogs (0 10px 15px)
<Card className="shadow-sm hover:shadow-md transition-shadow" />
Animations
- Duration: 200-300ms
- Easing: ease-in-out
- Use cases: Hover states, loading states, reveals
<Button className="transition-colors duration-200 hover:bg-primary/90">
<Card className="transition-all duration-200 hover:shadow-md hover:scale-[1.02]">
Accessibility
Always include:
- Semantic HTML (
<main>,<nav>,<article>) - ARIA labels on icons/actions
- Focus states (
:focus-visible:ring-2) - Keyboard navigation
- WCAG AA contrast (4.5:1 minimum)
<button
aria-label="Close dialog"
className="focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2"
>
<X className="h-4 w-4" />
</button>
Component Patterns
Dashboard Cards
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
{stats.map(stat => (
<Card key={stat.id} className="shadow-sm">
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium">
{stat.label}
</CardTitle>
{stat.icon}
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{stat.value}</div>
<p className="text-xs text-muted-foreground">
{stat.change}
</p>
</CardContent>
</Card>
))}
</div>
Forms
<form className="space-y-6">
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Full Name</Label>
<Input
id="name"
placeholder="Enter your name"
className="max-w-md"
/>
</div>
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="name@example.com"
className="max-w-md"
/>
</div>
</div>
<div className="flex gap-3">
<Button type="submit">Submit</Button>
<Button type="button" variant="outline">Cancel</Button>
</div>
</form>
Data Tables
<Card className="shadow-sm">
<CardHeader>
<CardTitle>Recent Orders</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Order</TableHead>
<TableHead>Customer</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{orders.map(order => (
<TableRow
key={order.id}
className="hover:bg-muted/50 transition-colors"
>
<TableCell className="font-medium">{order.id}</TableCell>
<TableCell>{order.customer}</TableCell>
<TableCell>
<Badge variant={order.statusVariant}>
{order.status}
</Badge>
</TableCell>
<TableCell className="text-right">
{order.amount}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
</Card>
Modals/Dialogs
<Dialog>
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Edit Profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when done.
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
{/* Form fields */}
</div>
<DialogFooter>
<Button type="submit">Save changes</Button>
</DialogFooter>
</DialogContent>
</Dialog>
Loading States
// Skeleton loading
<Card className="shadow-sm">
<CardHeader>
<Skeleton className="h-4 w-[200px]" />
</CardHeader>
<CardContent className="space-y-3">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-[80%]" />
</CardContent>
</Card>
// Loading button
<Button disabled>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Please wait
</Button>
Empty States
<Card className="shadow-sm">
<CardContent className="flex flex-col items-center justify-center py-12 space-y-3">
<div className="p-4 bg-muted rounded-full">
<FileX className="h-8 w-8 text-muted-foreground" />
</div>
<div className="text-center space-y-1">
<h3 className="font-semibold">No results found</h3>
<p className="text-sm text-muted-foreground">
Try adjusting your search
</p>
</div>
<Button variant="outline" size="sm">
Clear filters
</Button>
</CardContent>
</Card>
Layout Patterns
Container Widths
// Full width with constraints
<div className="container mx-auto px-4 max-w-7xl">
// Content-focused (prose)
<div className="container mx-auto px-4 max-w-3xl">
// Form-focused
<div className="container mx-auto px-4 max-w-2xl">
Responsive Grids
// Dashboard grid
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
// Content + sidebar
<div className="grid gap-6 lg:grid-cols-[1fr_300px]">
<main>{/* Content */}</main>
<aside>{/* Sidebar */}</aside>
</div>
// Two column split
<div className="grid gap-6 md:grid-cols-2">
Navigation
<header className="border-b border-border">
<div className="container mx-auto flex h-16 items-center justify-between px-4">
<div className="flex items-center gap-6">
<Logo />
<nav className="hidden md:flex gap-6">
<a href="#" className="text-sm font-medium transition-colors hover:text-primary">
Dashboard
</a>
<a href="#" className="text-sm font-medium text-muted-foreground transition-colors hover:text-foreground">
Projects
</a>
</nav>
</div>
<div className="flex items-center gap-4">
<Button variant="ghost" size="sm">
<Bell className="h-4 w-4" />
</Button>
<Avatar>
<AvatarImage src={user.avatar} />
<AvatarFallback>{user.initials}</AvatarFallback>
</Avatar>
</div>
</div>
</header>
Best Practices
Component Organization
// ✅ Good: Small, focused components
export function UserCard({ user }) {
return (
<Card>
<CardHeader>
<UserAvatar user={user} />
<UserDetails user={user} />
</CardHeader>
</Card>
)
}
// ❌ Avoid: Large monolithic components
export function DashboardPage() {
// 500 lines of JSX...
}
Composability
// ✅ Compose shadcn components
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<MoreVertical className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Share</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="text-destructive">
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
State Management
// Form state
const [formData, setFormData] = useState({ name: '', email: '' })
// Loading states
const [isLoading, setIsLoading] = useState(false)
// UI states
const [isOpen, setIsOpen] = useState(false)
Error Handling
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
className={errors.email ? "border-destructive" : ""}
/>
{errors.email && (
<p className="text-sm text-destructive">{errors.email}</p>
)}
</div>
</form>
Common Shadcn Components
Essential Components
- Layout: Card, Tabs, Sheet, Dialog, Popover, Separator
- Forms: Input, Textarea, Select, Checkbox, Radio, Switch, Label
- Buttons: Button, Toggle, ToggleGroup
- Display: Badge, Avatar, Skeleton, Table
- Feedback: Alert, Toast, Progress
- Navigation: NavigationMenu, DropdownMenu, Command
Button Variants
<Button>Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
<Button variant="destructive">Delete</Button>
Badge Variants
<Badge>Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="outline">Outline</Badge>
<Badge variant="destructive">Error</Badge>
Workflow
- Understand requirements - What component/page is needed?
- Choose components - Which shadcn/ui components fit?
- Build structure - Layout and hierarchy first
- Apply styling - Typography, spacing, colors
- Add interactions - Hover states, transitions, focus
- Ensure accessibility - ARIA, keyboard, contrast
- Test responsive - Mobile, tablet, desktop
Quality Checklist
Before completing:
- Uses shadcn/ui components appropriately
- Follows 4px spacing scale (p-2, p-4, p-6, etc.)
- Uses semantic color tokens (bg-card, text-foreground, etc.)
- Limited shadow usage (shadow-sm/md/lg only)
- Smooth transitions (200-300ms duration)
- ARIA labels on interactive elements
- Keyboard focus visible (ring-2 ring-primary)
- WCAG AA contrast ratios
- Mobile-responsive layout
- Loading and error states handled
References
- Shadcn UI - Component library
- Tailwind CSS - Utility classes
- WCAG 2.1 - Accessibility standards