omgkit 2.2.0 → 2.3.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.
Files changed (55) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/databases/mongodb/SKILL.md +60 -776
  3. package/plugin/skills/databases/prisma/SKILL.md +53 -744
  4. package/plugin/skills/databases/redis/SKILL.md +53 -860
  5. package/plugin/skills/devops/aws/SKILL.md +68 -672
  6. package/plugin/skills/devops/github-actions/SKILL.md +54 -657
  7. package/plugin/skills/devops/kubernetes/SKILL.md +67 -602
  8. package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
  9. package/plugin/skills/frameworks/django/SKILL.md +87 -853
  10. package/plugin/skills/frameworks/express/SKILL.md +95 -1301
  11. package/plugin/skills/frameworks/fastapi/SKILL.md +90 -1198
  12. package/plugin/skills/frameworks/laravel/SKILL.md +87 -1187
  13. package/plugin/skills/frameworks/nestjs/SKILL.md +106 -973
  14. package/plugin/skills/frameworks/react/SKILL.md +94 -962
  15. package/plugin/skills/frameworks/vue/SKILL.md +95 -1242
  16. package/plugin/skills/frontend/accessibility/SKILL.md +91 -1056
  17. package/plugin/skills/frontend/frontend-design/SKILL.md +69 -1262
  18. package/plugin/skills/frontend/responsive/SKILL.md +76 -799
  19. package/plugin/skills/frontend/shadcn-ui/SKILL.md +73 -921
  20. package/plugin/skills/frontend/tailwindcss/SKILL.md +60 -788
  21. package/plugin/skills/frontend/threejs/SKILL.md +72 -1266
  22. package/plugin/skills/languages/javascript/SKILL.md +106 -849
  23. package/plugin/skills/methodology/brainstorming/SKILL.md +70 -576
  24. package/plugin/skills/methodology/defense-in-depth/SKILL.md +79 -831
  25. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +81 -654
  26. package/plugin/skills/methodology/executing-plans/SKILL.md +86 -529
  27. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +95 -586
  28. package/plugin/skills/methodology/problem-solving/SKILL.md +67 -681
  29. package/plugin/skills/methodology/receiving-code-review/SKILL.md +70 -533
  30. package/plugin/skills/methodology/requesting-code-review/SKILL.md +70 -610
  31. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +70 -646
  32. package/plugin/skills/methodology/sequential-thinking/SKILL.md +70 -478
  33. package/plugin/skills/methodology/systematic-debugging/SKILL.md +66 -559
  34. package/plugin/skills/methodology/test-driven-development/SKILL.md +91 -752
  35. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +78 -687
  36. package/plugin/skills/methodology/token-optimization/SKILL.md +72 -602
  37. package/plugin/skills/methodology/verification-before-completion/SKILL.md +108 -529
  38. package/plugin/skills/methodology/writing-plans/SKILL.md +79 -566
  39. package/plugin/skills/omega/omega-architecture/SKILL.md +91 -752
  40. package/plugin/skills/omega/omega-coding/SKILL.md +161 -552
  41. package/plugin/skills/omega/omega-sprint/SKILL.md +132 -777
  42. package/plugin/skills/omega/omega-testing/SKILL.md +157 -845
  43. package/plugin/skills/omega/omega-thinking/SKILL.md +165 -606
  44. package/plugin/skills/security/better-auth/SKILL.md +46 -1034
  45. package/plugin/skills/security/oauth/SKILL.md +80 -934
  46. package/plugin/skills/security/owasp/SKILL.md +78 -862
  47. package/plugin/skills/testing/playwright/SKILL.md +77 -700
  48. package/plugin/skills/testing/pytest/SKILL.md +73 -811
  49. package/plugin/skills/testing/vitest/SKILL.md +60 -920
  50. package/plugin/skills/tools/document-processing/SKILL.md +111 -838
  51. package/plugin/skills/tools/image-processing/SKILL.md +126 -659
  52. package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
  53. package/plugin/skills/tools/media-processing/SKILL.md +118 -735
  54. package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
  55. package/plugin/skills/SKILL_STANDARDS.md +0 -743
@@ -1,442 +1,83 @@
1
1
  ---
2
- name: shadcn-ui
3
- description: shadcn/ui component library with accessible React components, theming, and form integration
4
- category: frontend
5
- triggers:
6
- - shadcn
7
- - shadcn/ui
8
- - ui components
9
- - radix ui
10
- - react components
2
+ name: building-with-shadcn
3
+ description: Claude builds accessible React UIs using shadcn/ui components with Radix primitives and React Hook Form integration. Use when creating forms, dialogs, or composable UI systems.
11
4
  ---
12
5
 
13
- # shadcn/ui
6
+ # Building with shadcn/ui
14
7
 
