
Frontend Design System
- 45 installs
- 1 repo stars
- Updated January 27, 2026
- bilalmk/todo_correct
frontend-design-system is a Claude Code skill for building responsive, accessible web UIs with Tailwind, shadcn/ui, Material UI, Chakra UI, Ant Design, or Mantine.
About
This skill helps build modern, responsive web UIs using design systems like Tailwind CSS, shadcn/ui, Material UI, Chakra UI, Ant Design, and Mantine. It covers design-system selection, responsive layout patterns, forms with validation, dark mode, and accessibility. A developer uses it when choosing a UI library or building components and layouts, such as a todo app or dashboard. It ships reusable task-card and task-form templates.
- Guides choosing and using Tailwind, shadcn/ui, Material UI, Chakra UI, Ant Design, and Mantine
- Covers responsive layouts, forms with React Hook Form + Zod, dark mode, and accessibility
- Ships task-card and task-form component templates
Frontend Design System by the numbers
- 45 all-time installs (skills.sh)
- Ranked #1,259 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
frontend-design-system capabilities & compatibility
- Capabilities
- ui design · frontend · web design
- Use cases
- ui design · frontend · web design
- IDEs
- vscode · cursor ide
- Pricing
- Free
What frontend-design-system says it does
Build modern, accessible, responsive web applications using industry-leading design systems and best practices.
Todo apps / Modern SaaS:** shadcn/ui + Tailwind
Always use mobile-first approach
npx skills add https://github.com/bilalmk/todo_correct --skill frontend-design-systemAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 45 |
|---|---|
| repo stars | ★ 1 |
| Last updated | January 27, 2026 |
| Repository | bilalmk/todo_correct ↗ |
What it does
Choose a UI design system and build responsive, accessible components, forms, and dark mode for a web app.
Who is it for?
Choosing a design system and building UI components, forms, and layouts for a web app.
When should I use this skill?
The user asks which UI library to use, or needs responsive layouts, forms with validation, dark mode, or accessible components.
What you get
Produces responsive, accessible components and forms with a chosen design system.
- task card component
- task form component
By the numbers
- Covers 6 design systems
- Standard breakpoints at 640/768/1024/1280px
- 10 core capabilities documented
Files
Frontend Design System
Build modern, accessible, responsive web applications using industry-leading design systems and best practices.
Core Capabilities
1. Design System Selection
When users ask "which UI library should I use" or need design system guidance:
Read the comparison guide:
references/design-system-comparison.mdDecision workflow: 1. Identify project requirements (bundle size, customization, speed) 2. Review comparison table for best match 3. Provide installation commands 4. Recommend component set for the use case
Quick recommendations:
- Todo apps / Modern SaaS: shadcn/ui + Tailwind
- Quick prototypes: Chakra UI or Mantine
- Enterprise dashboards: Material UI or Ant Design
- Custom designs: Headless UI + Tailwind
- TypeScript-heavy: Mantine
2. Responsive Layout Design
When users need mobile-friendly layouts or responsive components:
Read the patterns guide:
references/responsive-design-patterns.mdCore principles:
- Always use mobile-first approach
- Follow standard breakpoints: sm (640px), md (768px), lg (1024px), xl (1280px)
- Use grid for card layouts:
grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 - Use flexbox for navigation:
flex flex-col md:flex-row - Scale typography:
text-sm md:text-base lg:text-lg
3. Tailwind CSS Patterns
When building with Tailwind CSS:
Read Tailwind patterns:
references/tailwind-patterns.mdCommon patterns to apply:
- Cards:
rounded-lg border bg-card text-card-foreground shadow-sm p-6 - Buttons:
inline-flex items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 - Inputs:
flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 - Dark mode:
bg-white dark:bg-slate-950 text-slate-900 dark:text-slate-50
4. shadcn/ui Components
When using shadcn/ui (recommended for most projects):
Read component reference:
references/shadcn-components.mdEssential components for todo apps:
npx shadcn-ui@latest add button card input form dialog badge tabs checkboxUse provided templates:
- Task card:
assets/todo-card-template.tsx - Task form:
assets/task-form-template.tsx
Pattern: Compose components
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
<Card>
<CardHeader>
<CardTitle>Title</CardTitle>
</CardHeader>
<CardContent>
<p>Content</p>
<Button>Action</Button>
</CardContent>
</Card>5. Material UI Implementation
When users choose Material UI:
Read MUI patterns:
references/material-ui-patterns.mdSetup theme first:
import { ThemeProvider, createTheme } from '@mui/material/styles'
const theme = createTheme({
palette: {
primary: { main: '#1976d2' },
},
})
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>Use sx prop for styling:
<Box sx={{ p: 2, mt: 4, borderRadius: 2 }}>6. Chakra UI Implementation
When users choose Chakra UI:
Read Chakra patterns:
references/chakra-ui-patterns.mdKey advantages:
- Style props:
<Box bg="blue.500" p={4} borderRadius="md"> - Dark mode built-in:
useColorMode(),useColorModeValue() - Responsive arrays:
<Box w={['100%', '50%', '33%']}>
Layout with Stack:
<VStack spacing={4} align="stretch">
<Card>Item 1</Card>
<Card>Item 2</Card>
</VStack>7. Form Design & Validation
When building forms with validation:
Use template:
assets/task-form-template.tsxPattern: React Hook Form + Zod
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
const schema = z.object({
title: z.string().min(3),
})
const form = useForm({
resolver: zodResolver(schema),
})Always include:
- Field labels (
<FormLabel>) - Error messages (
<FormMessage>) - Helper text (
<FormDescription>) - Disabled states for loading
- Proper accessibility attributes
8. Accessibility Patterns
Always apply:
- Semantic HTML:
<button>not<div onClick> - ARIA labels:
aria-label="Delete task" - Screen reader text:
<span className="sr-only">Hidden</span> - Keyboard navigation: Focus states, tab order
- Color contrast: WCAG AA minimum (4.5:1)
- Touch targets: Minimum 44x44px
9. Dark Mode Implementation
Tailwind approach:
<div className="bg-white dark:bg-slate-950">
<p className="text-slate-900 dark:text-slate-50">
// Theme toggle
import { useTheme } from "next-themes"
const { theme, setTheme } = useTheme()Chakra UI approach:
const { colorMode, toggleColorMode } = useColorMode()
const bg = useColorModeValue('white', 'gray.800')Material UI approach:
const theme = createTheme({
palette: {
mode: prefersDarkMode ? 'dark' : 'light',
},
})10. Component Templates
When building todo apps or similar UIs:
Available templates: 1. Task Card (assets/todo-card-template.tsx)
- Includes: Checkbox, priority badges, tags, due dates, dropdown menu
- Variants: Card view, list item view
- Features: Hover states, accessibility
2. Task Form (assets/task-form-template.tsx)
- Includes: Title, description, priority, due date, tags
- Validation: React Hook Form + Zod
- Variants: Dialog form, inline form
Usage:
# Read template
Read: assets/todo-card-template.tsx
# Adapt to project's design system
# Modify imports and styling as neededWorkflow Guide
For New Projects
1. Select Design System
- Read:
references/design-system-comparison.md - Consider: Bundle size, customization needs, team experience
- Provide recommendation with rationale
2. Setup Theme/Config
- Install dependencies
- Configure theme (colors, typography, spacing)
- Setup dark mode if needed
3. Build Core Components
- Start with layout (Container, Grid)
- Add navigation
- Implement forms
- Create card/list components
4. Apply Responsive Patterns
- Read:
references/responsive-design-patterns.md - Mobile-first breakpoints
- Test at key viewports
5. Add Accessibility
- Semantic HTML
- ARIA labels
- Keyboard navigation
- Screen reader support
For Todo App Specifically
1. Choose design system (recommend shadcn/ui) 2. Install core components:
npx shadcn-ui@latest add button card input form dialog badge tabs checkbox3. Use templates:
- Copy
assets/todo-card-template.tsx - Copy
assets/task-form-template.tsx
4. Customize colors/spacing to match brand 5. Add responsive layouts from references/responsive-design-patterns.md 6. Implement dark mode
For Existing Projects
1. Audit current design system 2. Identify pain points (bundle size, customization limits, etc.) 3. If migrating:
- Read comparison guide for alternatives
- Plan incremental migration
- Start with new components
4. If optimizing:
- Apply patterns from references
- Improve responsiveness
- Add accessibility features
Quick Reference
Common Tasks
"Make this responsive" → Read: references/responsive-design-patterns.md → Apply mobile-first breakpoints → Use grid/flexbox patterns
"Add dark mode" → For Tailwind: Use dark: prefix → For Chakra: Use useColorMode() → For MUI: Configure theme palette mode
"Which UI library?" → Read: references/design-system-comparison.md → Provide recommendation based on requirements
"Build a form with validation" → Use: assets/task-form-template.tsx → Adapt to project's design system → Add custom fields as needed
"Create task card" → Use: assets/todo-card-template.tsx → Modify for specific features
Design Tokens Reference
Spacing scale: 2, 4, 6, 8, 12, 16, 24, 32, 48, 64 (px in 4px increments)
Typography scale: xs (12px), sm (14px), base (16px), lg (18px), xl (20px), 2xl (24px), 3xl (30px)
Breakpoints: sm (640px), md (768px), lg (1024px), xl (1280px), 2xl (1536px)
Border radius: sm (4px), md (8px), lg (16px), full (9999px)
Anti-Patterns to Avoid
❌ Desktop-first responsive design (always start mobile) ❌ Hardcoded colors (use design tokens/theme) ❌ Div soup (use semantic HTML) ❌ Missing accessibility (always include ARIA, focus states) ❌ Inconsistent spacing (use design system scale) ❌ Non-interactive elements with onClick (use button/a) ❌ Tiny touch targets on mobile (<44px)
Resources Summary
- Tailwind patterns:
references/tailwind-patterns.md - shadcn/ui components:
references/shadcn-components.md - Material UI patterns:
references/material-ui-patterns.md - Chakra UI patterns:
references/chakra-ui-patterns.md - Design system comparison:
references/design-system-comparison.md - Responsive patterns:
references/responsive-design-patterns.md - Task card template:
assets/todo-card-template.tsx - Task form template:
assets/task-form-template.tsx
// Task Form Component Template with Validation
// Uses: React Hook Form + Zod + shadcn/ui
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { Button } from "@/components/ui/button"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Calendar } from "@/components/ui/calendar"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { CalendarIcon } from "lucide-react"
import { format } from "date-fns"
// Validation schema
const taskFormSchema = z.object({
title: z.string()
.min(3, "Title must be at least 3 characters")
.max(100, "Title must be less than 100 characters"),
description: z.string()
.max(500, "Description must be less than 500 characters")
.optional(),
priority: z.enum(["high", "medium", "low"]).optional(),
dueDate: z.date().optional(),
tags: z.string().optional(), // Comma-separated tags
})
type TaskFormValues = z.infer<typeof taskFormSchema>
interface TaskFormProps {
open: boolean
onOpenChange: (open: boolean) => void
onSubmit: (values: TaskFormValues) => void
initialValues?: Partial<TaskFormValues>
mode?: 'create' | 'edit'
}
export function TaskFormDialog({
open,
onOpenChange,
onSubmit,
initialValues,
mode = 'create'
}: TaskFormProps) {
const form = useForm<TaskFormValues>({
resolver: zodResolver(taskFormSchema),
defaultValues: {
title: initialValues?.title || "",
description: initialValues?.description || "",
priority: initialValues?.priority || "medium",
dueDate: initialValues?.dueDate,
tags: initialValues?.tags || "",
},
})
const handleSubmit = (values: TaskFormValues) => {
onSubmit(values)
form.reset()
onOpenChange(false)
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[525px]">
<DialogHeader>
<DialogTitle>
{mode === 'create' ? 'Create New Task' : 'Edit Task'}
</DialogTitle>
<DialogDescription>
{mode === 'create'
? 'Add a new task to your list. Fill in the details below.'
: 'Update the task details below.'}
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
{/* Title Field */}
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Title *</FormLabel>
<FormControl>
<Input placeholder="Enter task title" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Description Field */}
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormControl>
<Textarea
placeholder="Add more details about this task"
className="resize-none"
rows={3}
{...field}
/>
</FormControl>
<FormDescription>
Optional: Provide additional context
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
{/* Priority Field */}
<FormField
control={form.control}
name="priority"
render={({ field }) => (
<FormItem>
<FormLabel>Priority</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select priority" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="high">High</SelectItem>
<SelectItem value="medium">Medium</SelectItem>
<SelectItem value="low">Low</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
{/* Due Date Field */}
<FormField
control={form.control}
name="dueDate"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Due Date</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
className={`w-full pl-3 text-left font-normal ${
!field.value && "text-muted-foreground"
}`}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
disabled={(date) =>
date < new Date(new Date().setHours(0, 0, 0, 0))
}
initialFocus
/>
</PopoverContent>
</Popover>
<FormDescription>
Optional: Set a deadline for this task
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
{/* Tags Field */}
<FormField
control={form.control}
name="tags"
render={({ field }) => (
<FormItem>
<FormLabel>Tags</FormLabel>
<FormControl>
<Input placeholder="work, personal, urgent" {...field} />
</FormControl>
<FormDescription>
Separate tags with commas
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => {
form.reset()
onOpenChange(false)
}}
>
Cancel
</Button>
<Button type="submit">
{mode === 'create' ? 'Create Task' : 'Save Changes'}
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
)
}
// Inline form version (without dialog)
export function TaskForm({ onSubmit, initialValues, mode = 'create' }: Omit<TaskFormProps, 'open' | 'onOpenChange'>) {
const form = useForm<TaskFormValues>({
resolver: zodResolver(taskFormSchema),
defaultValues: {
title: initialValues?.title || "",
description: initialValues?.description || "",
priority: initialValues?.priority || "medium",
dueDate: initialValues?.dueDate,
tags: initialValues?.tags || "",
},
})
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
{/* Same form fields as above */}
{/* ... */}
<Button type="submit" className="w-full">
{mode === 'create' ? 'Add Task' : 'Update Task'}
</Button>
</form>
</Form>
)
}
// Task Card Component Template
// Supports: shadcn/ui, Material UI, Chakra UI
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Checkbox } from "@/components/ui/checkbox"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { MoreVertical, Trash2, Edit, Check } from "lucide-react"
interface Task {
id: string
title: string
description?: string
completed: boolean
priority?: 'high' | 'medium' | 'low'
tags?: string[]
dueDate?: Date
}
interface TaskCardProps {
task: Task
onToggleComplete?: (id: string) => void
onEdit?: (id: string) => void
onDelete?: (id: string) => void
}
export function TaskCard({ task, onToggleComplete, onEdit, onDelete }: TaskCardProps) {
const priorityColors = {
high: 'destructive',
medium: 'default',
low: 'secondary'
} as const
return (
<Card className="group hover:shadow-md transition-shadow">
<CardContent className="pt-6">
<div className="flex items-start gap-3">
{/* Checkbox */}
<Checkbox
id={`task-${task.id}`}
checked={task.completed}
onCheckedChange={() => onToggleComplete?.(task.id)}
className="mt-1"
/>
{/* Content */}
<div className="flex-1 min-w-0">
<label
htmlFor={`task-${task.id}`}
className={`text-base font-medium cursor-pointer block ${
task.completed ? 'line-through text-muted-foreground' : ''
}`}
>
{task.title}
</label>
{task.description && (
<p className="text-sm text-muted-foreground mt-1 line-clamp-2">
{task.description}
</p>
)}
{/* Meta information */}
<div className="flex flex-wrap gap-2 mt-3">
{task.priority && (
<Badge variant={priorityColors[task.priority]}>
{task.priority}
</Badge>
)}
{task.tags?.map(tag => (
<Badge key={tag} variant="outline">
{tag}
</Badge>
))}
{task.dueDate && (
<Badge variant="outline" className="text-xs">
Due: {new Date(task.dueDate).toLocaleDateString()}
</Badge>
)}
</div>
</div>
{/* Actions */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="opacity-0 group-hover:opacity-100 transition-opacity"
>
<MoreVertical className="h-4 w-4" />
<span className="sr-only">Open menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{!task.completed && (
<DropdownMenuItem onClick={() => onToggleComplete?.(task.id)}>
<Check className="mr-2 h-4 w-4" />
Mark Complete
</DropdownMenuItem>
)}
<DropdownMenuItem onClick={() => onEdit?.(task.id)}>
<Edit className="mr-2 h-4 w-4" />
Edit
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => onDelete?.(task.id)}
className="text-destructive"
>
<Trash2 className="mr-2 h-4 w-4" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</CardContent>
</Card>
)
}
// Alternative: Compact List Item Version
export function TaskListItem({ task, onToggleComplete, onEdit, onDelete }: TaskCardProps) {
return (
<div className="flex items-center gap-3 py-3 px-4 hover:bg-accent/50 rounded-lg transition-colors group">
<Checkbox
checked={task.completed}
onCheckedChange={() => onToggleComplete?.(task.id)}
/>
<div className="flex-1 min-w-0">
<span className={task.completed ? 'line-through text-muted-foreground' : ''}>
{task.title}
</span>
</div>
{task.priority && (
<Badge variant={task.priority === 'high' ? 'destructive' : 'secondary'} className="shrink-0">
{task.priority}
</Badge>
)}
<div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
<Button variant="ghost" size="icon" onClick={() => onEdit?.(task.id)}>
<Edit className="h-4 w-4" />
</Button>
<Button variant="ghost" size="icon" onClick={() => onDelete?.(task.id)}>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</div>
)
}
Chakra UI Design Patterns
Installation
npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion
npm install @chakra-ui/iconsProvider Setup
import { ChakraProvider, extendTheme } from '@chakra-ui/react'
const theme = extendTheme({
colors: {
brand: {
50: '#e3f2fd',
500: '#2196f3',
900: '#0d47a1',
},
},
})
function App() {
return (
<ChakraProvider theme={theme}>
{/* Your app */}
</ChakraProvider>
)
}Common Components
Button
import { Button } from '@chakra-ui/react'
<Button colorScheme="blue">Blue Button</Button>
<Button colorScheme="green">Green Button</Button>
<Button colorScheme="red">Red Button</Button>
<Button variant="solid">Solid</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
<Button size="xs">Extra Small</Button>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>Card
import { Card, CardHeader, CardBody, CardFooter, Heading, Text } from '@chakra-ui/react'
<Card>
<CardHeader>
<Heading size="md">Card Title</Heading>
</CardHeader>
<CardBody>
<Text>Card content</Text>
</CardBody>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>Input
import { Input, FormControl, FormLabel, FormHelperText } from '@chakra-ui/react'
<FormControl>
<FormLabel>Task Title</FormLabel>
<Input placeholder="Enter task title" />
<FormHelperText>Required field</FormHelperText>
</FormControl>
<Input variant="outline" />
<Input variant="filled" />
<Input variant="flushed" />Modal
import { Modal, ModalOverlay, ModalContent, ModalHeader, ModalFooter, ModalBody, ModalCloseButton, useDisclosure } from '@chakra-ui/react'
function MyModal() {
const { isOpen, onOpen, onClose } = useDisclosure()
return (
<>
<Button onClick={onOpen}>Open Modal</Button>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Modal Title</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text>Modal content</Text>
</ModalBody>
<ModalFooter>
<Button onClick={onClose}>Close</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
)
}Badge
import { Badge } from '@chakra-ui/react'
<Badge>Default</Badge>
<Badge colorScheme="green">Success</Badge>
<Badge colorScheme="red">Error</Badge>
<Badge variant="solid" colorScheme="blue">Solid</Badge>
<Badge variant="outline" colorScheme="blue">Outline</Badge>Tabs
import { Tabs, TabList, TabPanels, Tab, TabPanel } from '@chakra-ui/react'
<Tabs>
<TabList>
<Tab>All</Tab>
<Tab>Active</Tab>
<Tab>Completed</Tab>
</TabList>
<TabPanels>
<TabPanel>All tasks</TabPanel>
<TabPanel>Active tasks</TabPanel>
<TabPanel>Completed tasks</TabPanel>
</TabPanels>
</Tabs>Checkbox
import { Checkbox, CheckboxGroup, Stack } from '@chakra-ui/react'
<Checkbox defaultChecked>Checkbox</Checkbox>
<Checkbox colorScheme="green">Green</Checkbox>
<Checkbox isDisabled>Disabled</Checkbox>
<CheckboxGroup>
<Stack>
<Checkbox value="1">Option 1</Checkbox>
<Checkbox value="2">Option 2</Checkbox>
</Stack>
</CheckboxGroup>Select
import { Select } from '@chakra-ui/react'
<Select placeholder="Select priority">
<option value="high">High</option>
<option value="medium">Medium</option>
<option value="low">Low</option>
</Select>Toast
import { useToast } from '@chakra-ui/react'
function MyComponent() {
const toast = useToast()
return (
<Button onClick={() => {
toast({
title: 'Success',
description: 'Task created successfully',
status: 'success',
duration: 3000,
isClosable: true,
})
}}>
Show Toast
</Button>
)
}Menu (Dropdown)
import { Menu, MenuButton, MenuList, MenuItem, IconButton } from '@chakra-ui/react'
import { ChevronDownIcon } from '@chakra-ui/icons'
<Menu>
<MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
Actions
</MenuButton>
<MenuList>
<MenuItem>Edit</MenuItem>
<MenuItem>Delete</MenuItem>
</MenuList>
</Menu>Layout Components
Container
import { Container } from '@chakra-ui/react'
<Container maxW="container.lg">
{/* Content */}
</Container>Stack Layouts
import { VStack, HStack, Stack } from '@chakra-ui/react'
// Vertical stack
<VStack spacing={4} align="stretch">
<Card>Item 1</Card>
<Card>Item 2</Card>
</VStack>
// Horizontal stack
<HStack spacing={4}>
<Button>Button 1</Button>
<Button>Button 2</Button>
</HStack>
// Responsive stack
<Stack direction={['column', 'row']} spacing={4}>
<Box>Item 1</Box>
<Box>Item 2</Box>
</Stack>Grid
import { Grid, GridItem } from '@chakra-ui/react'
<Grid templateColumns="repeat(3, 1fr)" gap={6}>
<GridItem>Item 1</GridItem>
<GridItem>Item 2</GridItem>
<GridItem>Item 3</GridItem>
</Grid>
// Responsive grid
<Grid templateColumns={['1fr', '1fr 1fr', '1fr 1fr 1fr']} gap={4}>
<GridItem>Item 1</GridItem>
<GridItem>Item 2</GridItem>
</Grid>Box (Flexible Container)
import { Box } from '@chakra-ui/react'
<Box
bg="blue.500"
color="white"
p={4}
borderRadius="md"
boxShadow="lg"
>
Content
</Box>Style Props (sx-like API)
<Box
w="300px"
h="200px"
bg="primary.500"
_hover={{ bg: 'primary.600' }}
borderRadius="lg"
p={6}
mt={4}
>
Content
</Box>Dark Mode
import { useColorMode, useColorModeValue, IconButton } from '@chakra-ui/react'
import { MoonIcon, SunIcon } from '@chakra-ui/icons'
function ThemeToggle() {
const { colorMode, toggleColorMode } = useColorMode()
return (
<IconButton
icon={colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
onClick={toggleColorMode}
aria-label="Toggle theme"
/>
)
}
// Color mode values
const bg = useColorModeValue('white', 'gray.800')
const color = useColorModeValue('black', 'white')Responsive Values
// Array syntax: [mobile, tablet, desktop]
<Box
fontSize={['sm', 'md', 'lg']}
px={[2, 4, 6]}
w={['100%', '80%', '60%']}
>
Responsive content
</Box>
// Object syntax
<Box
fontSize={{ base: 'sm', md: 'md', lg: 'lg' }}
px={{ base: 2, md: 4, lg: 6 }}
>
Responsive content
</Box>Design System Comparison & Selection Guide
Quick Comparison Table
| Design System | Bundle Size | Components | Customization | Learning Curve | TypeScript | Best For |
|---|---|---|---|---|---|---|
| shadcn/ui + Tailwind | ~50KB* | 40+ | Very High | Low | Excellent | Full control, modern apps |
| Chakra UI | ~150KB | 50+ | High | Low | Excellent | Quick prototyping, DX |
| Material UI | ~300KB | 100+ | Medium | Medium | Good | Enterprise, Google-like UI |
| Ant Design | ~600KB | 60+ | Medium | Medium | Excellent | Admin panels, data-heavy |
| Mantine | ~200KB | 100+ | High | Low | Excellent | TypeScript projects, forms |
| Headless UI | ~20KB | 10 | Complete | Medium | Excellent | Custom designs, accessibility |
*Only components you use
Decision Framework
Choose shadcn/ui + Tailwind when:
- ✅ You want full control over styling
- ✅ Bundle size is critical
- ✅ Using Next.js or React
- ✅ Team comfortable with Tailwind
- ✅ Need copy/paste components
- ✅ Want to customize everything
Example use cases: Modern SaaS apps, startups, custom designs
Choose Chakra UI when:
- ✅ Developer experience is priority
- ✅ Need quick prototyping
- ✅ Want style props API
- ✅ Dark mode is required
- ✅ Good defaults needed
- ✅ Accessibility matters
Example use cases: MVPs, internal tools, modern web apps
Choose Material UI when:
- ✅ Enterprise application
- ✅ Google Material Design aesthetic
- ✅ Need comprehensive components
- ✅ Large component library required
- ✅ Established design system
- ✅ Strong accessibility needs
Example use cases: Enterprise dashboards, admin panels, B2B apps
Choose Ant Design when:
- ✅ Data-heavy applications
- ✅ Complex forms
- ✅ Admin dashboards
- ✅ CRUD operations
- ✅ Table-heavy interfaces
- ✅ Asian market focus
Example use cases: Admin systems, data platforms, enterprise CRUD
Choose Mantine when:
- ✅ TypeScript-first project
- ✅ Complex form handling
- ✅ Need 100+ hooks
- ✅ Want beautiful defaults
- ✅ Developer experience matters
- ✅ Modern React patterns
Example use cases: TypeScript apps, form-heavy apps, modern SPAs
Choose Headless UI when:
- ✅ Complete styling freedom
- ✅ Using Tailwind CSS
- ✅ Minimal bundle size
- ✅ Accessibility required
- ✅ Custom design system
- ✅ Need unstyled primitives
Example use cases: Custom designs, Tailwind projects, unique branding
Feature Matrix
Component Coverage
| Feature | shadcn | Chakra | MUI | Ant | Mantine |
|---|---|---|---|---|---|
| Buttons | ✅ | ✅ | ✅ | ✅ | ✅ |
| Forms | ✅ | ✅ | ✅ | ✅ | ✅✅ |
| Tables | ⚠️ | ✅ | ✅ | ✅✅ | ✅ |
| Charts | ❌ | ⚠️ | ⚠️ | ✅ | ✅ |
| Date Pickers | ✅ | ⚠️ | ✅✅ | ✅ | ✅ |
| Data Grid | ❌ | ❌ | ✅✅ | ✅✅ | ✅ |
Legend: ✅✅ Excellent | ✅ Good | ⚠️ Basic | ❌ Not included
Developer Experience
| Aspect | shadcn | Chakra | MUI | Ant | Mantine |
|---|---|---|---|---|---|
| TypeScript | ✅✅ | ✅✅ | ✅ | ✅✅ | ✅✅ |
| Documentation | ✅✅ | ✅✅ | ✅✅ | ✅ | ✅✅ |
| Community | ✅ | ✅✅ | ✅✅ | ✅✅ | ✅ |
| Updates | ✅✅ | ✅ | ✅✅ | ✅ | ✅✅ |
| Learning Curve | Easy | Easy | Medium | Medium | Easy |
Performance Comparison
Initial Bundle Sizes (minified + gzipped)
Headless UI: ~20KB ████
shadcn/ui: ~50KB ██████████
Chakra UI: ~150KB ██████████████████████████████
Mantine: ~200KB ████████████████████████████████████████
Material UI: ~300KB ████████████████████████████████████████████████████████████
Ant Design: ~600KB ████████████████████████████████████████████████████████████████████████████████████████████████████████████████Runtime Performance
All modern libraries have excellent runtime performance. Differences are minimal for typical applications.
Ecosystem & Integration
Next.js Integration
- Excellent: shadcn/ui, Chakra UI, Mantine
- Good: Material UI (requires configuration)
- Fair: Ant Design (SSR quirks)
Tailwind Compatibility
- Native: shadcn/ui, Headless UI
- Compatible: Chakra UI (via @chakra-ui/styled-system)
- Separate: Material UI, Ant Design, Mantine
Form Libraries
- React Hook Form: All compatible
- Formik: All compatible
- Built-in: Mantine (excellent), Ant Design (good)
Migration Difficulty
From shadcn/ui to:
- Chakra UI: Medium (style props → components)
- Material UI: Hard (different patterns)
- Ant Design: Hard (different patterns)
From Material UI to:
- shadcn/ui: Hard (component → utility classes)
- Chakra UI: Medium (similar patterns)
- Ant Design: Medium (similar component APIs)
From Chakra UI to:
- shadcn/ui: Medium (components → Tailwind)
- Material UI: Medium (style props → sx)
- Mantine: Easy (very similar APIs)
Recommendations by Project Type
Todo App (Hackathon)
Recommended: shadcn/ui + Tailwind
- Fast development
- Small bundle
- Modern aesthetic
- Full customization
Alternative: Chakra UI
- Even faster prototyping
- Great defaults
- Built-in dark mode
Enterprise Dashboard
Recommended: Material UI or Ant Design
- Comprehensive components
- Data tables built-in
- Professional look
- Enterprise support
Startup SaaS
Recommended: shadcn/ui + Tailwind
- Modern design
- Full control
- Small bundle
- Easy customization
Admin Panel
Recommended: Ant Design or Material UI
- Rich component library
- Form handling
- Data tables
- Professional aesthetic
Internal Tool
Recommended: Chakra UI or Mantine
- Fast development
- Good defaults
- Developer-friendly
- Quick prototyping
Installation Guides
shadcn/ui + Tailwind
npx shadcn-ui@latest init
npx shadcn-ui@latest add button card dialogChakra UI
npm install @chakra-ui/react @emotion/react @emotion/styled framer-motionMaterial UI
npm install @mui/material @emotion/react @emotion/styled
npm install @mui/icons-materialAnt Design
npm install antdMantine
npm install @mantine/core @mantine/hooksHeadless UI
npm install @headlessui/reactMaterial UI (MUI) Design Patterns
Installation
npm install @mui/material @emotion/react @emotion/styled
npm install @mui/icons-materialTheme Setup
import { ThemeProvider, createTheme } from '@mui/material/styles'
import CssBaseline from '@mui/material/CssBaseline'
const theme = createTheme({
palette: {
mode: 'light', // or 'dark'
primary: {
main: '#1976d2',
},
secondary: {
main: '#dc004e',
},
},
typography: {
fontFamily: 'Roboto, Arial, sans-serif',
},
})
function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
{/* Your app */}
</ThemeProvider>
)
}Common Components
Button
import Button from '@mui/material/Button'
<Button variant="contained">Contained</Button>
<Button variant="outlined">Outlined</Button>
<Button variant="text">Text</Button>
<Button color="primary">Primary</Button>
<Button color="secondary">Secondary</Button>
<Button color="error">Error</Button>
<Button color="success">Success</Button>
<Button size="small">Small</Button>
<Button size="medium">Medium</Button>
<Button size="large">Large</Button>Card
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import CardActions from '@mui/material/CardActions'
<Card sx={{ maxWidth: 345 }}>
<CardHeader
title="Card Title"
subheader="Subheader"
/>
<CardContent>
<Typography>Content here</Typography>
</CardContent>
<CardActions>
<Button size="small">Action</Button>
</CardActions>
</Card>TextField
import TextField from '@mui/material/TextField'
<TextField label="Task Title" variant="outlined" fullWidth />
<TextField label="Description" multiline rows={4} />
<TextField type="date" label="Due Date" InputLabelProps={{ shrink: true }} />Dialog
import Dialog from '@mui/material/Dialog'
import DialogTitle from '@mui/material/DialogTitle'
import DialogContent from '@mui/material/DialogContent'
import DialogActions from '@mui/material/DialogActions'
<Dialog open={open} onClose={handleClose}>
<DialogTitle>Dialog Title</DialogTitle>
<DialogContent>
<TextField label="Input" fullWidth />
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleSave}>Save</Button>
</DialogActions>
</Dialog>Chip (Badge)
import Chip from '@mui/material/Chip'
<Chip label="High Priority" color="error" />
<Chip label="Work" variant="outlined" />
<Chip label="Completed" color="success" onDelete={handleDelete} />List
import List from '@mui/material/List'
import ListItem from '@mui/material/ListItem'
import ListItemButton from '@mui/material/ListItemButton'
import ListItemIcon from '@mui/material/ListItemIcon'
import ListItemText from '@mui/material/ListItemText'
import Checkbox from '@mui/material/Checkbox'
<List>
{tasks.map((task) => (
<ListItem key={task.id}>
<ListItemButton>
<ListItemIcon>
<Checkbox edge="start" />
</ListItemIcon>
<ListItemText primary={task.title} secondary={task.description} />
</ListItemButton>
</ListItem>
))}
</List>Tabs
import Tabs from '@mui/material/Tabs'
import Tab from '@mui/material/Tab'
import Box from '@mui/material/Box'
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs value={value} onChange={handleChange}>
<Tab label="All" />
<Tab label="Active" />
<Tab label="Completed" />
</Tabs>
</Box>Date Picker
npm install @mui/x-date-pickersimport { DatePicker } from '@mui/x-date-pickers/DatePicker'
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Due Date"
value={date}
onChange={setDate}
/>
</LocalizationProvider>Select
import Select from '@mui/material/Select'
import MenuItem from '@mui/material/MenuItem'
import FormControl from '@mui/material/FormControl'
import InputLabel from '@mui/material/InputLabel'
<FormControl fullWidth>
<InputLabel>Priority</InputLabel>
<Select value={priority} onChange={handleChange} label="Priority">
<MenuItem value="high">High</MenuItem>
<MenuItem value="medium">Medium</MenuItem>
<MenuItem value="low">Low</MenuItem>
</Select>
</FormControl>Snackbar (Toast)
import Snackbar from '@mui/material/Snackbar'
import Alert from '@mui/material/Alert'
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
<Alert onClose={handleClose} severity="success">
Task created successfully!
</Alert>
</Snackbar>Layout Patterns
Container
import Container from '@mui/material/Container'
<Container maxWidth="lg">
{/* Content */}
</Container>Grid System
import Grid from '@mui/material/Grid'
<Grid container spacing={2}>
<Grid item xs={12} md={6} lg={4}>
<Card>Item 1</Card>
</Grid>
<Grid item xs={12} md={6} lg={4}>
<Card>Item 2</Card>
</Grid>
</Grid>Stack (Flexbox)
import Stack from '@mui/material/Stack'
<Stack direction="row" spacing={2}>
<Button>Button 1</Button>
<Button>Button 2</Button>
</Stack>
<Stack direction="column" spacing={3}>
<Card>Card 1</Card>
<Card>Card 2</Card>
</Stack>Styling with sx prop
<Box
sx={{
width: 300,
height: 300,
backgroundColor: 'primary.main',
'&:hover': {
backgroundColor: 'primary.dark',
},
borderRadius: 2,
p: 2, // padding: theme.spacing(2)
mt: 4, // marginTop: theme.spacing(4)
}}
>
Content
</Box>Dark Mode
import { useTheme } from '@mui/material/styles'
import useMediaQuery from '@mui/material/useMediaQuery'
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)')
const theme = createTheme({
palette: {
mode: prefersDarkMode ? 'dark' : 'light',
},
})Responsive Design Patterns & Best Practices
Mobile-First Philosophy
Always start with mobile layout and enhance for larger screens.
// ✅ Good: Mobile-first
<div className="w-full md:w-1/2 lg:w-1/3">
Mobile: 100% width
Tablet: 50% width
Desktop: 33% width
</div>
// ❌ Bad: Desktop-first requires overrides
<div className="w-1/3 lg:w-1/2 md:w-full">Common Breakpoints
// Tailwind breakpoints
sm: 640px // Phone landscape
md: 768px // Tablet
lg: 1024px // Laptop
xl: 1280px // Desktop
2xl: 1536px // Large desktop
// Material UI breakpoints
xs: 0px // Phone
sm: 600px // Tablet
md: 900px // Small laptop
lg: 1200px // Desktop
xl: 1536px // Large desktop
// Chakra UI breakpoints
base: 0px // Phone
sm: 30em // 480px
md: 48em // 768px
lg: 62em // 992px
xl: 80em // 1280px
2xl: 96em // 1536pxLayout Patterns
Container Pattern
// Responsive max-width container
<div className="container mx-auto px-4 md:px-6 lg:px-8 max-w-7xl">
{/* Content stays centered with appropriate padding */}
</div>
// Material UI
<Container maxWidth="lg" sx={{ px: { xs: 2, md: 3 } }}>
// Chakra UI
<Container maxW="container.lg" px={{ base: 4, md: 6 }}>Grid Layouts
Auto-Responsive Grid
// Automatically adjusts columns based on min-width
<div className="grid grid-cols-[repeat(auto-fit,minmax(300px,1fr))] gap-4">
{/* Items automatically wrap when screen < 300px per item */}
</div>Explicit Responsive Grid
// 1 → 2 → 3 → 4 columns
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
// Material UI
<Grid container spacing={2}>
<Grid item xs={12} sm={6} lg={4} xl={3}>
// Chakra UI
<Grid
templateColumns={{
base: '1fr',
sm: 'repeat(2, 1fr)',
lg: 'repeat(3, 1fr)'
}}
gap={4}
>Flexbox Patterns
Responsive Navigation
// Vertical on mobile, horizontal on desktop
<nav className="flex flex-col md:flex-row gap-4 items-start md:items-center">
<Logo />
<div className="flex flex-col md:flex-row gap-2 md:ml-auto">
<Button>Tasks</Button>
<Button>Settings</Button>
</div>
</nav>Responsive Card Layout
// Stack vertically on mobile, side-by-side on tablet+
<div className="flex flex-col md:flex-row gap-4">
<div className="flex-1">Content 1</div>
<div className="flex-1">Content 2</div>
</div>Typography Scaling
Fluid Typography
// Responsive text sizes
<h1 className="text-2xl md:text-3xl lg:text-4xl xl:text-5xl">
Heading scales with viewport
</h1>
// Body text
<p className="text-sm md:text-base lg:text-lg">
Paragraph text
</p>
// Material UI
<Typography
variant="h1"
sx={{
fontSize: { xs: '2rem', md: '3rem', lg: '4rem' }
}}
>
// Chakra UI
<Heading fontSize={{ base: '2xl', md: '3xl', lg: '4xl' }}>Clamp for Smooth Scaling
// CSS clamp: min, preferred, max
<h1 style={{ fontSize: 'clamp(1.5rem, 5vw, 3rem)' }}>
Smoothly scales between 1.5rem and 3rem
</h1>Spacing Patterns
Responsive Padding/Margin
// Increase spacing on larger screens
<div className="p-4 md:p-6 lg:p-8 xl:p-12">
{/* 16px → 24px → 32px → 48px padding */}
</div>
// Gap in flex/grid
<div className="flex gap-2 md:gap-4 lg:gap-6">Container Spacing
// Full-width on mobile, constrained on desktop
<div className="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">Component Patterns
Responsive Card
function ResponsiveCard({ task }) {
return (
<Card className="p-4 md:p-6">
<div className="flex flex-col md:flex-row gap-4">
{/* Content stacks on mobile, side-by-side on tablet+ */}
<div className="flex-1">
<h3 className="text-lg md:text-xl">{task.title}</h3>
<p className="text-sm md:text-base text-muted-foreground">
{task.description}
</p>
</div>
<div className="flex md:flex-col gap-2">
<Button size="sm" className="md:size-default">Complete</Button>
<Button variant="outline" size="sm">Edit</Button>
</div>
</div>
</Card>
)
}Responsive Table → Cards
// Desktop: Table
// Mobile: Card list
function ResponsiveTaskList({ tasks }) {
return (
<>
{/* Desktop table */}
<div className="hidden md:block">
<table className="w-full">
<thead>
<tr>
<th>Title</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{tasks.map(task => (
<tr key={task.id}>
<td>{task.title}</td>
<td>{task.status}</td>
<td><Button>Edit</Button></td>
</tr>
))}
</tbody>
</table>
</div>
{/* Mobile cards */}
<div className="md:hidden space-y-4">
{tasks.map(task => (
<Card key={task.id}>
<CardContent>
<h3>{task.title}</h3>
<Badge>{task.status}</Badge>
<Button className="mt-2">Edit</Button>
</CardContent>
</Card>
))}
</div>
</>
)
}Responsive Modal
// Full-screen on mobile, centered dialog on desktop
<Dialog>
<DialogContent className="w-full h-full md:h-auto md:max-w-2xl md:rounded-lg">
{/* Content */}
</DialogContent>
</Dialog>Media Queries in CSS-in-JS
Tailwind (with arbitrary values)
<div className="w-full [@media(min-width:900px)]:w-1/2">Material UI sx prop
<Box
sx={{
width: '100%',
'@media (min-width: 768px)': {
width: '50%'
}
}}
>Chakra UI
<Box
w="100%"
sx={{
'@media screen and (min-width: 768px)': {
w: '50%'
}
}}
>Image Optimization
Responsive Images
import Image from 'next/image'
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
priority
/>Picture Element
<picture>
<source media="(max-width: 768px)" srcSet="/mobile.jpg" />
<source media="(max-width: 1200px)" srcSet="/tablet.jpg" />
<img src="/desktop.jpg" alt="Responsive" />
</picture>Touch-Friendly Patterns
Minimum Touch Target Size
// 44x44px minimum (Apple HIG) or 48x48px (Material Design)
<button className="min-w-[44px] min-h-[44px] p-2">
<Icon />
</button>Swipe Gestures
import { useSwipeable } from 'react-swipeable'
const handlers = useSwipeable({
onSwipedLeft: () => deleteTask(),
onSwipedRight: () => completeTask(),
})
<div {...handlers} className="touch-pan-y">
Swipeable task
</div>Performance Considerations
Lazy Load Components
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <Skeleton />,
ssr: false
})
// Only load on larger screens
const DesktopFeature = dynamic(() => import('./DesktopFeature'), {
loading: () => null,
ssr: false
})
function App() {
const isDesktop = useMediaQuery('(min-width: 1024px)')
return (
<>
{isDesktop && <DesktopFeature />}
</>
)
}Responsive Loading
// Load smaller images on mobile
const imageSrc = useBreakpointValue({
base: '/image-mobile.jpg',
md: '/image-tablet.jpg',
lg: '/image-desktop.jpg'
})
<Image src={imageSrc} alt="Responsive" />Testing Responsive Layouts
Media Query Hooks
// Tailwind
import { useMediaQuery } from 'react-responsive'
const isMobile = useMediaQuery({ maxWidth: 767 })
const isTablet = useMediaQuery({ minWidth: 768, maxWidth: 1023 })
const isDesktop = useMediaQuery({ minWidth: 1024 })
// Material UI
import useMediaQuery from '@mui/material/useMediaQuery'
const isMobile = useMediaQuery('(max-width:767px)')
// Chakra UI
import { useBreakpointValue } from '@chakra-ui/react'
const columns = useBreakpointValue({ base: 1, md: 2, lg: 3 })Accessibility in Responsive Design
// Hide visually but keep for screen readers
<span className="sr-only md:not-sr-only">
Desktop label
</span>
// Skip to main content
<a href="#main" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4">
Skip to main content
</a>
// Responsive focus states
<button className="focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 md:focus:ring-offset-4">shadcn/ui Component Patterns
Installation
# Individual components (recommended)
npx shadcn-ui@latest add button
npx shadcn-ui@latest add card
npx shadcn-ui@latest add dialog
npx shadcn-ui@latest add input
npx shadcn-ui@latest add formEssential Components for Todo Apps
Button Component
import { Button } from "@/components/ui/button"
<Button variant="default">Default</Button>
<Button variant="destructive">Delete</Button>
<Button variant="outline">Outline</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
// Sizes
<Button size="default">Default</Button>
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>
<Button size="icon">Icon</Button>Card Component
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card"
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card description</CardDescription>
</CardHeader>
<CardContent>
<p>Card content</p>
</CardContent>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>Dialog Component
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from "@/components/ui/dialog"
<Dialog>
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogDescription>Dialog description</DialogDescription>
</DialogHeader>
{/* Content */}
<DialogFooter>
<Button>Save</Button>
</DialogFooter>
</DialogContent>
</Dialog>Form Components
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import * as z from "zod"
const formSchema = z.object({
title: z.string().min(3, "Title must be at least 3 characters"),
})
function MyForm() {
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: { title: "" }
})
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Title</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormDescription>Enter task title</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
)
}Badge Component
import { Badge } from "@/components/ui/badge"
<Badge>Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Destructive</Badge>
<Badge variant="outline">Outline</Badge>Tabs Component
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
<Tabs defaultValue="all">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="all">All</TabsTrigger>
<TabsTrigger value="active">Active</TabsTrigger>
<TabsTrigger value="completed">Completed</TabsTrigger>
</TabsList>
<TabsContent value="all">All tasks</TabsContent>
<TabsContent value="active">Active tasks</TabsContent>
<TabsContent value="completed">Completed tasks</TabsContent>
</Tabs>Calendar & Date Picker
import { Calendar } from "@/components/ui/calendar"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { format } from "date-fns"
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">
{date ? format(date, "PPP") : "Pick a date"}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar mode="single" selected={date} onSelect={setDate} />
</PopoverContent>
</Popover>Select Component
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
<Select>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select priority" />
</SelectTrigger>
<SelectContent>
<SelectItem value="high">High</SelectItem>
<SelectItem value="medium">Medium</SelectItem>
<SelectItem value="low">Low</SelectItem>
</SelectContent>
</Select>Checkbox Component
import { Checkbox } from "@/components/ui/checkbox"
<div className="flex items-center space-x-2">
<Checkbox id="terms" />
<label htmlFor="terms">Accept terms and conditions</label>
</div>Skeleton Loader
import { Skeleton } from "@/components/ui/skeleton"
<div className="space-y-4">
<Skeleton className="h-12 w-full" />
<Skeleton className="h-4 w-3/4" />
<Skeleton className="h-4 w-1/2" />
</div>Toast Notifications
import { useToast } from "@/components/ui/use-toast"
import { Toaster } from "@/components/ui/toaster"
function MyComponent() {
const { toast } = useToast()
return (
<>
<Button onClick={() => {
toast({
title: "Success",
description: "Task created successfully",
})
}}>
Show Toast
</Button>
<Toaster />
</>
)
}Dropdown Menu
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">Open Menu</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Recommended Components for Todo App
# Core essentials
npx shadcn-ui@latest add button card input label textarea
# Forms and validation
npx shadcn-ui@latest add form dialog alert-dialog
# UI enhancements
npx shadcn-ui@latest add tabs badge skeleton toast
# Advanced features
npx shadcn-ui@latest add calendar popover select checkbox dropdown-menuTailwind CSS Design Patterns
Responsive Design Principles
Mobile-First Breakpoints
// ✅ Good: Mobile-first, scale up
<div className="w-full md:w-1/2 lg:w-1/3 xl:w-1/4">
// Breakpoints:
// sm: 640px - phones landscape
// md: 768px - tablets
// lg: 1024px - laptops
// xl: 1280px - desktops
// 2xl: 1536px - large screensLayout Patterns
Grid Layouts
// Responsive grid: 1→2→3 columns
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
// Auto-fit grid (fills space)
<div className="grid grid-cols-[repeat(auto-fit,minmax(300px,1fr))] gap-4">Flexbox Patterns
// Responsive navigation
<nav className="flex flex-col md:flex-row items-start md:items-center gap-4">
<Logo />
<div className="flex gap-2 md:ml-auto">
<Button>Action</Button>
</div>
</nav>
// Center content
<div className="flex items-center justify-center min-h-screen">
// Space between
<div className="flex justify-between items-center">Design Tokens
Spacing Scale
const spacing = {
tight: "space-y-2", // 8px
normal: "space-y-4", // 16px
relaxed: "space-y-6", // 24px
loose: "space-y-8" // 32px
}Typography Scale
const text = {
xs: "text-xs", // 0.75rem
sm: "text-sm", // 0.875rem
base: "text-base", // 1rem
lg: "text-lg", // 1.125rem
xl: "text-xl", // 1.25rem
"2xl": "text-2xl" // 1.5rem
}Color Patterns
// Semantic colors
<Button className="bg-primary text-primary-foreground hover:bg-primary/90">
<Alert className="bg-destructive/10 text-destructive border-destructive/20">
// Opacity modifiers
<div className="bg-slate-900/95 backdrop-blur-sm">Component Patterns
Card Component
<div className="rounded-lg border bg-card text-card-foreground shadow-sm p-6">
<h3 className="text-2xl font-semibold leading-none tracking-tight">
Title
</h3>
<p className="text-sm text-muted-foreground mt-2">
Description
</p>
</div>Button Variants
// Primary
<button className="inline-flex items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90">
// Outline
<button className="border border-input bg-background hover:bg-accent hover:text-accent-foreground">
// Ghost
<button className="hover:bg-accent hover:text-accent-foreground">Input Fields
<input className="flex h-10 w-full rounded-md border border-input 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" />Animations & Transitions
// Smooth transitions
<button className="transition-all duration-200 hover:scale-105">
// Fade in
<div className="animate-in fade-in duration-300">
// Slide in from bottom
<div className="animate-in slide-in-from-bottom-4 duration-500">Accessibility Patterns
// Screen reader only
<span className="sr-only">Hidden text for screen readers</span>
// Focus visible
<button className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
// Disabled states
<button className="disabled:cursor-not-allowed disabled:opacity-50">Dark Mode Support
// Color scheme variants
<div className="bg-white dark:bg-slate-950">
<p className="text-slate-900 dark:text-slate-50">
// Theme-aware icons
<SunIcon className="rotate-0 scale-100 dark:-rotate-90 dark:scale-0" />
<MoonIcon className="rotate-90 scale-0 dark:rotate-0 dark:scale-100" />