@coston/ui 0.1.1 → 0.2.2

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.
Files changed (177) hide show
  1. package/README.md +75 -10
  2. package/dist/{button-CJvoztqg.js → button-BJdMtkHX.js} +3 -3
  3. package/dist/button-BJdMtkHX.js.map +1 -0
  4. package/dist/{button-QxkOGnNm.d.ts → button-CyD56dqi.d.ts} +5 -5
  5. package/dist/{button-QxkOGnNm.d.ts.map → button-CyD56dqi.d.ts.map} +1 -1
  6. package/dist/components/accordion.d.ts.map +1 -1
  7. package/dist/components/accordion.js +4 -4
  8. package/dist/components/accordion.js.map +1 -1
  9. package/dist/components/alert-dialog.d.ts +3 -3
  10. package/dist/components/alert-dialog.d.ts.map +1 -1
  11. package/dist/components/alert-dialog.js +5 -5
  12. package/dist/components/alert-dialog.js.map +1 -1
  13. package/dist/components/alert.d.ts +2 -2
  14. package/dist/components/alert.js +3 -3
  15. package/dist/components/alert.js.map +1 -1
  16. package/dist/components/avatar.d.ts.map +1 -1
  17. package/dist/components/avatar.js +2 -2
  18. package/dist/components/avatar.js.map +1 -1
  19. package/dist/components/badge.d.ts +5 -5
  20. package/dist/components/badge.d.ts.map +1 -1
  21. package/dist/components/badge.js +4 -1
  22. package/dist/components/badge.js.map +1 -1
  23. package/dist/components/breadcrumb.d.ts +3 -3
  24. package/dist/components/breadcrumb.js +1 -1
  25. package/dist/components/button.d.ts +1 -1
  26. package/dist/components/button.js +2 -2
  27. package/dist/components/calendar.d.ts +2 -2
  28. package/dist/components/calendar.d.ts.map +1 -1
  29. package/dist/components/calendar.js +1 -1
  30. package/dist/components/card.d.ts +4 -1
  31. package/dist/components/card.d.ts.map +1 -1
  32. package/dist/components/card.js +5 -5
  33. package/dist/components/card.js.map +1 -1
  34. package/dist/components/carousel.d.ts +1 -1
  35. package/dist/components/carousel.js +2 -2
  36. package/dist/components/chart.d.ts +1 -1
  37. package/dist/components/chart.d.ts.map +1 -1
  38. package/dist/components/chart.js +1 -1
  39. package/dist/components/checkbox.js +3 -3
  40. package/dist/components/checkbox.js.map +1 -1
  41. package/dist/components/command.d.ts +75 -10
  42. package/dist/components/command.d.ts.map +1 -1
  43. package/dist/components/command.js +3 -3
  44. package/dist/components/command.js.map +1 -1
  45. package/dist/components/context-menu.d.ts +2 -2
  46. package/dist/components/context-menu.d.ts.map +1 -1
  47. package/dist/components/context-menu.js +3 -3
  48. package/dist/components/context-menu.js.map +1 -1
  49. package/dist/components/dialog.d.ts +3 -3
  50. package/dist/components/dialog.js +2 -2
  51. package/dist/components/drawer.d.ts +13 -11
  52. package/dist/components/drawer.d.ts.map +1 -1
  53. package/dist/components/drawer.js +2 -2
  54. package/dist/components/drawer.js.map +1 -1
  55. package/dist/components/dropdown-menu.d.ts +2 -2
  56. package/dist/components/dropdown-menu.d.ts.map +1 -1
  57. package/dist/components/dropdown-menu.js +3 -3
  58. package/dist/components/dropdown-menu.js.map +1 -1
  59. package/dist/components/hover-card.js +2 -2
  60. package/dist/components/hover-card.js.map +1 -1
  61. package/dist/components/input.js +1 -1
  62. package/dist/components/label.d.ts +2 -2
  63. package/dist/components/label.js +1 -1
  64. package/dist/components/menubar.d.ts +3 -3
  65. package/dist/components/menubar.js +4 -4
  66. package/dist/components/menubar.js.map +1 -1
  67. package/dist/components/navigation-menu.d.ts +2 -2
  68. package/dist/components/navigation-menu.js +2 -2
  69. package/dist/components/navigation-menu.js.map +1 -1
  70. package/dist/components/pagination.d.ts +7 -7
  71. package/dist/components/pagination.js +2 -2
  72. package/dist/components/popover.js +2 -2
  73. package/dist/components/popover.js.map +1 -1
  74. package/dist/components/progress.js +2 -2
  75. package/dist/components/progress.js.map +1 -1
  76. package/dist/components/radio-group.js +1 -1
  77. package/dist/components/resizable.d.ts +5 -5
  78. package/dist/components/resizable.d.ts.map +1 -1
  79. package/dist/components/resizable.js +4 -4
  80. package/dist/components/resizable.js.map +1 -1
  81. package/dist/components/scroll-area.js +1 -1
  82. package/dist/components/select.js +2 -2
  83. package/dist/components/select.js.map +1 -1
  84. package/dist/components/separator.js +2 -2
  85. package/dist/components/sheet.d.ts +6 -6
  86. package/dist/components/sheet.d.ts.map +1 -1
  87. package/dist/components/sheet.js +8 -8
  88. package/dist/components/sheet.js.map +1 -1
  89. package/dist/components/sidebar.d.ts +1 -1
  90. package/dist/components/sidebar.js +5 -5
  91. package/dist/components/sidebar.js.map +1 -1
  92. package/dist/components/skeleton.d.ts +2 -2
  93. package/dist/components/skeleton.js +1 -1
  94. package/dist/components/slider.js +3 -3
  95. package/dist/components/slider.js.map +1 -1
  96. package/dist/components/sonner.d.ts +2 -2
  97. package/dist/components/sonner.d.ts.map +1 -1
  98. package/dist/components/switch.js +3 -3
  99. package/dist/components/switch.js.map +1 -1
  100. package/dist/components/table.d.ts.map +1 -1
  101. package/dist/components/table.js +2 -2
  102. package/dist/components/table.js.map +1 -1
  103. package/dist/components/tabs.js +2 -2
  104. package/dist/components/tabs.js.map +1 -1
  105. package/dist/components/textarea.js +1 -1
  106. package/dist/components/toggle-group.d.ts +3 -3
  107. package/dist/components/toggle-group.js +2 -2
  108. package/dist/components/toggle.d.ts +3 -3
  109. package/dist/components/toggle.js +2 -2
  110. package/dist/components/tooltip.js +2 -2
  111. package/dist/{dialog-B_8jhMOs.js → dialog-COSqOqbH.js} +6 -6
  112. package/dist/dialog-COSqOqbH.js.map +1 -0
  113. package/dist/lib/utils.d.ts +2 -1
  114. package/dist/lib/utils.d.ts.map +1 -1
  115. package/dist/lib/utils.js +2 -2
  116. package/dist/{separator-9lzFsfCC.js → separator-C3mBJ8ik.js} +2 -2
  117. package/dist/{separator-9lzFsfCC.js.map → separator-C3mBJ8ik.js.map} +1 -1
  118. package/dist/{toggle-BpKiTVe4.js → toggle-CD9xIRlr.js} +3 -3
  119. package/dist/toggle-CD9xIRlr.js.map +1 -0
  120. package/dist/{tooltip-CXS6wR7g.js → tooltip-PxI7r4yP.js} +3 -3
  121. package/dist/tooltip-PxI7r4yP.js.map +1 -0
  122. package/dist/{utils-C0f9Ma6r.js → utils-CyPJ3VV3.js} +3 -2
  123. package/dist/utils-CyPJ3VV3.js.map +1 -0
  124. package/llms.txt +989 -0
  125. package/package.json +52 -14
  126. package/source.css +40 -0
  127. package/dist/button-CJvoztqg.js.map +0 -1
  128. package/dist/dialog-B_8jhMOs.js.map +0 -1
  129. package/dist/toggle-BpKiTVe4.js.map +0 -1
  130. package/dist/tooltip-CXS6wR7g.js.map +0 -1
  131. package/dist/utils-C0f9Ma6r.js.map +0 -1
  132. package/src/components/accordion.tsx +0 -51
  133. package/src/components/alert-dialog.tsx +0 -115
  134. package/src/components/alert.tsx +0 -49
  135. package/src/components/aspect-ratio.tsx +0 -5
  136. package/src/components/avatar.tsx +0 -44
  137. package/src/components/badge.tsx +0 -32
  138. package/src/components/breadcrumb.tsx +0 -101
  139. package/src/components/button.tsx +0 -48
  140. package/src/components/calendar.tsx +0 -76
  141. package/src/components/card.tsx +0 -60
  142. package/src/components/carousel.tsx +0 -240
  143. package/src/components/chart.tsx +0 -232
  144. package/src/components/checkbox.tsx +0 -25
  145. package/src/components/collapsible.tsx +0 -11
  146. package/src/components/command.tsx +0 -136
  147. package/src/components/context-menu.tsx +0 -187
  148. package/src/components/dialog.tsx +0 -105
  149. package/src/components/drawer.tsx +0 -98
  150. package/src/components/dropdown-menu.tsx +0 -185
  151. package/src/components/hover-card.tsx +0 -29
  152. package/src/components/input.tsx +0 -24
  153. package/src/components/label.tsx +0 -19
  154. package/src/components/menubar.tsx +0 -219
  155. package/src/components/navigation-menu.tsx +0 -120
  156. package/src/components/pagination.tsx +0 -98
  157. package/src/components/popover.tsx +0 -27
  158. package/src/components/progress.tsx +0 -23
  159. package/src/components/radio-group.tsx +0 -35
  160. package/src/components/resizable.tsx +0 -38
  161. package/src/components/scroll-area.tsx +0 -43
  162. package/src/components/select.tsx +0 -147
  163. package/src/components/separator.tsx +0 -23
  164. package/src/components/sheet.tsx +0 -122
  165. package/src/components/sidebar.tsx +0 -507
  166. package/src/components/skeleton.tsx +0 -7
  167. package/src/components/slider.tsx +0 -23
  168. package/src/components/sonner.tsx +0 -26
  169. package/src/components/switch.tsx +0 -26
  170. package/src/components/table.tsx +0 -95
  171. package/src/components/tabs.tsx +0 -53
  172. package/src/components/textarea.tsx +0 -20
  173. package/src/components/toggle-group.tsx +0 -54
  174. package/src/components/toggle.tsx +0 -40
  175. package/src/components/tooltip.tsx +0 -27
  176. package/src/hooks/use-mobile.tsx +0 -19
  177. package/src/lib/utils.ts +0 -6