15
- Enterprise-grade **React component library** built on Radix UI primitives following industry best practices. This skill covers component installation, theming, form integration, customization, and accessibility patterns used by top engineering teams.
16
-
17
- ## Purpose
18
-
19
- Build beautiful, accessible React applications:
20
-
21
- - Install and configure components
22
- - Customize themes and variants
23
- - Integrate with React Hook Form
24
- - Build complex UI patterns
25
- - Extend component functionality
26
- - Maintain accessibility standards
27
- - Create consistent design systems
28
-
29
- ## Features
30
-
31
- ### 1. Installation and Setup
8
+ ## Quick Start
32
9
 
33
10
  ```bash
34
- # Initialize shadcn/ui in your project
11
+ # Initialize and add components
35
12
  npx shadcn-ui@latest init
36
-
37
- # Add individual components
38
- npx shadcn-ui@latest add button
39
- npx shadcn-ui@latest add card
40
- npx shadcn-ui@latest add form
41
- npx shadcn-ui@latest add input
42
- npx shadcn-ui@latest add dialog
43
- npx shadcn-ui@latest add dropdown-menu
44
- npx shadcn-ui@latest add table
45
- npx shadcn-ui@latest add toast
46
-
47
- # Add multiple components at once
48
- npx shadcn-ui@latest add button card input form
13
+ npx shadcn-ui@latest add button card form input dialog
49
14
  ```
50
15
 
51
- ```json
52
- // components.json
53
- {
54
- "$schema": "https://ui.shadcn.com/schema.json",
55
- "style": "default",
56
- "rsc": true,
57
- "tsx": true,
58
- "tailwind": {
59
- "config": "tailwind.config.ts",
60
- "css": "app/globals.css",
61
- "baseColor": "slate",
62
- "cssVariables": true,
63
- "prefix": ""
64
- },
65
- "aliases": {
66
- "components": "@/components",
67
- "utils": "@/lib/utils"
68
- }
69
- }
70
- ```
71
-
72
- ```typescript
73
- // lib/utils.ts
74
- import { type ClassValue, clsx } from "clsx";
75
- import { twMerge } from "tailwind-merge";
76
-
77
- export function cn(...inputs: ClassValue[]) {
78
- return twMerge(clsx(inputs));
79
- }
80
- ```
81
-
82
- ### 2. Core Components
83
-
84
16
  ```tsx
85
- // Button usage with variants
86
17
  import { Button } from "@/components/ui/button";
18
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
87
19
 
88
- export function ButtonExamples() {
89
- return (
90
- <div className="flex flex-wrap gap-4">
91
- <Button>Default</Button>
92
- <Button variant="secondary">Secondary</Button>
93
- <Button variant="destructive">Destructive</Button>
94
- <Button variant="outline">Outline</Button>
95
- <Button variant="ghost">Ghost</Button>
96
- <Button variant="link">Link</Button>
97
-
98
- {/* Sizes */}
99
- <Button size="sm">Small</Button>
100
- <Button size="default">Default</Button>
101
- <Button size="lg">Large</Button>
102
- <Button size="icon">
103
- <PlusIcon className="h-4 w-4" />
104
- </Button>
105
-
106
- {/* With loading state */}
107
- <Button disabled>
108
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
109
- Please wait
110
- </Button>
111
-
112
- {/* As child (for links) */}
113
- <Button asChild>
114
- <a href="/dashboard">Go to Dashboard</a>
115
- </Button>
116
- </div>
117
- );
118
- }
119
-
120
- // Card component
121
- import {
122
- Card,
123
- CardContent,
124
- CardDescription,
125
- CardFooter,
126
- CardHeader,
127
- CardTitle,
128
- } from "@/components/ui/card";
129
-
130
- export function CardExample() {
20
+ export function Example() {
131
21
  return (
132
- <Card className="w-[350px]">
22
+ <Card>
133
23
  <CardHeader>
134
- <CardTitle>Create project</CardTitle>
135
- <CardDescription>Deploy your new project in one-click.</CardDescription>
24
+ <CardTitle>Welcome</CardTitle>
136
25
  </CardHeader>
137
- <CardContent>
138
- <form>
139
- <div className="grid w-full items-center gap-4">
140
- <div className="flex flex-col space-y-1.5">
141
- <Label htmlFor="name">Name</Label>
142
- <Input id="name" placeholder="Name of your project" />
143
- </div>
144
- <div className="flex flex-col space-y-1.5">
145
- <Label htmlFor="framework">Framework</Label>
146
- <Select>
147
- <SelectTrigger id="framework">
148
- <SelectValue placeholder="Select" />
149
- </SelectTrigger>
150
- <SelectContent position="popper">
151
- <SelectItem value="next">Next.js</SelectItem>
152
- <SelectItem value="sveltekit">SvelteKit</SelectItem>
153
- <SelectItem value="astro">Astro</SelectItem>
154
- <SelectItem value="nuxt">Nuxt.js</SelectItem>
155
- </SelectContent>
156
- </Select>
157
- </div>
158
- </div>
159
- </form>
26
+ <CardContent className="flex gap-4">
27
+ <Button>Primary</Button>
28
+ <Button variant="outline">Outline</Button>
160
29
  </CardContent>
161
- <CardFooter className="flex justify-between">
162
- <Button variant="outline">Cancel</Button>
163
- <Button>Deploy</Button>
164
- </CardFooter>
165
30
  </Card>
166
31
  );
167
32
  }
168
-
169
- // Input component
170
- import { Input } from "@/components/ui/input";
171
- import { Label } from "@/components/ui/label";
172
-
173
- export function InputWithLabel() {
174
- return (
175
- <div className="grid w-full max-w-sm items-center gap-1.5">
176
- <Label htmlFor="email">Email</Label>
177
- <Input type="email" id="email" placeholder="Email" />
178
- </div>
179
- );
180
- }
181
-
182
- export function InputWithButton() {
183
- return (
184
- <div className="flex w-full max-w-sm items-center space-x-2">
185
- <Input type="email" placeholder="Email" />
186
- <Button type="submit">Subscribe</Button>
187
- </div>
188
- );
189
- }
190
33
  ```
