@ez-cook/nano-peaces 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@ez-cook/nano-peaces",
3
+ "version": "0.1.0",
4
+ "description": "The missing UI skills pack for AI agents",
5
+ "type": "module",
6
+ "bin": {
7
+ "nano-peaces": "./dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "files": [
12
+ "dist",
13
+ "skills",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsup && node scripts/copy-skills.js",
18
+ "dev": "tsup --watch",
19
+ "typecheck": "tsc --noEmit"
20
+ },
21
+ "dependencies": {
22
+ "commander": "^13.0.0",
23
+ "@clack/prompts": "^0.9.0",
24
+ "fast-glob": "^3.3.0",
25
+ "fs-extra": "^11.2.0"
26
+ },
27
+ "devDependencies": {
28
+ "typescript": "^5.7.0",
29
+ "tsup": "^8.4.0",
30
+ "@types/fs-extra": "^11.0.0",
31
+ "@types/node": "^22.0.0"
32
+ },
33
+ "keywords": [
34
+ "ai",
35
+ "agent",
36
+ "ui",
37
+ "skills",
38
+ "shadcn",
39
+ "cli",
40
+ "antigravity",
41
+ "claude",
42
+ "cursor",
43
+ "ez-cook",
44
+ "nano-peaces"
45
+ ],
46
+ "license": "MIT",
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "https://github.com/ez-cook/nano-peaces"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ }
54
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@ez-cook/skills",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "Skill content for nano-peaces — source of truth",
6
+ "scripts": {
7
+ "typecheck": "echo 'No TypeScript in skills package'"
8
+ },
9
+ "license": "MIT"
10
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "version": "0.1.0",
3
+ "skills": [
4
+ {
5
+ "id": "shadcn-ui",
6
+ "description": "Expert shadcn/ui knowledge for AI agents (2026)",
7
+ "signals": {
8
+ "dependencies": [
9
+ "radix-ui",
10
+ "@radix-ui/*",
11
+ "@base-ui-components/*",
12
+ "tailwindcss",
13
+ "class-variance-authority",
14
+ "tailwind-merge",
15
+ "clsx",
16
+ "lucide-react",
17
+ "@tabler/icons-react",
18
+ "tw-animate-css"
19
+ ],
20
+ "filePatterns": ["**/components/ui/**", "**/ui/*.tsx", "**/ui/*.jsx", "components.json"],
21
+ "keywords": [
22
+ "shadcn",
23
+ "button variant",
24
+ "dialog",
25
+ "sheet",
26
+ "command palette",
27
+ "combobox",
28
+ "data table",
29
+ "form validation",
30
+ "sidebar",
31
+ "dropdown",
32
+ "field component",
33
+ "sonner toast",
34
+ "asChild",
35
+ "cn() utility"
36
+ ],
37
+ "taskIntent": [
38
+ "create component",
39
+ "build form",
40
+ "add UI element",
41
+ "style component",
42
+ "add dialog",
43
+ "create table",
44
+ "setup shadcn",
45
+ "add shadcn component",
46
+ "add sidebar",
47
+ "dark mode",
48
+ "build data table",
49
+ "add toast notification"
50
+ ]
51
+ },
52
+ "priority": 1,
53
+ "skillPath": "shadcn-ui/SKILL.md"
54
+ }
55
+ ]
56
+ }
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: shadcn-ui
3
+ description: 'Expert knowledge for building UI with shadcn/ui (2026). Activated when: project uses radix-ui or @base-ui-components/*, tailwindcss. Covers: component generation, theming, forms (Field+Controller pattern), data tables (TanStack Table), sidebar, accessibility, RTL, MCP, registry.'
4
+ metadata:
5
+ version: 1.0.0
6
+ tags: [ui, react, tailwind, shadcn, components, radix, base-ui, field, registry]
7
+ requires: [react, tailwindcss]
8
+ ---
9
+
10
+ # shadcn/ui Skill
11
+
12
+ ## Mental Model
13
+
14
+ shadcn/ui is NOT a library. It's a code distribution platform.
15
+ You COPY components into your project and OWN them.
16
+ Never `npm install shadcn` — use `npx shadcn@latest add [component]`.
17
+ Since Feb 2026: imports use unified `radix-ui` (not `@radix-ui/react-*`).
18
+ Dual primitive support: Radix UI or Base UI — same API, different engine.
19
+
20
+ ## Context Router
21
+
22
+ | User Intent | Load Chunk | When |
23
+ | ---------------------------- | ------------------------------- | ---------------------------------------------------- |
24
+ | Create button, card, badge | chunks/components/button.md | Mentions specific component name |
25
+ | Build form with validation | chunks/components/form.md | Mentions form, input, validation, zod, Field |
26
+ | Add data table | chunks/components/data-table.md | Mentions table, sorting, filtering, pagination |
27
+ | Add dialog, sheet, drawer | chunks/components/dialog.md | Mentions modal, popup, overlay, sheet |
28
+ | Build sidebar / navigation | chunks/components/sidebar.md | Mentions sidebar, nav, collapsible menu |
29
+ | Setup/install shadcn | chunks/installation.md | New project or first shadcn mention |
30
+ | Theme, dark mode, colors | chunks/theming.md | Mentions theme, dark, colors, CSS variables, oklch |
31
+ | components.json config | chunks/config.md | Mentions config, aliases, registry, monorepo setup |
32
+ | Breadcrumb, tabs, pagination | chunks/patterns/navigation.md | Mentions breadcrumb, tabs, nav-menu, pagination |
33
+ | cn(), asChild, cva, extend | chunks/patterns/composition.md | Mentions composition, variants, extending components |
34
+ | Something went wrong | chunks/anti-patterns.md | User reports bug or unexpected behavior |
35
+
36
+ ## Quick Reference (Top Patterns)
37
+
38
+ ### Add a component
39
+
40
+ ```bash
41
+ npx shadcn@latest add button
42
+ ```
43
+
44
+ ### New project (choose Radix or Base UI)
45
+
46
+ ```bash
47
+ npx shadcn@latest create
48
+ ```
49
+
50
+ ### Button variants & sizes
51
+
52
+ ```tsx
53
+ <Button variant="default" /> // primary
54
+ <Button variant="destructive" /> // danger
55
+ <Button variant="outline" /> // bordered
56
+ <Button variant="secondary" /> // muted
57
+ <Button variant="ghost" /> // no border
58
+ <Button variant="link" /> // text link
59
+
60
+ // sizes: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg"
61
+ <Button size="sm">Small</Button>
62
+ ```
63
+
64
+ ### cn() utility (ALWAYS use this for conditional classes)
65
+
66
+ ```tsx
67
+ import { cn } from '@/lib/utils'
68
+ ;<div className={cn('base-class', condition && 'conditional-class')} />
69
+ ```
70
+
71
+ ### Icon spacing (REQUIRED for icons in buttons/menu items)
72
+
73
+ ```tsx
74
+ <Button>
75
+ <IconGitBranch data-icon="inline-start" /> New Branch
76
+ </Button>
77
+ ```
78
+
79
+ ### Imports (2026 — unified package)
80
+
81
+ ```tsx
82
+ // ✅ Correct (2026)
83
+ import { Dialog as DialogPrimitive } from 'radix-ui'
84
+ // ❌ Deprecated
85
+ import * as DialogPrimitive from '@radix-ui/react-dialog'
86
+ ```
87
+
88
+ ## Critical Rules
89
+
90
+ 1. NEVER install shadcn as npm dependency — always use CLI `add`
91
+ 2. ALWAYS use `cn()` for class merging (not template literals)
92
+ 3. ALWAYS check components.json for project config before generating
93
+ 4. Components go in the path specified by components.json `aliases.ui`
94
+ 5. Use `asChild` prop (Radix Slot) for polymorphic rendering
95
+ 6. Import from `radix-ui` unified package (not `@radix-ui/react-*`)
96
+ 7. Use `data-icon="inline-start"|"inline-end"` on icons inside buttons
97
+ 8. Use `data-invalid` on Field + `aria-invalid` on controls for error states
98
+ 9. Tailwind v4: button has `cursor: default` — add CSS override if need pointer
99
+ 10. `use client` directive required for interactive components when RSC is enabled
@@ -0,0 +1,97 @@
1
+ # Anti-Patterns & Common Mistakes — shadcn/ui
2
+
3
+ ## Critical Anti-Patterns Table
4
+
5
+ | ❌ Mistake | ✅ Correct | Why |
6
+ | -------------------------------------- | --------------------------------------------- | --------------------------------------------- |
7
+ | `npm install shadcn` | `npx shadcn@latest add button` | shadcn is a CLI tool, not an npm package |
8
+ | `import from "@radix-ui/react-dialog"` | `import { Dialog } from "radix-ui"` | Unified package since Feb 2026 |
9
+ | `<Form><FormField>` pattern | `<Controller><Field>` pattern | Old form API deprecated in 2026 |
10
+ | Template literal class merge | `cn()` utility | tailwind-merge handles class conflicts |
11
+ | Hardcode `@/components/ui` | Read `aliases.ui` from components.json | Path varies per project configuration |
12
+ | Skip `"use client"` | Check `rsc` in components.json | RSC won't render interactive components |
13
+ | Style `default` | Style `new-york` or nova variants | `default` style is deprecated |
14
+ | Manual validation in form | Zod schema + zodResolver | Standard Schema + react-hook-form integration |
15
+ | `cursor: pointer` assumed | Tailwind v4 uses `cursor: default` on buttons | Add CSS override if needed |
16
+ | Skip `data-invalid`/`aria-invalid` | Required for accessible error states | a11y compliance |
17
+
18
+ ## Deprecated Patterns (2026)
19
+
20
+ ### Old Radix Imports
21
+
22
+ ```tsx
23
+ // ❌ Deprecated
24
+ import * as DialogPrimitive from '@radix-ui/react-dialog'
25
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
26
+
27
+ // ✅ Correct — unified package
28
+ import { Dialog as DialogPrimitive } from 'radix-ui'
29
+ import { DropdownMenu as DropdownMenuPrimitive } from 'radix-ui'
30
+
31
+ // Migration command:
32
+ // npx shadcn@latest migrate radix
33
+ ```
34
+
35
+ ### Old Form Pattern
36
+
37
+ ```tsx
38
+ // ❌ Deprecated
39
+ <Form {...form}>
40
+ <FormField
41
+ control={form.control}
42
+ name="username"
43
+ render={({ field }) => (
44
+ <FormItem>
45
+ <FormLabel>Username</FormLabel>
46
+ <FormControl><Input {...field} /></FormControl>
47
+ <FormMessage />
48
+ </FormItem>
49
+ )}
50
+ />
51
+ </Form>
52
+
53
+ // ✅ Correct (2026)
54
+ <form onSubmit={form.handleSubmit(onSubmit)}>
55
+ <Controller
56
+ name="username"
57
+ control={form.control}
58
+ render={({ field, fieldState }) => (
59
+ <Field data-invalid={fieldState.invalid || undefined}>
60
+ <FieldLabel>Username</FieldLabel>
61
+ <Input {...field} aria-invalid={fieldState.invalid || undefined} />
62
+ {fieldState.error && <FieldError errors={[fieldState.error]} />}
63
+ </Field>
64
+ )}
65
+ />
66
+ </form>
67
+ ```
68
+
69
+ ## Common Runtime Errors
70
+
71
+ ### Missing "use client"
72
+
73
+ **Error:** Component renders as empty or hydration mismatch
74
+ **Fix:** Add `"use client"` at top of file for any component using hooks, event handlers, or browser APIs
75
+
76
+ ### Wrong Alias Path
77
+
78
+ **Error:** `Module not found: Can't resolve '@/components/ui/button'`
79
+ **Fix:** Check `components.json` → `aliases.ui` matches your tsconfig paths
80
+
81
+ ### Missing DialogTitle
82
+
83
+ **Error:** Console warning about missing accessible name
84
+ **Fix:** Always include `<DialogTitle>` (use `<VisuallyHidden>` if you don't want visible title)
85
+
86
+ ### className Conflicts
87
+
88
+ **Error:** Styles not applying or overriding unexpectedly
89
+ **Fix:** Always use `cn()` to merge classes — never concatenate strings
90
+
91
+ ## Migration Commands
92
+
93
+ ```bash
94
+ npx shadcn@latest migrate radix # @radix-ui/react-* → radix-ui
95
+ npx shadcn@latest migrate rtl # physical → logical CSS classes
96
+ npx shadcn@latest migrate icons # switch icon library
97
+ ```
@@ -0,0 +1,96 @@
1
+ # Button — shadcn/ui
2
+
3
+ ## Install
4
+
5
+ ```bash
6
+ npx shadcn@latest add button
7
+ ```
8
+
9
+ ## Variants
10
+
11
+ ```tsx
12
+ <Button variant="default" /> // primary — solid background
13
+ <Button variant="destructive" /> // danger — red
14
+ <Button variant="outline" /> // bordered — transparent bg
15
+ <Button variant="secondary" /> // muted — subtle background
16
+ <Button variant="ghost" /> // no border, hover only
17
+ <Button variant="link" /> // looks like a text link
18
+ ```
19
+
20
+ ## Sizes
21
+
22
+ ```tsx
23
+ <Button size="default" /> // h-9 px-4 py-2
24
+ <Button size="xs" /> // h-7 px-2.5 text-xs
25
+ <Button size="sm" /> // h-8 px-3
26
+ <Button size="lg" /> // h-10 px-6
27
+ <Button size="icon" /> // h-9 w-9 (square)
28
+ <Button size="icon-xs" /> // h-7 w-7
29
+ <Button size="icon-sm" /> // h-8 w-8
30
+ <Button size="icon-lg" /> // h-10 w-10
31
+ ```
32
+
33
+ ## Icon Usage (REQUIRED pattern)
34
+
35
+ ```tsx
36
+ // data-icon adds correct spacing automatically
37
+ <Button>
38
+ <IconPlus data-icon="inline-start" />
39
+ Add Item
40
+ </Button>
41
+
42
+ <Button>
43
+ Settings
44
+ <IconChevronRight data-icon="inline-end" />
45
+ </Button>
46
+
47
+ // Icon-only button
48
+ <Button size="icon" aria-label="Settings">
49
+ <IconSettings />
50
+ </Button>
51
+ ```
52
+
53
+ ## Button as Link (asChild)
54
+
55
+ ```tsx
56
+ <Button asChild>
57
+ <Link href="/dashboard">Go to Dashboard</Link>
58
+ </Button>
59
+ ```
60
+
61
+ ## Loading State
62
+
63
+ ```tsx
64
+ <Button disabled>
65
+ <Spinner data-icon="inline-start" />
66
+ Saving...
67
+ </Button>
68
+ ```
69
+
70
+ ## Button Group
71
+
72
+ ```tsx
73
+ <ButtonGroup>
74
+ <Button variant="outline">Left</Button>
75
+ <Button variant="outline">Center</Button>
76
+ <Button variant="outline">Right</Button>
77
+ </ButtonGroup>
78
+ ```
79
+
80
+ ## Cursor Override (Tailwind v4)
81
+
82
+ Tailwind v4 sets `cursor: default` on buttons. If you need pointer cursor:
83
+
84
+ ```css
85
+ /* globals.css */
86
+ button:not(:disabled) {
87
+ cursor: pointer;
88
+ }
89
+ ```
90
+
91
+ ## Anti-patterns
92
+
93
+ - ❌ Don't merge classNames with template literals — use `cn()`
94
+ - ❌ Don't wrap Button in `<a>` — use `asChild` with `<Link>`
95
+ - ❌ Don't forget `aria-label` on icon-only buttons
96
+ - ❌ Don't forget `data-icon` on icons — spacing will be wrong without it
@@ -0,0 +1,156 @@
1
+ # Data Table — shadcn/ui + TanStack Table
2
+
3
+ ## Stack
4
+
5
+ ```bash
6
+ npx shadcn@latest add table
7
+ npm i @tanstack/react-table
8
+ ```
9
+
10
+ ## File Structure
11
+
12
+ ```
13
+ app/payments/
14
+ ├── columns.tsx ← column definitions (client component)
15
+ ├── data-table.tsx ← DataTable component (client component)
16
+ └── page.tsx ← server component, fetch data
17
+ ```
18
+
19
+ ## Step 1: Column Definitions
20
+
21
+ ```tsx
22
+ 'use client'
23
+
24
+ import { ColumnDef } from '@tanstack/react-table'
25
+
26
+ export type Payment = {
27
+ id: string
28
+ amount: number
29
+ status: 'pending' | 'processing' | 'success' | 'failed'
30
+ email: string
31
+ }
32
+
33
+ export const columns: ColumnDef<Payment>[] = [
34
+ {
35
+ accessorKey: 'status',
36
+ header: 'Status',
37
+ },
38
+ {
39
+ accessorKey: 'email',
40
+ header: 'Email',
41
+ },
42
+ {
43
+ accessorKey: 'amount',
44
+ header: () => <div className="text-right">Amount</div>,
45
+ cell: ({ row }) => {
46
+ const formatted = new Intl.NumberFormat('en-US', {
47
+ style: 'currency',
48
+ currency: 'USD',
49
+ }).format(row.getValue('amount'))
50
+ return <div className="text-right font-medium">{formatted}</div>
51
+ },
52
+ },
53
+ ]
54
+ ```
55
+
56
+ ## Step 2: DataTable Component
57
+
58
+ ```tsx
59
+ 'use client'
60
+
61
+ import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'
62
+ import {
63
+ Table,
64
+ TableBody,
65
+ TableCell,
66
+ TableHead,
67
+ TableHeader,
68
+ TableRow,
69
+ } from '@/components/ui/table'
70
+
71
+ export function DataTable<TData, TValue>({
72
+ columns,
73
+ data,
74
+ }: {
75
+ columns: ColumnDef<TData, TValue>[]
76
+ data: TData[]
77
+ }) {
78
+ const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() })
79
+
80
+ return (
81
+ <Table>
82
+ <TableHeader>
83
+ {table.getHeaderGroups().map((headerGroup) => (
84
+ <TableRow key={headerGroup.id}>
85
+ {headerGroup.headers.map((header) => (
86
+ <TableHead key={header.id}>
87
+ {flexRender(header.column.columnDef.header, header.getContext())}
88
+ </TableHead>
89
+ ))}
90
+ </TableRow>
91
+ ))}
92
+ </TableHeader>
93
+ <TableBody>
94
+ {table.getRowModel().rows.map((row) => (
95
+ <TableRow key={row.id}>
96
+ {row.getVisibleCells().map((cell) => (
97
+ <TableCell key={cell.id}>
98
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
99
+ </TableCell>
100
+ ))}
101
+ </TableRow>
102
+ ))}
103
+ </TableBody>
104
+ </Table>
105
+ )
106
+ }
107
+ ```
108
+
109
+ ## Progressive Features
110
+
111
+ Add these incrementally as needed:
112
+
113
+ ### Pagination
114
+
115
+ ```tsx
116
+ import { getPaginationRowModel } from "@tanstack/react-table"
117
+ const table = useReactTable({ ..., getPaginationRowModel: getPaginationRowModel() })
118
+ // Controls: table.previousPage(), table.nextPage(), table.getCanPreviousPage()
119
+ ```
120
+
121
+ ### Sorting
122
+
123
+ ```tsx
124
+ import { getSortedRowModel, SortingState } from "@tanstack/react-table"
125
+ const [sorting, setSorting] = useState<SortingState>([])
126
+ const table = useReactTable({ ..., getSortedRowModel: getSortedRowModel(), onSortingChange: setSorting, state: { sorting } })
127
+ ```
128
+
129
+ ### Column Filtering
130
+
131
+ ```tsx
132
+ import { getFilteredRowModel, ColumnFiltersState } from '@tanstack/react-table'
133
+ const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
134
+ // Add onColumnFiltersChange: setColumnFilters, state: { columnFilters }
135
+ ```
136
+
137
+ ### Row Selection
138
+
139
+ ```tsx
140
+ // Add checkbox column:
141
+ { id: "select", header: ({ table }) => <Checkbox checked={table.getIsAllPageRowsSelected()} onCheckedChange={v => table.toggleAllPageRowsSelected(!!v)} />, cell: ({ row }) => <Checkbox checked={row.getIsSelected()} onCheckedChange={v => row.toggleSelected(!!v)} /> }
142
+ ```
143
+
144
+ ### Column Visibility
145
+
146
+ ```tsx
147
+ const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({})
148
+ // Add DropdownMenu to toggle columns: table.getAllColumns().filter(c => c.getCanHide())
149
+ ```
150
+
151
+ ## Anti-patterns
152
+
153
+ - ❌ Don't put columns + table + page in one file — separate for maintainability
154
+ - ❌ Don't skip `getCoreRowModel()` — required base
155
+ - ❌ Don't use native `<table>` — use shadcn `<Table>` components
156
+ - ❌ Don't load 10k+ rows client-side — use server-side pagination
@@ -0,0 +1,123 @@
1
+ # Dialog, Sheet, Drawer, Alert Dialog — shadcn/ui
2
+
3
+ ## Install
4
+
5
+ ```bash
6
+ npx shadcn@latest add dialog sheet drawer alert-dialog
7
+ ```
8
+
9
+ ## Dialog (Modal)
10
+
11
+ ```tsx
12
+ <Dialog>
13
+ <DialogTrigger asChild>
14
+ <Button>Open</Button>
15
+ </DialogTrigger>
16
+ <DialogContent>
17
+ <DialogHeader>
18
+ <DialogTitle>Edit Profile</DialogTitle>
19
+ <DialogDescription>Make changes to your profile here.</DialogDescription>
20
+ </DialogHeader>
21
+ {/* form content */}
22
+ <DialogFooter>
23
+ <Button type="submit">Save</Button>
24
+ </DialogFooter>
25
+ </DialogContent>
26
+ </Dialog>
27
+ ```
28
+
29
+ ## Sheet (Slide-over Panel)
30
+
31
+ ```tsx
32
+ <Sheet>
33
+ <SheetTrigger asChild>
34
+ <Button variant="outline">Open Menu</Button>
35
+ </SheetTrigger>
36
+ <SheetContent side="right">
37
+ {' '}
38
+ {/* "top" | "right" | "bottom" | "left" */}
39
+ <SheetHeader>
40
+ <SheetTitle>Menu</SheetTitle>
41
+ <SheetDescription>Navigation</SheetDescription>
42
+ </SheetHeader>
43
+ {/* content */}
44
+ </SheetContent>
45
+ </Sheet>
46
+ ```
47
+
48
+ ## Drawer (Mobile Bottom Sheet)
49
+
50
+ Based on `vaul`. Great for mobile-first interactions.
51
+
52
+ ```tsx
53
+ <Drawer>
54
+ <DrawerTrigger asChild>
55
+ <Button variant="outline">Open</Button>
56
+ </DrawerTrigger>
57
+ <DrawerContent>
58
+ <DrawerHeader>
59
+ <DrawerTitle>Settings</DrawerTitle>
60
+ <DrawerDescription>Adjust your preferences.</DrawerDescription>
61
+ </DrawerHeader>
62
+ {/* content */}
63
+ <DrawerFooter>
64
+ <Button>Submit</Button>
65
+ <DrawerClose asChild>
66
+ <Button variant="outline">Cancel</Button>
67
+ </DrawerClose>
68
+ </DrawerFooter>
69
+ </DrawerContent>
70
+ </Drawer>
71
+ ```
72
+
73
+ ## Alert Dialog (Non-dismissable)
74
+
75
+ Cannot be closed by clicking outside or pressing Escape. For destructive confirmations.
76
+
77
+ ```tsx
78
+ <AlertDialog>
79
+ <AlertDialogTrigger asChild>
80
+ <Button variant="destructive">Delete</Button>
81
+ </AlertDialogTrigger>
82
+ <AlertDialogContent>
83
+ <AlertDialogHeader>
84
+ <AlertDialogTitle>Are you sure?</AlertDialogTitle>
85
+ <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
86
+ </AlertDialogHeader>
87
+ <AlertDialogFooter>
88
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
89
+ <AlertDialogAction>Delete</AlertDialogAction>
90
+ </AlertDialogFooter>
91
+ </AlertDialogContent>
92
+ </AlertDialog>
93
+ ```
94
+
95
+ ## Responsive: Dialog on Desktop, Drawer on Mobile
96
+
97
+ ```tsx
98
+ import { useMediaQuery } from '@/hooks/use-media-query'
99
+
100
+ function ResponsiveModal({ children }) {
101
+ const isDesktop = useMediaQuery('(min-width: 768px)')
102
+
103
+ if (isDesktop) {
104
+ return <Dialog>{children}</Dialog>
105
+ }
106
+
107
+ return <Drawer>{children}</Drawer>
108
+ }
109
+ ```
110
+
111
+ ## Controlled State
112
+
113
+ ```tsx
114
+ const [open, setOpen] = useState(false)
115
+ <Dialog open={open} onOpenChange={setOpen}>...</Dialog>
116
+ ```
117
+
118
+ ## Anti-patterns
119
+
120
+ - ❌ Don't skip `DialogTitle` — required for accessibility (screen readers)
121
+ - ❌ Don't nest Dialog inside Dialog — causes focus trap issues
122
+ - ❌ Don't use Dialog for destructive confirmations — use Alert Dialog
123
+ - ❌ Don't use Sheet on mobile — use Drawer instead