package/llms.txt ADDED
@@ -0,0 +1,989 @@
1
+ # @coston/ui
2
+
3
+ > React UI component library built on Radix UI and Tailwind CSS.
4
+
5
+ ## Setup
6
+
7
+ ### Install
8
+
9
+ ```bash
10
+ npm install @coston/ui @coston/design-tokens
11
+ ```
12
+
13
+ Peer dependencies: `react`, `react-dom`
14
+
15
+ ### CSS (required — components render unstyled without this)
16
+
17
+ Add to your root CSS file:
18
+
19
+ ```css
20
+ @import 'tailwindcss';
21
+ @import '@coston/design-tokens/tailwind.css';
22
+ @import '@coston/ui/source.css';
23
+ ```
24
+
25
+ ### Import pattern
26
+
27
+ ```tsx
28
+ // Each component is a named export from a separate entry point
29
+ import { Button } from '@coston/ui/button';
30
+ import { Card, CardHeader, CardTitle, CardContent } from '@coston/ui/card';
31
+ import { cn } from '@coston/ui/lib/utils';
32
+ import { useIsMobile } from '@coston/ui/hooks/use-mobile';
33
+ ```
34
+
35
+ ### Do NOT
36
+
37
+ - Do not import from the package root: `import { Button } from '@coston/ui'` — there is no root export
38
+ - Do not use default imports: `import Button from '@coston/ui/button'` — all exports are named
39
+ - Do not skip the CSS setup — components will render without styles
40
+ - Do not import `@coston/ui/source.css` without also importing `@coston/design-tokens/tailwind.css`
41
+
42
+ ## Optional peer dependencies
43
+
44
+ | Component | Package |
45
+ | --------- | ------- |
46
+ | Chart | `recharts` |
47
+ | Carousel | `embla-carousel-react` |
48
+ | Sonner | `sonner` |
49
+ | Drawer | `vaul` |
50
+ | Command | `cmdk` |
51
+ | Resizable | `react-resizable-panels` |
52
+ | Calendar | `react-day-picker`, `date-fns` |
53
+
54
+ ## Components and exports
55
+
56
+ ### accordion
57
+ Import: `@coston/ui/accordion`
58
+ Exports: Accordion, AccordionItem, AccordionTrigger, AccordionContent
59
+
60
+ ### alert-dialog
61
+ Import: `@coston/ui/alert-dialog`
62
+ Exports: AlertDialog, AlertDialogPortal, AlertDialogOverlay, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel
63
+
64
+ ### alert
65
+ Import: `@coston/ui/alert`
66
+ Exports: Alert, AlertTitle, AlertDescription
67
+
68
+ ### aspect-ratio
69
+ Import: `@coston/ui/aspect-ratio`
70
+ Exports: AspectRatio
71
+
72
+ ### avatar
73
+ Import: `@coston/ui/avatar`
74
+ Exports: Avatar, AvatarImage, AvatarFallback
75
+
76
+ ### badge
77
+ Import: `@coston/ui/badge`
78
+ Exports: Badge, badgeVariants
79
+
80
+ ### breadcrumb
81
+ Import: `@coston/ui/breadcrumb`
82
+ Exports: Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis
83
+
84
+ ### button
85
+ Import: `@coston/ui/button`
86
+ Exports: Button, buttonVariants
87
+
88
+ ### calendar
89
+ Import: `@coston/ui/calendar`
90
+ Exports: Calendar
91
+
92
+ ### card
93
+ Import: `@coston/ui/card`
94
+ Exports: Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent
95
+
96
+ ### carousel
97
+ Import: `@coston/ui/carousel`
98
+ Exports: CarouselApi, Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext
99
+
100
+ ### chart
101
+ Import: `@coston/ui/chart`
102
+ Exports: ChartContainer, ChartTooltip, ChartTooltipContent, ChartConfig
103
+
104
+ ### checkbox
105
+ Import: `@coston/ui/checkbox`
106
+ Exports: Checkbox
107
+
108
+ ### collapsible
109
+ Import: `@coston/ui/collapsible`
110
+ Exports: Collapsible, CollapsibleTrigger, CollapsibleContent
111
+
112
+ ### command
113
+ Import: `@coston/ui/command`
114
+ Exports: Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut
115
+
116
+ ### context-menu
117
+ Import: `@coston/ui/context-menu`
118
+ Exports: ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioItem, ContextMenuLabel, ContextMenuSeparator, ContextMenuShortcut, ContextMenuGroup, ContextMenuPortal, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuRadioGroup
119
+
120
+ ### dialog
121
+ Import: `@coston/ui/dialog`
122
+ Exports: Dialog, DialogPortal, DialogOverlay, DialogClose, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription
123
+
124
+ ### drawer
125
+ Import: `@coston/ui/drawer`
126
+ Exports: Drawer, DrawerPortal, DrawerOverlay, DrawerTrigger, DrawerClose, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription
127
+
128
+ ### dropdown-menu
129
+ Import: `@coston/ui/dropdown-menu`
130
+ Exports: DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuGroup, DropdownMenuPortal, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuRadioGroup
131
+
132
+ ### hover-card
133
+ Import: `@coston/ui/hover-card`
134
+ Exports: HoverCard, HoverCardTrigger, HoverCardContent
135
+
136
+ ### input
137
+ Import: `@coston/ui/input`
138
+ Exports: Input
139
+
140
+ ### label
141
+ Import: `@coston/ui/label`
142
+ Exports: Label
143
+
144
+ ### menubar
145
+ Import: `@coston/ui/menubar`
146
+ Exports: Menubar, MenubarMenu, MenubarTrigger, MenubarContent, MenubarItem, MenubarSeparator, MenubarLabel, MenubarCheckboxItem, MenubarRadioGroup, MenubarRadioItem, MenubarPortal, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarGroup, MenubarShortcut
147
+
148
+ ### navigation-menu
149
+ Import: `@coston/ui/navigation-menu`
150
+ Exports: navigationMenuTriggerStyle, NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuContent, NavigationMenuTrigger, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport
151
+
152
+ ### pagination
153
+ Import: `@coston/ui/pagination`
154
+ Exports: Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis
155
+
156
+ ### popover
157
+ Import: `@coston/ui/popover`
158
+ Exports: Popover, PopoverTrigger, PopoverContent
159
+
160
+ ### progress
161
+ Import: `@coston/ui/progress`
162
+ Exports: Progress
163
+
164
+ ### radio-group
165
+ Import: `@coston/ui/radio-group`
166
+ Exports: RadioGroup, RadioGroupItem
167
+
168
+ ### resizable
169
+ Import: `@coston/ui/resizable`
170
+ Exports: ResizablePanelGroup, ResizablePanel, ResizableHandle
171
+
172
+ ### scroll-area
173
+ Import: `@coston/ui/scroll-area`
174
+ Exports: ScrollArea, ScrollBar
175
+
176
+ ### select
177
+ Import: `@coston/ui/select`
178
+ Exports: Select, SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator, SelectScrollUpButton, SelectScrollDownButton
179
+
180
+ ### separator
181
+ Import: `@coston/ui/separator`
182
+ Exports: Separator
183
+
184
+ ### sheet
185
+ Import: `@coston/ui/sheet`
186
+ Exports: Sheet, SheetPortal, SheetOverlay, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription
187
+
188
+ ### sidebar
189
+ Import: `@coston/ui/sidebar`
190
+ Exports: Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarSeparator, SidebarTrigger, useSidebar
191
+
192
+ ### skeleton
193
+ Import: `@coston/ui/skeleton`
194
+ Exports: Skeleton
195
+
196
+ ### slider
197
+ Import: `@coston/ui/slider`
198
+ Exports: Slider
199
+
200
+ ### sonner
201
+ Import: `@coston/ui/sonner`
202
+ Exports: Toaster
203
+
204
+ ### switch
205
+ Import: `@coston/ui/switch`
206
+ Exports: Switch
207
+
208
+ ### table
209
+ Import: `@coston/ui/table`
210
+ Exports: Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption
211
+
212
+ ### tabs
213
+ Import: `@coston/ui/tabs`
214
+ Exports: Tabs, TabsList, TabsTrigger, TabsContent
215
+
216
+ ### textarea
217
+ Import: `@coston/ui/textarea`
218
+ Exports: Textarea
219
+
220
+ ### toggle-group
221
+ Import: `@coston/ui/toggle-group`
222
+ Exports: ToggleGroup, ToggleGroupItem
223
+
224
+ ### toggle
225
+ Import: `@coston/ui/toggle`
226
+ Exports: Toggle, toggleVariants
227
+
228
+ ### tooltip
229
+ Import: `@coston/ui/tooltip`
230
+ Exports: Tooltip, TooltipTrigger, TooltipContent, TooltipProvider
231
+
232
+ ### Utilities
233
+
234
+ - `cn(...classValues)` — `@coston/ui/lib/utils`
235
+ - `useIsMobile()` — `@coston/ui/hooks/use-mobile`
236
+
237
+ ## Usage patterns
238
+
239
+ ### confirmation flow
240
+ ```tsx
241
+ import { useState } from 'react';
242
+ import { toast } from 'sonner';
243
+ import {
244
+ AlertDialog,
245
+ AlertDialogAction,
246
+ AlertDialogCancel,
247
+ AlertDialogContent,
248
+ AlertDialogDescription,
249
+ AlertDialogFooter,
250
+ AlertDialogHeader,
251
+ AlertDialogTitle,
252
+ AlertDialogTrigger,
253
+ } from '@coston/ui/alert-dialog';
254
+ import { Button } from '@coston/ui/button';
255
+ import { Input } from '@coston/ui/input';
256
+ import { Label } from '@coston/ui/label';
257
+
258
+ function DestructiveConfirm() {
259
+ return (
260
+ <AlertDialog>
261
+ <AlertDialogTrigger asChild>
262
+ <Button variant="destructive">Delete project</Button>
263
+ </AlertDialogTrigger>
264
+ <AlertDialogContent>
265
+ <AlertDialogHeader>
266
+ <AlertDialogTitle>Delete project?</AlertDialogTitle>
267
+ <AlertDialogDescription>
268
+ This action cannot be undone. All project data will be permanently removed.
269
+ </AlertDialogDescription>
270
+ </AlertDialogHeader>
271
+ <AlertDialogFooter>
272
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
273
+ <AlertDialogAction
274
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
275
+ onClick={() => toast.error('Project deleted')}
276
+ >
277
+ Delete
278
+ </AlertDialogAction>
279
+ </AlertDialogFooter>
280
+ </AlertDialogContent>
281
+ </AlertDialog>
282
+ );
283
+ }
284
+
285
+ function TypeToConfirm() {
286
+ const [value, setValue] = useState('');
287
+ const confirmed = value === 'DELETE';
288
+
289
+ return (
290
+ <AlertDialog onOpenChange={() => setValue('')}>
291
+ <AlertDialogTrigger asChild>
292
+ <Button variant="outline">Delete account</Button>
293
+ </AlertDialogTrigger>
294
+ <AlertDialogContent>
295
+ <AlertDialogHeader>
296
+ <AlertDialogTitle>Delete your account?</AlertDialogTitle>
297
+ <AlertDialogDescription>
298
+ This permanently deletes your account and all associated data. Type{' '}
299
+ <strong>DELETE</strong> to confirm.
300
+ </AlertDialogDescription>
301
+ </AlertDialogHeader>
302
+ <div className="grid gap-2 py-2">
303
+ <Label htmlFor="confirm-input">Confirmation</Label>
304
+ <Input
305
+ id="confirm-input"
306
+ value={value}
307
+ onChange={e => setValue(e.target.value)}
308
+ placeholder="Type DELETE"
309
+ />
310
+ </div>
311
+ <AlertDialogFooter>
312
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
313
+ <AlertDialogAction
314
+ disabled={!confirmed}
315
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90 disabled:opacity-50"
316
+ onClick={() => toast.success('Account deleted')}
317
+ >
318
+ Delete account
319
+ </AlertDialogAction>
320
+ </AlertDialogFooter>
321
+ </AlertDialogContent>
322
+ </AlertDialog>
323
+ );
324
+ }
325
+
326
+ export function ConfirmationFlow() {
327
+ return (
328
+ <div className="flex flex-wrap gap-4 p-6">
329
+ <DestructiveConfirm />
330
+ <TypeToConfirm />
331
+ </div>
332
+ );
333
+ }
334
+ ```
335
+
336
+ ### contextual header
337
+ ```tsx
338
+ import {
339
+ Breadcrumb,
340
+ BreadcrumbItem,
341
+ BreadcrumbLink,
342
+ BreadcrumbList,
343
+ BreadcrumbPage,
344
+ BreadcrumbSeparator,
345
+ } from '@coston/ui/breadcrumb';
346
+ import { Separator } from '@coston/ui/separator';
347
+ import { SidebarTrigger } from '@coston/ui/sidebar';
348
+ import { Tabs, TabsList, TabsTrigger } from '@coston/ui/tabs';
349
+
350
+ const tabItems = ['Overview', 'Details', 'Projects', 'Notes'];
351
+
352
+ /**
353
+ * Contextual Header
354
+ *
355
+ * Header with breadcrumb trail and inline sub-navigation tabs.
356
+ * Use for record detail pages (CRM accounts, project views, etc.)
357
+ * where the user navigated from a parent list into a specific record.
358
+ *
359
+ * Desktop: single row — SidebarTrigger | Breadcrumb | Tabs
360
+ * Mobile: two rows — [SidebarTrigger | Breadcrumb] / [scrollable Tabs]
361
+ */
362
+ export function ContextualHeader() {
363
+ return (
364
+ <div className="flex shrink-0 flex-col border-b border-border/50">
365
+ {/* Primary row: trigger + breadcrumb (+ inline tabs on sm+) */}
366
+ <div className="flex h-12 items-center gap-1 px-4 lg:gap-2 lg:px-6">
367
+ <SidebarTrigger className="-ml-1" />
368
+ <Separator orientation="vertical" className="mx-2 data-[orientation=vertical]:h-4" />
369
+ <Breadcrumb>
370
+ <BreadcrumbList>
371
+ <BreadcrumbItem>
372
+ <BreadcrumbLink href="#">External</BreadcrumbLink>
373
+ </BreadcrumbItem>
374
+ <BreadcrumbSeparator />
375
+ <BreadcrumbItem>
376
+ <BreadcrumbPage>Acme Inc.</BreadcrumbPage>
377
+ </BreadcrumbItem>
378
+ </BreadcrumbList>
379
+ </Breadcrumb>
380
+
381
+ {/* Tabs inline — only on sm+ */}
382
+ <div className="ml-2 hidden items-center sm:flex">
383
+ <Separator orientation="vertical" className="mx-2 data-[orientation=vertical]:h-4" />
384
+ <Tabs defaultValue="overview">
385
+ <TabsList>
386
+ {tabItems.map(tab => (
387
+ <TabsTrigger key={tab} value={tab.toLowerCase()}>
388
+ {tab}
389
+ </TabsTrigger>
390
+ ))}
391
+ </TabsList>
392
+ </Tabs>
393
+ </div>
394
+ </div>
395
+
396
+ {/* Secondary row: tabs on mobile only */}
397
+ <div className="flex items-center overflow-x-auto border-t border-border/30 px-3 pb-1 pt-0.5 sm:hidden">
398
+ <Tabs defaultValue="overview">
399
+ <TabsList>
400
+ {tabItems.map(tab => (
401
+ <TabsTrigger key={tab} value={tab.toLowerCase()}>
402
+ {tab}
403
+ </TabsTrigger>
404
+ ))}
405
+ </TabsList>
406
+ </Tabs>
407
+ </div>
408
+ </div>
409
+ );
410
+ }
411
+ ```
412
+
413
+ ### data table toolbar
414
+ ```tsx
415
+ import { useState } from 'react';
416
+ import { DownloadIcon, PlusIcon, SearchIcon } from 'lucide-react';
417
+ import { Badge } from '@coston/ui/badge';
418
+ import { Button } from '@coston/ui/button';
419
+ import { Input } from '@coston/ui/input';
420
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@coston/ui/select';
421
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@coston/ui/table';
422
+
423
+ type Status = 'Active' | 'Inactive' | 'Archived';
424
+
425
+ type Row = {
426
+ id: number;
427
+ name: string;
428
+ email: string;
429
+ role: string;
430
+ status: Status;
431
+ createdAt: string;
432
+ };
433
+
434
+ const data: Row[] = [
435
+ {
436
+ id: 1,
437
+ name: 'Alice Martin',
438
+ email: 'alice@example.com',
439
+ role: 'Admin',
440
+ status: 'Active',
441
+ createdAt: 'Jan 12, 2024',
442
+ },
443
+ {
444
+ id: 2,
445
+ name: 'Bob Chen',
446
+ email: 'bob@example.com',
447
+ role: 'Editor',
448
+ status: 'Active',
449
+ createdAt: 'Feb 3, 2024',
450
+ },
451
+ {
452
+ id: 3,
453
+ name: 'Carol Davis',
454
+ email: 'carol@example.com',
455
+ role: 'Viewer',
456
+ status: 'Inactive',
457
+ createdAt: 'Mar 8, 2024',
458
+ },
459
+ {
460
+ id: 4,
461
+ name: 'Dan Kim',
462
+ email: 'dan@example.com',
463
+ role: 'Editor',
464
+ status: 'Active',
465
+ createdAt: 'Mar 19, 2024',
466
+ },
467
+ {
468
+ id: 5,
469
+ name: 'Eva Rossi',
470
+ email: 'eva@example.com',
471
+ role: 'Admin',
472
+ status: 'Archived',
473
+ createdAt: 'Apr 2, 2024',
474
+ },
475
+ {
476
+ id: 6,
477
+ name: 'Frank Lee',
478
+ email: 'frank@example.com',
479
+ role: 'Viewer',
480
+ status: 'Active',
481
+ createdAt: 'Apr 15, 2024',
482
+ },
483
+ {
484
+ id: 7,
485
+ name: 'Grace Park',
486
+ email: 'grace@example.com',
487
+ role: 'Editor',
488
+ status: 'Inactive',
489
+ createdAt: 'May 7, 2024',
490
+ },
491
+ {
492
+ id: 8,
493
+ name: 'Henry Wu',
494
+ email: 'henry@example.com',
495
+ role: 'Viewer',
496
+ status: 'Active',
497
+ createdAt: 'May 20, 2024',
498
+ },
499
+ ];
500
+
501
+ const statusVariant: Record<Status, 'default' | 'secondary' | 'outline'> = {
502
+ Active: 'default',
503
+ Inactive: 'secondary',
504
+ Archived: 'outline',
505
+ };
506
+
507
+ const PAGE_SIZE = 5;
508
+
509
+ export function DataTableToolbar() {
510
+ const [search, setSearch] = useState('');
511
+ const [status, setStatus] = useState('all');
512
+ const [page, setPage] = useState(0);
513
+
514
+ const filtered = data.filter(row => {
515
+ const matchesSearch =
516
+ row.name.toLowerCase().includes(search.toLowerCase()) ||
517
+ row.email.toLowerCase().includes(search.toLowerCase());
518
+ const matchesStatus = status === 'all' || row.status === status;
519
+ return matchesSearch && matchesStatus;
520
+ });
521
+
522
+ const pageCount = Math.ceil(filtered.length / PAGE_SIZE);
523
+ const paged = filtered.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);
524
+
525
+ return (
526
+ <div className="flex flex-col gap-4 p-4">
527
+ <div className="flex flex-wrap items-center gap-2">
528
+ <div className="relative min-w-[160px] flex-1">
529
+ <SearchIcon className="absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
530
+ <Input
531
+ placeholder="Search users…"
532
+ value={search}
533
+ onChange={e => {
534
+ setSearch(e.target.value);
535
+ setPage(0);
536
+ }}
537
+ className="pl-8"
538
+ />
539
+ </div>
540
+ <Select
541
+ value={status}
542
+ onValueChange={v => {
543
+ setStatus(v);
544
+ setPage(0);
545
+ }}
546
+ >
547
+ <SelectTrigger className="w-[140px]">
548
+ <SelectValue />
549
+ </SelectTrigger>
550
+ <SelectContent>
551
+ <SelectItem value="all">All statuses</SelectItem>
552
+ <SelectItem value="Active">Active</SelectItem>
553
+ <SelectItem value="Inactive">Inactive</SelectItem>
554
+ <SelectItem value="Archived">Archived</SelectItem>
555
+ </SelectContent>
556
+ </Select>
557
+ <Button variant="outline" size="sm">
558
+ <PlusIcon className="h-4 w-4" />
559
+ Add user
560
+ </Button>
561
+ <Button variant="ghost" size="sm">
562
+ <DownloadIcon className="h-4 w-4" />
563
+ Export
564
+ </Button>
565
+ </div>
566
+
567
+ <div className="rounded-md border border-border">
568
+ <Table>
569
+ <TableHeader>
570
+ <TableRow>
571
+ <TableHead>Name</TableHead>
572
+ <TableHead className="hidden sm:table-cell">Email</TableHead>
573
+ <TableHead className="hidden md:table-cell">Role</TableHead>
574
+ <TableHead>Status</TableHead>
575
+ <TableHead className="hidden lg:table-cell">Created</TableHead>
576
+ </TableRow>
577
+ </TableHeader>
578
+ <TableBody>
579
+ {paged.length ? (
580
+ paged.map(row => (
581
+ <TableRow key={row.id}>
582
+ <TableCell className="font-medium">{row.name}</TableCell>
583
+ <TableCell className="hidden text-muted-foreground sm:table-cell">
584
+ {row.email}
585
+ </TableCell>
586
+ <TableCell className="hidden md:table-cell">{row.role}</TableCell>
587
+ <TableCell>
588
+ <Badge variant={statusVariant[row.status]}>{row.status}</Badge>
589
+ </TableCell>
590
+ <TableCell className="hidden text-muted-foreground lg:table-cell">
591
+ {row.createdAt}
592
+ </TableCell>
593
+ </TableRow>
594
+ ))
595
+ ) : (
596
+ <TableRow>
597
+ <TableCell colSpan={5} className="h-24 text-center text-muted-foreground">
598
+ No results found.
599
+ </TableCell>
600
+ </TableRow>
601
+ )}
602
+ </TableBody>
603
+ </Table>
604
+ </div>
605
+
606
+ <div className="flex items-center justify-between text-sm text-muted-foreground">
607
+ <span>
608
+ {filtered.length} result{filtered.length !== 1 ? 's' : ''}
609
+ </span>
610
+ <div className="flex items-center gap-2">
611
+ <Button
612
+ variant="outline"
613
+ size="sm"
614
+ disabled={page === 0}
615
+ onClick={() => setPage(p => p - 1)}
616
+ >
617
+ Previous
618
+ </Button>
619
+ <span>
620
+ Page {page + 1} of {Math.max(1, pageCount)}
621
+ </span>
622
+ <Button
623
+ variant="outline"
624
+ size="sm"
625
+ disabled={page >= pageCount - 1}
626
+ onClick={() => setPage(p => p + 1)}
627
+ >
628
+ Next
629
+ </Button>
630
+ </div>
631
+ </div>
632
+ </div>
633
+ );
634
+ }
635
+ ```
636
+
637
+ ### empty state
638
+ ```tsx
639
+ import { SearchIcon, FolderIcon, AlertCircleIcon } from 'lucide-react';
640
+ import { Button } from '@coston/ui/button';
641
+
642
+ const variants = [
643
+ {
644
+ icon: SearchIcon,
645
+ title: 'No results',
646
+ description: 'Try adjusting your filters',
647
+ action: 'Clear filters',
648
+ },
649
+ {
650
+ icon: FolderIcon,
651
+ title: 'No items yet',
652
+ description: 'Get started by creating your first one',
653
+ action: 'Create item',
654
+ },
655
+ {
656
+ icon: AlertCircleIcon,
657
+ title: 'Something went wrong',
658
+ description: 'We had trouble loading this content',
659
+ action: 'Retry',
660
+ },
661
+ ];
662
+
663
+ export function EmptyState() {
664
+ return (
665
+ <div className="grid grid-cols-1 gap-4 p-6 sm:grid-cols-3">
666
+ {variants.map(({ icon: Icon, title, description, action }) => (
667
+ <div
668
+ key={title}
669
+ className="flex flex-col items-center gap-3 rounded-lg border border-border p-8 text-center"
670
+ >
671
+ <div className="flex h-12 w-12 items-center justify-center rounded-full bg-muted">
672
+ <Icon className="h-6 w-6 text-muted-foreground" />
673
+ </div>
674
+ <div>
675
+ <p className="text-sm font-medium">{title}</p>
676
+ <p className="text-sm text-muted-foreground">{description}</p>
677
+ </div>
678
+ <Button variant="outline" size="sm">
679
+ {action}
680
+ </Button>
681
+ </div>
682
+ ))}
683
+ </div>
684
+ );
685
+ }
686
+ ```
687
+
688
+ ### multi step form
689
+ ```tsx
690
+ import { useState } from 'react';
691
+ import { toast } from 'sonner';
692
+ import { Button } from '@coston/ui/button';
693
+ import { Input } from '@coston/ui/input';
694
+ import { Label } from '@coston/ui/label';
695
+ import { Progress } from '@coston/ui/progress';
696
+ import { Separator } from '@coston/ui/separator';
697
+ import { Textarea } from '@coston/ui/textarea';
698
+
699
+ type FormValues = {
700
+ email: string;
701
+ password: string;
702
+ name: string;
703
+ bio: string;
704
+ };
705
+
706
+ const steps = ['Account', 'Profile', 'Confirm'];
707
+
708
+ function StepIndicator({ current }: { current: number }) {
709
+ return (
710
+ <div className="flex items-center justify-center">
711
+ {steps.map((label, i) => (
712
+ <div key={label} className="flex items-center">
713
+ <div className="flex flex-col items-center gap-1">
714
+ <div
715
+ className={[
716
+ 'flex h-7 w-7 items-center justify-center rounded-full text-xs font-medium',
717
+ i < current
718
+ ? 'bg-primary text-primary-foreground'
719
+ : i === current
720
+ ? 'text-primary ring-2 ring-primary'
721
+ : 'bg-muted text-muted-foreground',
722
+ ].join(' ')}
723
+ >
724
+ {i < current ? '✓' : i + 1}
725
+ </div>
726
+ <span
727
+ className={`text-xs ${i === current ? 'font-medium text-foreground' : 'text-muted-foreground'}`}
728
+ >
729
+ {label}
730
+ </span>
731
+ </div>
732
+ {i < steps.length - 1 && (
733
+ <div className={`mx-2 mb-4 h-px w-12 ${i < current ? 'bg-primary' : 'bg-border'}`} />
734
+ )}
735
+ </div>
736
+ ))}
737
+ </div>
738
+ );
739
+ }
740
+
741
+ export function MultiStepForm() {
742
+ const [step, setStep] = useState(0);
743
+ const [values, setValues] = useState<FormValues>({ email: '', password: '', name: '', bio: '' });
744
+
745
+ const set =
746
+ (field: keyof FormValues) => (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>
747
+ setValues(v => ({ ...v, [field]: e.target.value }));
748
+
749
+ const handleSubmit = () => {
750
+ toast.success('Account created!');
751
+ setStep(0);
752
+ setValues({ email: '', password: '', name: '', bio: '' });
753
+ };
754
+
755
+ return (
756
+ <div className="p-6">
757
+ <div className="mx-auto flex max-w-md flex-col gap-6">
758
+ <Progress value={((step + 1) / steps.length) * 100} className="h-1" />
759
+ <StepIndicator current={step} />
760
+ <Separator />
761
+
762
+ {step === 0 && (
763
+ <div className="flex flex-col gap-4">
764
+ <div className="grid gap-2">
765
+ <Label htmlFor="email">Email</Label>
766
+ <Input
767
+ id="email"
768
+ type="email"
769
+ placeholder="you@example.com"
770
+ value={values.email}
771
+ onChange={set('email')}
772
+ />
773
+ </div>
774
+ <div className="grid gap-2">
775
+ <Label htmlFor="password">Password</Label>
776
+ <Input
777
+ id="password"
778
+ type="password"
779
+ placeholder="••••••••"
780
+ value={values.password}
781
+ onChange={set('password')}
782
+ />
783
+ </div>
784
+ </div>
785
+ )}
786
+
787
+ {step === 1 && (
788
+ <div className="flex flex-col gap-4">
789
+ <div className="grid gap-2">
790
+ <Label htmlFor="name">Full name</Label>
791
+ <Input
792
+ id="name"
793
+ placeholder="Jane Smith"
794
+ value={values.name}
795
+ onChange={set('name')}
796
+ />
797
+ </div>
798
+ <div className="grid gap-2">
799
+ <Label htmlFor="bio">Bio</Label>
800
+ <Textarea
801
+ id="bio"
802
+ placeholder="Tell us a little about yourself…"
803
+ value={values.bio}
804
+ onChange={set('bio')}
805
+ />
806
+ </div>
807
+ </div>
808
+ )}
809
+
810
+ {step === 2 && (
811
+ <div className="flex flex-col gap-3 text-sm">
812
+ <p className="font-medium">Review your details</p>
813
+ {(
814
+ [
815
+ ['Email', values.email || '—'],
816
+ ['Password', values.password ? '••••••••' : '—'],
817
+ ['Name', values.name || '—'],
818
+ ['Bio', values.bio || '—'],
819
+ ] as [string, string][]
820
+ ).map(([label, value]) => (
821
+ <div key={label} className="flex justify-between">
822
+ <span className="text-muted-foreground">{label}</span>
823
+ <span>{value}</span>
824
+ </div>
825
+ ))}
826
+ </div>
827
+ )}
828
+
829
+ <div className="flex justify-between">
830
+ <Button variant="outline" onClick={() => setStep(s => s - 1)} disabled={step === 0}>
831
+ Back
832
+ </Button>
833
+ {step < steps.length - 1 ? (
834
+ <Button onClick={() => setStep(s => s + 1)}>Next</Button>
835
+ ) : (
836
+ <Button onClick={handleSubmit}>Create account</Button>
837
+ )}
838
+ </div>
839
+ </div>
840
+ </div>
841
+ );
842
+ }
843
+ ```
844
+
845
+ ### split pane
846
+ ```tsx
847
+ import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@coston/ui/resizable';
848
+ import { ScrollArea } from '@coston/ui/scroll-area';
849
+
850
+ const fileTree = [
851
+ 'src/',
852
+ ' components/',
853
+ ' Button.tsx',
854
+ ' Input.tsx',
855
+ ' pages/',
856
+ ' Home.tsx',
857
+ ' Settings.tsx',
858
+ 'package.json',
859
+ 'tsconfig.json',
860
+ ];
861
+
862
+ export function SplitPane() {
863
+ return (
864
+ <div className="flex flex-col gap-6 p-6">
865
+ <div>
866
+ <p className="mb-2 text-xs text-muted-foreground">Horizontal — file explorer + document</p>
867
+ <ResizablePanelGroup
868
+ orientation="horizontal"
869
+ className="h-[260px] rounded-lg border border-border"
870
+ >
871
+ <ResizablePanel defaultSize={30} minSize={20}>
872
+ <ScrollArea className="h-full">
873
+ <div className="flex flex-col gap-0.5 p-2">
874
+ {fileTree.map((item, i) => (
875
+ <div
876
+ key={i}
877
+ className="cursor-pointer rounded px-2 py-1 font-mono text-xs text-muted-foreground hover:bg-muted"
878
+ >
879
+ {item}
880
+ </div>
881
+ ))}
882
+ </div>
883
+ </ScrollArea>
884
+ </ResizablePanel>
885
+ <ResizableHandle withHandle />
886
+ <ResizablePanel defaultSize={70}>
887
+ <ScrollArea className="h-full">
888
+ <div className="p-4">
889
+ <h3 className="mb-2 text-sm font-semibold">Button.tsx</h3>
890
+ <p className="text-sm leading-relaxed text-muted-foreground">
891
+ Primary interactive element. Supports multiple variants (default, outline, ghost,
892
+ destructive) and sizes (sm, default, lg, icon). Built on Radix Slot for
893
+ polymorphic rendering.
894
+ </p>
895
+ </div>
896
+ </ScrollArea>
897
+ </ResizablePanel>
898
+ </ResizablePanelGroup>
899
+ </div>
900
+
901
+ <div>
902
+ <p className="mb-2 text-xs text-muted-foreground">Vertical — editor + preview</p>
903
+ <ResizablePanelGroup
904
+ orientation="vertical"
905
+ className="h-[300px] rounded-lg border border-border"
906
+ >
907
+ <ResizablePanel defaultSize={50}>
908
+ <ScrollArea className="h-full">
909
+ <pre className="p-4 font-mono text-xs text-muted-foreground">{`export function Hello() {
910
+ return (
911
+ <div className="p-4">
912
+ <h1>Hello, world!</h1>
913
+ <p>Edit this to see changes.</p>
914
+ </div>
915
+ );
916
+ }`}</pre>
917
+ </ScrollArea>
918
+ </ResizablePanel>
919
+ <ResizableHandle withHandle />
920
+ <ResizablePanel defaultSize={50}>
921
+ <div className="flex h-full items-start p-4">
922
+ <div>
923
+ <h1 className="text-lg font-bold">Hello, world!</h1>
924
+ <p className="text-sm text-muted-foreground">Edit this to see changes.</p>
925
+ </div>
926
+ </div>
927
+ </ResizablePanel>
928
+ </ResizablePanelGroup>
929
+ </div>
930
+ </div>
931
+ );
932
+ }
933
+ ```
934
+
935
+ ### toast feedback
936
+ ```tsx
937
+ import { toast } from 'sonner';
938
+ import { Button } from '@coston/ui/button';
939
+
940
+ const variants = [
941
+ { label: 'Default', action: () => toast('Default notification') },
942
+ { label: 'Success', action: () => toast.success('Changes saved successfully') },
943
+ { label: 'Error', action: () => toast.error('Something went wrong') },
944
+ { label: 'Warning', action: () => toast.warning('Low disk space') },
945
+ { label: 'Info', action: () => toast.info('Update available') },
946
+ {
947
+ label: 'With description',
948
+ action: () =>
949
+ toast('File uploaded', {
950
+ description: 'report.pdf was uploaded to /documents',
951
+ }),
952
+ },
953
+ {
954
+ label: 'With action',
955
+ action: () =>
956
+ toast('Message sent', {
957
+ action: { label: 'Undo', onClick: () => toast.success('Message unsent') },
958
+ }),
959
+ },
960
+ {
961
+ label: 'Loading → done',
962
+ action: () => {
963
+ const id = toast.loading('Saving…');
964
+ setTimeout(() => toast.success('Saved!', { id }), 2000);
965
+ },
966
+ },
967
+ {
968
+ label: 'Promise',
969
+ action: () =>
970
+ toast.promise(new Promise(resolve => setTimeout(resolve, 2000)), {
971
+ loading: 'Uploading file…',
972
+ success: 'Upload complete',
973
+ error: 'Upload failed',
974
+ }),
975
+ },
976
+ ];
977
+
978
+ export function ToastFeedback() {
979
+ return (
980
+ <div className="grid grid-cols-2 gap-2 p-6 sm:grid-cols-3">
981
+ {variants.map(({ label, action }) => (
982
+ <Button key={label} variant="outline" size="sm" onClick={action}>
983
+ {label}
984
+ </Button>
985
+ ))}
986
+ </div>
987
+ );
988
+ }
989
+ ```