191
34
 
192
- ### 3. Form Integration with React Hook Form
35
+ ## Features
193
36
 
194
- ```tsx
195
- // Complete form example with validation
196
- "use client";
37
+ | Feature | Description | Guide |
38
+ |---------|-------------|-------|
39
+ | Button Variants | default, secondary, destructive, outline, ghost, link | `ref/button.md` |
40
+ | Form Integration | React Hook Form + Zod validation pattern | `ref/forms.md` |
41
+ | Dialog/Sheet | Modal dialogs and slide-out panels | `ref/dialogs.md` |
42
+ | Data Display | Table, Tabs, Accordion components | `ref/data-display.md` |
43
+ | Navigation | DropdownMenu, Command palette, NavigationMenu | `ref/navigation.md` |
44
+ | Feedback | Toast notifications with useToast hook | `ref/toast.md` |
197
45
 
198
- import { zodResolver } from "@hookform/resolvers/zod";
199
- import { useForm } from "react-hook-form";
200
- import * as z from "zod";
46
+ ## Common Patterns
201
47
 
202
- import { Button } from "@/components/ui/button";
203
- import {
204
- Form,
205
- FormControl,
206
- FormDescription,
207
- FormField,
208
- FormItem,
209
- FormLabel,
210
- FormMessage,
211
- } from "@/components/ui/form";
212
- import { Input } from "@/components/ui/input";
213
- import {
214
- Select,
215
- SelectContent,
216
- SelectItem,
217
- SelectTrigger,
218
- SelectValue,
219
- } from "@/components/ui/select";
220
- import { Textarea } from "@/components/ui/textarea";
221
- import { Checkbox } from "@/components/ui/checkbox";
222
- import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
223
- import { Switch } from "@/components/ui/switch";
224
- import { toast } from "@/components/ui/use-toast";
48
+ ### Form with Validation
225
49
 
50
+ ```tsx
226
51
  const formSchema = z.object({
227
- username: z
228
- .string()
229
- .min(2, "Username must be at least 2 characters.")
230
- .max(30, "Username must not exceed 30 characters."),
231
- email: z.string().email("Please enter a valid email address."),
232
- bio: z
233
- .string()
234
- .max(160, "Bio must not exceed 160 characters.")
235
- .optional(),
236
- role: z.enum(["admin", "user", "guest"], {
237
- required_error: "Please select a role.",
238
- }),
239
- notifications: z.boolean().default(false),
240
- marketingEmails: z.boolean().default(false),
241
- securityEmails: z.boolean().default(true),
242
- communicationMethod: z.enum(["email", "sms", "push"], {
243
- required_error: "Please select a communication method.",
244
- }),
52
+ email: z.string().email("Invalid email"),
53
+ name: z.string().min(2, "Name must be at least 2 characters"),
245
54
  });
246
55
 
247
- type FormData = z.infer<typeof formSchema>;
248
-
249
56
  export function ProfileForm() {
250
- const form = useForm<FormData>({
57
+ const form = useForm<z.infer<typeof formSchema>>({
251
58
  resolver: zodResolver(formSchema),
252
- defaultValues: {
253
- username: "",
254
- email: "",
255
- bio: "",
256
- notifications: false,
257
- marketingEmails: false,
258
- securityEmails: true,
259
- },
59
+ defaultValues: { email: "", name: "" },
260
60
  });
261
61
 
262
- async function onSubmit(data: FormData) {
263
- try {
264
- // API call here
265
- console.log(data);
266
- toast({
267
- title: "Profile updated",
268
- description: "Your profile has been updated successfully.",
269
- });
270
- } catch (error) {
271
- toast({
272
- title: "Error",
273
- description: "Something went wrong. Please try again.",
274
- variant: "destructive",
275
- });
276
- }
277
- }
278
-
279
62
  return (
280
63
  <Form {...form}>
281
- <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
282
- <FormField
283
- control={form.control}
284
- name="username"
285
- render={({ field }) => (
286
- <FormItem>
287
- <FormLabel>Username</FormLabel>
288
- <FormControl>
289
- <Input placeholder="johndoe" {...field} />
290
- </FormControl>
291
- <FormDescription>
292
- This is your public display name.
293
- </FormDescription>
294
- <FormMessage />
295
- </FormItem>
296
- )}
297
- />
298
-
299
- <FormField
300
- control={form.control}
301
- name="email"
302
- render={({ field }) => (
303
- <FormItem>
304
- <FormLabel>Email</FormLabel>
305
- <FormControl>
306
- <Input type="email" placeholder="john@example.com" {...field} />
307
- </FormControl>
308
- <FormMessage />
309
- </FormItem>
310
- )}
311
- />
312
-
313
- <FormField
314
- control={form.control}
315
- name="bio"
316
- render={({ field }) => (
317
- <FormItem>
318
- <FormLabel>Bio</FormLabel>
319
- <FormControl>
320
- <Textarea
321
- placeholder="Tell us a little about yourself"
322
- className="resize-none"
323
- {...field}
324
- />
325
- </FormControl>
326
- <FormDescription>
327
- You can @mention other users and organizations.
328
- </FormDescription>
329
- <FormMessage />
330
- </FormItem>
331
- )}
332
- />
333
-
334
- <FormField
335
- control={form.control}
336
- name="role"
337
- render={({ field }) => (
338
- <FormItem>
339
- <FormLabel>Role</FormLabel>
340
- <Select onValueChange={field.onChange} defaultValue={field.value}>
341
- <FormControl>
342
- <SelectTrigger>
343
- <SelectValue placeholder="Select a role" />
344
- </SelectTrigger>
345
- </FormControl>
346
- <SelectContent>
347
- <SelectItem value="admin">Admin</SelectItem>
348
- <SelectItem value="user">User</SelectItem>
349
- <SelectItem value="guest">Guest</SelectItem>
350
- </SelectContent>
351
- </Select>
352
- <FormMessage />
353
- </FormItem>
354
- )}
355
- />
356
-
357
- <FormField
358
- control={form.control}
359
- name="communicationMethod"
360
- render={({ field }) => (
361
- <FormItem className="space-y-3">
362
- <FormLabel>Notify me via</FormLabel>
363
- <FormControl>
364
- <RadioGroup
365
- onValueChange={field.onChange}
366
- defaultValue={field.value}
367
- className="flex flex-col space-y-1"
368
- >
369
- <FormItem className="flex items-center space-x-3 space-y-0">
370
- <FormControl>
371
- <RadioGroupItem value="email" />
372
- </FormControl>
373
- <FormLabel className="font-normal">Email</FormLabel>
374
- </FormItem>
375
- <FormItem className="flex items-center space-x-3 space-y-0">
376
- <FormControl>
377
- <RadioGroupItem value="sms" />
378
- </FormControl>
379
- <FormLabel className="font-normal">SMS</FormLabel>
380
- </FormItem>
381
- <FormItem className="flex items-center space-x-3 space-y-0">
382
- <FormControl>
383
- <RadioGroupItem value="push" />
384
- </FormControl>
385
- <FormLabel className="font-normal">Push notification</FormLabel>
386
- </FormItem>
387
- </RadioGroup>
388
- </FormControl>
389
- <FormMessage />
390
- </FormItem>
391
- )}
392
- />
393
-
394
- <div className="space-y-4">
395
- <FormField
396
- control={form.control}
397
- name="notifications"
398
- render={({ field }) => (
399
- <FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
400
- <div className="space-y-0.5">
401
- <FormLabel className="text-base">Push Notifications</FormLabel>
402
- <FormDescription>
403
- Receive push notifications on your device.
404
- </FormDescription>
405
- </div>
406
- <FormControl>
407
- <Switch
408
- checked={field.value}
409
- onCheckedChange={field.onChange}
410
- />
411
- </FormControl>
412
- </FormItem>
413
- )}
414
- />
415
-
416
- <FormField
417
- control={form.control}
418
- name="marketingEmails"
419
- render={({ field }) => (
420
- <FormItem className="flex flex-row items-start space-x-3 space-y-0">
421
- <FormControl>
422
- <Checkbox
423
- checked={field.value}
424
- onCheckedChange={field.onChange}
425
- />
426
- </FormControl>
427
- <div className="space-y-1 leading-none">
428
- <FormLabel>Marketing emails</FormLabel>
429
- <FormDescription>
430
- Receive emails about new products and features.
431
- </FormDescription>
432
- </div>
433
- </FormItem>
434
- )}
435
- />
436
- </div>
437
-
64
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
65
+ <FormField control={form.control} name="email" render={({ field }) => (
66
+ <FormItem>
67
+ <FormLabel>Email</FormLabel>
68
+ <FormControl><Input {...field} /></FormControl>
69
+ <FormMessage />
70
+ </FormItem>
71
+ )} />
72
+ <FormField control={form.control} name="name" render={({ field }) => (
73
+ <FormItem>
74
+ <FormLabel>Name</FormLabel>
75
+ <FormControl><Input {...field} /></FormControl>
76
+ <FormMessage />
77
+ </FormItem>
78
+ )} />
438
79
  <Button type="submit" disabled={form.formState.isSubmitting}>
439
- {form.formState.isSubmitting ? "Saving..." : "Save changes"}
80
+ {form.formState.isSubmitting ? "Saving..." : "Save"}
440
81
  </Button>
441
82
  </form>
442
83
  </Form>
@@ -444,553 +85,64 @@ export function ProfileForm() {
444
85
  }
445
86
  ```
446
87
 
447
- ### 4. Dialog and Modals
88
+ ### Dialog with Form
448
89
 
449
90
  ```tsx
450
- // Dialog component
451
- import {
452
- Dialog,
453
- DialogContent,
454
- DialogDescription,
455
- DialogFooter,
456
- DialogHeader,
457
- DialogTitle,
458
- DialogTrigger,
459
- DialogClose,
460
- } from "@/components/ui/dialog";
461
-
462
- export function DialogExample() {
91
+ export function EditDialog({ onSave }: { onSave: (data: Data) => void }) {
463
92
  return (
464
93
  <Dialog>
465
94
  <DialogTrigger asChild>
466
95
  <Button variant="outline">Edit Profile</Button>
467
96
  </DialogTrigger>
468
- <DialogContent className="sm:max-w-[425px]">
97
+ <DialogContent>
469
98
  <DialogHeader>
470
- <DialogTitle>Edit profile</DialogTitle>
471
- <DialogDescription>
472
- Make changes to your profile here. Click save when you're done.
473
- </DialogDescription>
99
+ <DialogTitle>Edit Profile</DialogTitle>
100
+ <DialogDescription>Update your profile information.</DialogDescription>
474
101
  </DialogHeader>
475
102
  <div className="grid gap-4 py-4">
476
103
  <div className="grid grid-cols-4 items-center gap-4">
477
- <Label htmlFor="name" className="text-right">
478
- Name
479
- </Label>
480
- <Input
481
- id="name"
482
- defaultValue="Pedro Duarte"
483
- className="col-span-3"
484
- />
485
- </div>
486
- <div className="grid grid-cols-4 items-center gap-4">
487
- <Label htmlFor="username" className="text-right">
488
- Username
489
- </Label>
490
- <Input
491
- id="username"
492
- defaultValue="@peduarte"
493
- className="col-span-3"
494
- />
104
+ <Label htmlFor="name" className="text-right">Name</Label>
105
+ <Input id="name" className="col-span-3" />
495
106
  </div>
496
107
  </div>
497
108
  <DialogFooter>
498
- <DialogClose asChild>
499
- <Button variant="outline">Cancel</Button>
500
- </DialogClose>
501
- <Button type="submit">Save changes</Button>
109
+ <DialogClose asChild><Button variant="outline">Cancel</Button></DialogClose>
110
+ <Button onClick={() => onSave(data)}>Save</Button>
502
111
  </DialogFooter>
503
112
  </DialogContent>
504
113
  </Dialog>
505
114
  );
506
115
  }
507
-
508
- // Alert Dialog for confirmations
509
- import {
510
- AlertDialog,
511
- AlertDialogAction,
512
- AlertDialogCancel,
513
- AlertDialogContent,
514
- AlertDialogDescription,
515
- AlertDialogFooter,
516
- AlertDialogHeader,
517
- AlertDialogTitle,
518
- AlertDialogTrigger,
519
- } from "@/components/ui/alert-dialog";
520
-
521
- export function DeleteConfirmation({ onDelete }: { onDelete: () => void }) {
522
- return (
523
- <AlertDialog>
524
- <AlertDialogTrigger asChild>
525
- <Button variant="destructive">Delete</Button>
526
- </AlertDialogTrigger>
527
- <AlertDialogContent>
528
- <AlertDialogHeader>
529
- <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
530
- <AlertDialogDescription>
531
- This action cannot be undone. This will permanently delete your
532
- account and remove your data from our servers.
533
- </AlertDialogDescription>
534
- </AlertDialogHeader>
535
- <AlertDialogFooter>
536
- <AlertDialogCancel>Cancel</AlertDialogCancel>
537
- <AlertDialogAction onClick={onDelete}>
538
- Yes, delete account
539
- </AlertDialogAction>
540
- </AlertDialogFooter>
541
- </AlertDialogContent>
542
- </AlertDialog>
543
- );
544
- }
545
-
546
- // Sheet (slide-out panel)
547
- import {
548
- Sheet,
549
- SheetClose,
550
- SheetContent,
551
- SheetDescription,
552
- SheetFooter,
553
- SheetHeader,
554
- SheetTitle,
555
- SheetTrigger,
556
- } from "@/components/ui/sheet";
557
-
558
- export function SheetExample() {
559
- return (
560
- <Sheet>
561
- <SheetTrigger asChild>
562
- <Button variant="outline">Open Settings</Button>
563
- </SheetTrigger>
564
- <SheetContent>
565
- <SheetHeader>
566
- <SheetTitle>Edit settings</SheetTitle>
567
- <SheetDescription>
568
- Make changes to your settings here.
569
- </SheetDescription>
570
- </SheetHeader>
571
- <div className="grid gap-4 py-4">
572
- {/* Form fields */}
573
- </div>
574
- <SheetFooter>
575
- <SheetClose asChild>
576
- <Button type="submit">Save changes</Button>
577
- </SheetClose>
578
- </SheetFooter>
579
- </SheetContent>
580
- </Sheet>
581
- );
582
- }
583
- ```
584
-
585
- ### 5. Data Display Components
586
-
587
- ```tsx
588
- // Table component
589
- import {
590
- Table,
591
- TableBody,
592
- TableCaption,
593
- TableCell,
594
- TableHead,
595
- TableHeader,
596
- TableRow,
597
- } from "@/components/ui/table";
598
-
599
- interface Invoice {
600
- id: string;
601
- paymentStatus: string;
602
- totalAmount: number;
603
- paymentMethod: string;
604
- }
605
-
606
- export function DataTable({ invoices }: { invoices: Invoice[] }) {
607
- return (
608
- <Table>
609
- <TableCaption>A list of your recent invoices.</TableCaption>
610
- <TableHeader>
611
- <TableRow>
612
- <TableHead className="w-[100px]">Invoice</TableHead>
613
- <TableHead>Status</TableHead>
614
- <TableHead>Method</TableHead>
615
- <TableHead className="text-right">Amount</TableHead>
616
- </TableRow>
617
- </TableHeader>
618
- <TableBody>
619
- {invoices.map((invoice) => (
620
- <TableRow key={invoice.id}>
621
- <TableCell className="font-medium">{invoice.id}</TableCell>
622
- <TableCell>
623
- <Badge
624
- variant={
625
- invoice.paymentStatus === "paid" ? "default" : "secondary"
626
- }
627
- >
628
- {invoice.paymentStatus}
629
- </Badge>
630
- </TableCell>
631
- <TableCell>{invoice.paymentMethod}</TableCell>
632
- <TableCell className="text-right">
633
- ${invoice.totalAmount.toFixed(2)}
634
- </TableCell>
635
- </TableRow>
636
- ))}
637
- </TableBody>
638
- </Table>
639
- );
640
- }
641
-
642
- // Tabs component
643
- import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
644
-
645
- export function TabsExample() {
646
- return (
647
- <Tabs defaultValue="account" className="w-[400px]">
648
- <TabsList className="grid w-full grid-cols-2">
649
- <TabsTrigger value="account">Account</TabsTrigger>
650
- <TabsTrigger value="password">Password</TabsTrigger>
651
- </TabsList>
652
- <TabsContent value="account">
653
- <Card>
654
- <CardHeader>
655
- <CardTitle>Account</CardTitle>
656
- <CardDescription>
657
- Make changes to your account here.
658
- </CardDescription>
659
- </CardHeader>
660
- <CardContent className="space-y-2">
661
- <div className="space-y-1">
662
- <Label htmlFor="name">Name</Label>
663
- <Input id="name" defaultValue="Pedro Duarte" />
664
- </div>
665
- <div className="space-y-1">
666
- <Label htmlFor="username">Username</Label>
667
- <Input id="username" defaultValue="@peduarte" />
668
- </div>
669
- </CardContent>
670
- <CardFooter>
671
- <Button>Save changes</Button>
672
- </CardFooter>
673
- </Card>
674
- </TabsContent>
675
- <TabsContent value="password">
676
- <Card>
677
- <CardHeader>
678
- <CardTitle>Password</CardTitle>
679
- <CardDescription>Change your password here.</CardDescription>
680
- </CardHeader>
681
- <CardContent className="space-y-2">
682
- <div className="space-y-1">
683
- <Label htmlFor="current">Current password</Label>
684
- <Input id="current" type="password" />
685
- </div>
686
- <div className="space-y-1">
687
- <Label htmlFor="new">New password</Label>
688
- <Input id="new" type="password" />
689
- </div>
690
- </CardContent>
691
- <CardFooter>
692
- <Button>Save password</Button>
693
- </CardFooter>
694
- </Card>
695
- </TabsContent>
696
- </Tabs>
697
- );
698
- }
699
- ```
700
-
701
- ### 6. Navigation Components
702
-
703
- ```tsx
704
- // Dropdown Menu
705
- import {
706
- DropdownMenu,
707
- DropdownMenuContent,
708
- DropdownMenuItem,
709
- DropdownMenuLabel,
710
- DropdownMenuSeparator,
711
- DropdownMenuShortcut,
712
- DropdownMenuTrigger,
713
- DropdownMenuSub,
714
- DropdownMenuSubContent,
715
- DropdownMenuSubTrigger,
716
- } from "@/components/ui/dropdown-menu";
717
-
718
- export function UserMenu() {
719
- return (
720
- <DropdownMenu>
721
- <DropdownMenuTrigger asChild>
722
- <Button variant="ghost" className="relative h-8 w-8 rounded-full">
723
- <Avatar className="h-8 w-8">
724
- <AvatarImage src="/avatars/01.png" alt="@username" />
725
- <AvatarFallback>JD</AvatarFallback>
726
- </Avatar>
727
- </Button>
728
- </DropdownMenuTrigger>
729
- <DropdownMenuContent className="w-56" align="end" forceMount>
730
- <DropdownMenuLabel className="font-normal">
731
- <div className="flex flex-col space-y-1">
732
- <p className="text-sm font-medium leading-none">John Doe</p>
733
- <p className="text-xs leading-none text-muted-foreground">
734
- john@example.com
735
- </p>
736
- </div>
737
- </DropdownMenuLabel>
738
- <DropdownMenuSeparator />
739
- <DropdownMenuItem>
740
- <User className="mr-2 h-4 w-4" />
741
- Profile
742
- <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
743
- </DropdownMenuItem>
744
- <DropdownMenuItem>
745
- <Settings className="mr-2 h-4 w-4" />
746
- Settings
747
- <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
748
- </DropdownMenuItem>
749
- <DropdownMenuSeparator />
750
- <DropdownMenuItem className="text-red-600">
751
- <LogOut className="mr-2 h-4 w-4" />
752
- Log out
753
- <DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
754
- </DropdownMenuItem>
755
- </DropdownMenuContent>
756
- </DropdownMenu>
757
- );
758
- }
759
-
760
- // Command Menu (CMD+K)
761
- import {
762
- CommandDialog,
763
- CommandEmpty,
764
- CommandGroup,
765
- CommandInput,
766
- CommandItem,
767
- CommandList,
768
- CommandSeparator,
769
- CommandShortcut,
770
- } from "@/components/ui/command";
771
-
772
- export function CommandMenu() {
773
- const [open, setOpen] = useState(false);
774
-
775
- useEffect(() => {
776
- const down = (e: KeyboardEvent) => {
777
- if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
778
- e.preventDefault();
779
- setOpen((open) => !open);
780
- }
781
- };
782
-
783
- document.addEventListener("keydown", down);
784
- return () => document.removeEventListener("keydown", down);
785
- }, []);
786
-
787
- return (
788
- <CommandDialog open={open} onOpenChange={setOpen}>
789
- <CommandInput placeholder="Type a command or search..." />
790
- <CommandList>
791
- <CommandEmpty>No results found.</CommandEmpty>
792
- <CommandGroup heading="Suggestions">
793
- <CommandItem>
794
- <Calendar className="mr-2 h-4 w-4" />
795
- Calendar
796
- </CommandItem>
797
- <CommandItem>
798
- <Search className="mr-2 h-4 w-4" />
799
- Search
800
- </CommandItem>
801
- </CommandGroup>
802
- <CommandSeparator />
803
- <CommandGroup heading="Settings">
804
- <CommandItem>
805
- <User className="mr-2 h-4 w-4" />
806
- Profile
807
- <CommandShortcut>⌘P</CommandShortcut>
808
- </CommandItem>
809
- <CommandItem>
810
- <Settings className="mr-2 h-4 w-4" />
811
- Settings
812
- <CommandShortcut>⌘S</CommandShortcut>
813
- </CommandItem>
814
- </CommandGroup>
815
- </CommandList>
816
- </CommandDialog>
817
- );
818
- }
819
116
  ```
820
117
 
821
- ### 7. Toast Notifications
118
+ ### Toast Notifications
822
119
 
823
120
  ```tsx
824
- // Toast setup and usage
825
- // components/ui/toaster.tsx
826
- "use client";
827
-
828
- import {
829
- Toast,
830
- ToastClose,
831
- ToastDescription,
832
- ToastProvider,
833
- ToastTitle,
834
- ToastViewport,
835
- } from "@/components/ui/toast";
836
121
  import { useToast } from "@/components/ui/use-toast";
837
122
 
838
- export function Toaster() {
839
- const { toasts } = useToast();
840
-
841
- return (
842
- <ToastProvider>
843
- {toasts.map(function ({ id, title, description, action, ...props }) {
844
- return (
845
- <Toast key={id} {...props}>
846
- <div className="grid gap-1">
847
- {title && <ToastTitle>{title}</ToastTitle>}
848
- {description && (
849
- <ToastDescription>{description}</ToastDescription>
850
- )}
851
- </div>
852
- {action}
853
- <ToastClose />
854
- </Toast>
855
- );
856
- })}
857
- <ToastViewport />
858
- </ToastProvider>
859
- );
860
- }
861
-
862
- // Usage in components
863
- import { useToast } from "@/components/ui/use-toast";
864
- import { ToastAction } from "@/components/ui/toast";
865
-
866
- export function ToastDemo() {
123
+ export function SaveButton() {
867
124
  const { toast } = useToast();
868
125
 
869
- return (
870
- <div className="space-x-4">
871
- <Button
872
- onClick={() => {
873
- toast({
874
- title: "Success!",
875
- description: "Your changes have been saved.",
876
- });
877
- }}
878
- >
879
- Show Toast
880
- </Button>
881
-
882
- <Button
883
- variant="destructive"
884
- onClick={() => {
885
- toast({
886
- variant: "destructive",
887
- title: "Uh oh! Something went wrong.",
888
- description: "There was a problem with your request.",
889
- action: <ToastAction altText="Try again">Try again</ToastAction>,
890
- });
891
- }}
892
- >
893
- Show Error Toast
894
- </Button>
895
- </div>
896
- );
897
- }
898
- ```
899
-
900
- ## Use Cases
901
-
902
- ### Complete Dashboard Page
903
-
904
- ```tsx
905
- // app/dashboard/page.tsx
906
- import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
907
- import { DataTable } from "@/components/data-table";
908
- import { UserMenu } from "@/components/user-menu";
909
-
910
- export default function DashboardPage() {
911
- return (
912
- <div className="flex min-h-screen flex-col">
913
- <header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
914
- <div className="container flex h-14 items-center">
915
- <div className="mr-4 flex">
916
- <a className="mr-6 flex items-center space-x-2" href="/">
917
- <span className="font-bold">Dashboard</span>
918
- </a>
919
- </div>
920
- <div className="flex flex-1 items-center justify-end space-x-2">
921
- <UserMenu />
922
- </div>
923
- </div>
924
- </header>
925
-
926
- <main className="flex-1 space-y-4 p-8 pt-6">
927
- <div className="flex items-center justify-between space-y-2">
928
- <h2 className="text-3xl font-bold tracking-tight">Dashboard</h2>
929
- <Button>Download Report</Button>
930
- </div>
931
-
932
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
933
- <Card>
934
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
935
- <CardTitle className="text-sm font-medium">Total Revenue</CardTitle>
936
- <DollarSign className="h-4 w-4 text-muted-foreground" />
937
- </CardHeader>
938
- <CardContent>
939
- <div className="text-2xl font-bold">$45,231.89</div>
940
- <p className="text-xs text-muted-foreground">
941
- +20.1% from last month
942
- </p>
943
- </CardContent>
944
- </Card>
945
- {/* More cards... */}
946
- </div>
126
+ const handleSave = async () => {
127
+ try {
128
+ await saveData();
129
+ toast({ title: "Success", description: "Changes saved." });
130
+ } catch {
131
+ toast({ variant: "destructive", title: "Error", description: "Failed to save." });
132
+ }
133
+ };
947
134
 
948
- <Card>
949
- <CardHeader>
950
- <CardTitle>Recent Orders</CardTitle>
951
- </CardHeader>
952
- <CardContent>
953
- <DataTable />
954
- </CardContent>
955
- </Card>
956
- </main>
957
- </div>
958
- );
135
+ return <Button onClick={handleSave}>Save</Button>;
959
136
  }
960
137
  ```
961
138
 
962
139
  ## Best Practices
963
140
 
964
- ### Do's
965
-
966
- - Install only components you need
967
- - Use the cn() utility for class merging
968
- - Extend components with composition
969
- - Follow form validation patterns
970
- - Use proper ARIA attributes
971
- - Customize via CSS variables
972
- - Keep components accessible
973
- - Use TypeScript for type safety
974
- - Follow React Hook Form patterns
975
- - Test component accessibility
976
-
977
- ### Don'ts
978
-
979
- - Don't modify generated component files directly
980
- - Don't skip form validation
981
- - Don't ignore accessibility features
982
- - Don't override styles without reason
983
- - Don't use inline styles
984
- - Don't skip loading states
985
- - Don't ignore error handling
986
- - Don't hardcode text strings
987
- - Don't skip responsive testing
988
- - Don't ignore keyboard navigation
989
-
990
- ## References
991
-
992
- - [shadcn/ui Documentation](https://ui.shadcn.com/)
993
- - [Radix UI Primitives](https://www.radix-ui.com/)
994
- - [React Hook Form](https://react-hook-form.com/)
995
- - [Zod Validation](https://zod.dev/)
996
- - [Tailwind CSS](https://tailwindcss.com/)
141
+ | Do | Avoid |
142
+ |----|-------|
143
+ | Install only components you need | Modifying generated component files directly |
144
+ | Use `cn()` utility for class merging | Skipping form validation |
145
+ | Extend components with composition | Overriding styles without good reason |
146
+ | Follow React Hook Form patterns | Using inline styles |
147
+ | Use TypeScript for type safety | Skipping loading and error states |
148
+ | Test component accessibility | Ignoring keyboard navigation |