start-vibing 2.0.11 → 2.0.13
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/README.md +177 -177
- package/dist/cli.js +19 -2
- package/package.json +42 -42
- package/template/.claude/CLAUDE.md +174 -174
- package/template/.claude/agents/01-orchestration/agent-selector.md +130 -130
- package/template/.claude/agents/01-orchestration/checkpoint-manager.md +142 -142
- package/template/.claude/agents/01-orchestration/context-manager.md +138 -138
- package/template/.claude/agents/01-orchestration/error-recovery.md +182 -182
- package/template/.claude/agents/01-orchestration/orchestrator.md +114 -114
- package/template/.claude/agents/01-orchestration/parallel-coordinator.md +141 -141
- package/template/.claude/agents/01-orchestration/task-decomposer.md +121 -121
- package/template/.claude/agents/01-orchestration/workflow-router.md +114 -114
- package/template/.claude/agents/02-typescript/bun-runtime-expert.md +197 -197
- package/template/.claude/agents/02-typescript/esm-resolver.md +193 -193
- package/template/.claude/agents/02-typescript/import-alias-enforcer.md +158 -158
- package/template/.claude/agents/02-typescript/ts-generics-helper.md +183 -183
- package/template/.claude/agents/02-typescript/ts-migration-helper.md +238 -238
- package/template/.claude/agents/02-typescript/ts-strict-checker.md +180 -180
- package/template/.claude/agents/02-typescript/ts-types-analyzer.md +199 -199
- package/template/.claude/agents/02-typescript/type-definition-writer.md +187 -187
- package/template/.claude/agents/02-typescript/zod-schema-designer.md +212 -212
- package/template/.claude/agents/02-typescript/zod-validator.md +158 -158
- package/template/.claude/agents/03-testing/playwright-assertions.md +265 -265
- package/template/.claude/agents/03-testing/playwright-e2e.md +247 -247
- package/template/.claude/agents/03-testing/playwright-fixtures.md +234 -234
- package/template/.claude/agents/03-testing/playwright-multi-viewport.md +256 -256
- package/template/.claude/agents/03-testing/playwright-page-objects.md +247 -247
- package/template/.claude/agents/03-testing/test-cleanup-manager.md +248 -248
- package/template/.claude/agents/03-testing/test-data-generator.md +254 -254
- package/template/.claude/agents/03-testing/tester-integration.md +278 -278
- package/template/.claude/agents/03-testing/tester-unit.md +207 -207
- package/template/.claude/agents/03-testing/vitest-config.md +287 -287
- package/template/.claude/agents/04-docker/container-health.md +255 -255
- package/template/.claude/agents/04-docker/deployment-validator.md +225 -225
- package/template/.claude/agents/04-docker/docker-compose-designer.md +281 -281
- package/template/.claude/agents/04-docker/docker-env-manager.md +235 -235
- package/template/.claude/agents/04-docker/docker-multi-stage.md +241 -241
- package/template/.claude/agents/04-docker/dockerfile-optimizer.md +208 -208
- package/template/.claude/agents/05-database/database-seeder.md +273 -273
- package/template/.claude/agents/05-database/mongodb-query-optimizer.md +230 -230
- package/template/.claude/agents/05-database/mongoose-aggregation.md +306 -306
- package/template/.claude/agents/05-database/mongoose-index-optimizer.md +182 -182
- package/template/.claude/agents/05-database/mongoose-schema-designer.md +267 -267
- package/template/.claude/agents/06-security/auth-session-validator.md +68 -68
- package/template/.claude/agents/06-security/input-sanitizer.md +80 -80
- package/template/.claude/agents/06-security/owasp-checker.md +97 -97
- package/template/.claude/agents/06-security/permission-auditor.md +100 -100
- package/template/.claude/agents/06-security/security-auditor.md +84 -84
- package/template/.claude/agents/06-security/sensitive-data-scanner.md +83 -83
- package/template/.claude/agents/07-documentation/api-documenter.md +136 -136
- package/template/.claude/agents/07-documentation/changelog-manager.md +105 -105
- package/template/.claude/agents/07-documentation/documenter.md +76 -76
- package/template/.claude/agents/07-documentation/domain-updater.md +81 -81
- package/template/.claude/agents/07-documentation/jsdoc-generator.md +114 -114
- package/template/.claude/agents/07-documentation/readme-generator.md +135 -135
- package/template/.claude/agents/08-git/branch-manager.md +58 -58
- package/template/.claude/agents/08-git/commit-manager.md +63 -63
- package/template/.claude/agents/08-git/pr-creator.md +76 -76
- package/template/.claude/agents/09-quality/code-reviewer.md +71 -71
- package/template/.claude/agents/09-quality/quality-checker.md +67 -67
- package/template/.claude/agents/10-research/best-practices-finder.md +89 -89
- package/template/.claude/agents/10-research/competitor-analyzer.md +106 -106
- package/template/.claude/agents/10-research/pattern-researcher.md +93 -93
- package/template/.claude/agents/10-research/research-cache-manager.md +76 -76
- package/template/.claude/agents/10-research/research-web.md +98 -98
- package/template/.claude/agents/10-research/tech-evaluator.md +101 -101
- package/template/.claude/agents/11-ui-ux/accessibility-auditor.md +136 -136
- package/template/.claude/agents/11-ui-ux/design-system-enforcer.md +125 -125
- package/template/.claude/agents/11-ui-ux/skeleton-generator.md +118 -118
- package/template/.claude/agents/11-ui-ux/ui-desktop.md +132 -132
- package/template/.claude/agents/11-ui-ux/ui-mobile.md +98 -98
- package/template/.claude/agents/11-ui-ux/ui-tablet.md +110 -110
- package/template/.claude/agents/12-performance/api-latency-analyzer.md +156 -156
- package/template/.claude/agents/12-performance/bundle-analyzer.md +113 -113
- package/template/.claude/agents/12-performance/memory-leak-detector.md +137 -137
- package/template/.claude/agents/12-performance/performance-profiler.md +115 -115
- package/template/.claude/agents/12-performance/query-optimizer.md +124 -124
- package/template/.claude/agents/12-performance/render-optimizer.md +154 -154
- package/template/.claude/agents/13-debugging/build-error-fixer.md +207 -207
- package/template/.claude/agents/13-debugging/debugger.md +149 -149
- package/template/.claude/agents/13-debugging/error-stack-analyzer.md +141 -141
- package/template/.claude/agents/13-debugging/network-debugger.md +208 -208
- package/template/.claude/agents/13-debugging/runtime-error-fixer.md +181 -181
- package/template/.claude/agents/13-debugging/type-error-resolver.md +185 -185
- package/template/.claude/agents/14-validation/final-validator.md +93 -93
- package/template/.claude/agents/_backup/analyzer.md +134 -134
- package/template/.claude/agents/_backup/code-reviewer.md +279 -279
- package/template/.claude/agents/_backup/commit-manager.md +219 -219
- package/template/.claude/agents/_backup/debugger.md +280 -280
- package/template/.claude/agents/_backup/documenter.md +237 -237
- package/template/.claude/agents/_backup/domain-updater.md +197 -197
- package/template/.claude/agents/_backup/final-validator.md +169 -169
- package/template/.claude/agents/_backup/orchestrator.md +149 -149
- package/template/.claude/agents/_backup/performance.md +232 -232
- package/template/.claude/agents/_backup/quality-checker.md +240 -240
- package/template/.claude/agents/_backup/research.md +315 -315
- package/template/.claude/agents/_backup/security-auditor.md +192 -192
- package/template/.claude/agents/_backup/tester.md +566 -566
- package/template/.claude/agents/_backup/ui-ux-reviewer.md +247 -247
- package/template/.claude/config/README.md +30 -30
- package/template/.claude/config/mcp-config.json +344 -344
- package/template/.claude/config/project-config.json +53 -53
- package/template/.claude/config/quality-gates.json +46 -46
- package/template/.claude/config/security-rules.json +45 -45
- package/template/.claude/config/testing-config.json +164 -164
- package/template/.claude/hooks/SETUP.md +126 -126
- package/template/.claude/hooks/run-hook.ts +176 -176
- package/template/.claude/hooks/stop-validator.ts +914 -824
- package/template/.claude/hooks/user-prompt-submit.ts +886 -886
- package/template/.claude/scripts/mcp-quick-install.ts +151 -151
- package/template/.claude/scripts/setup-mcps.ts +651 -651
- package/template/.claude/settings.json +275 -275
- package/template/.claude/skills/bun-runtime/SKILL.md +430 -430
- package/template/.claude/skills/codebase-knowledge/domains/claude-system.md +431 -431
- package/template/.claude/skills/codebase-knowledge/domains/mcp-integration.md +295 -295
- package/template/.claude/skills/debugging-patterns/SKILL.md +485 -485
- package/template/.claude/skills/docker-patterns/SKILL.md +555 -555
- package/template/.claude/skills/git-workflow/SKILL.md +454 -454
- package/template/.claude/skills/mongoose-patterns/SKILL.md +499 -499
- package/template/.claude/skills/nextjs-app-router/SKILL.md +327 -327
- package/template/.claude/skills/performance-patterns/SKILL.md +547 -547
- package/template/.claude/skills/playwright-automation/SKILL.md +438 -438
- package/template/.claude/skills/react-patterns/SKILL.md +389 -389
- package/template/.claude/skills/research-cache/SKILL.md +222 -222
- package/template/.claude/skills/shadcn-ui/SKILL.md +511 -511
- package/template/.claude/skills/tailwind-patterns/SKILL.md +465 -465
- package/template/.claude/skills/test-coverage/SKILL.md +467 -467
- package/template/.claude/skills/trpc-api/SKILL.md +434 -434
- package/template/.claude/skills/typescript-strict/SKILL.md +367 -367
- package/template/.claude/skills/zod-validation/SKILL.md +403 -403
- package/template/CLAUDE.md +117 -117
|
@@ -1,511 +1,511 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: shadcn-ui
|
|
3
|
-
description: shadcn/ui component patterns. Component installation, customization, theming, accessibility. Use when building UI with shadcn components.
|
|
4
|
-
allowed-tools: Read, Write, Edit, Grep, Glob, Bash
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# shadcn/ui - Component Library Patterns
|
|
8
|
-
|
|
9
|
-
## Purpose
|
|
10
|
-
|
|
11
|
-
Expert guidance for shadcn/ui:
|
|
12
|
-
|
|
13
|
-
- **Component Usage** - Proper component implementation
|
|
14
|
-
- **Customization** - Tailwind-based styling
|
|
15
|
-
- **Theming** - Dark mode, colors, variants
|
|
16
|
-
- **Accessibility** - Radix UI primitives
|
|
17
|
-
- **Best Practices** - Composition patterns
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Installation
|
|
22
|
-
|
|
23
|
-
### Initial Setup
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
# Initialize shadcn/ui
|
|
27
|
-
bunx --bun shadcn@latest init
|
|
28
|
-
|
|
29
|
-
# Install components
|
|
30
|
-
bunx --bun shadcn@latest add button
|
|
31
|
-
bunx --bun shadcn@latest add card
|
|
32
|
-
bunx --bun shadcn@latest add dialog
|
|
33
|
-
bunx --bun shadcn@latest add form
|
|
34
|
-
bunx --bun shadcn@latest add input
|
|
35
|
-
bunx --bun shadcn@latest add toast
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### Project Structure
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
components/
|
|
42
|
-
├── ui/ # shadcn components (auto-generated)
|
|
43
|
-
│ ├── button.tsx
|
|
44
|
-
│ ├── card.tsx
|
|
45
|
-
│ ├── dialog.tsx
|
|
46
|
-
│ └── ...
|
|
47
|
-
├── features/ # Feature-specific components
|
|
48
|
-
│ └── user-card.tsx
|
|
49
|
-
└── layouts/ # Layout components
|
|
50
|
-
└── main-layout.tsx
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## Core Components
|
|
56
|
-
|
|
57
|
-
### Button
|
|
58
|
-
|
|
59
|
-
```tsx
|
|
60
|
-
import { Button } from '@/components/ui/button';
|
|
61
|
-
|
|
62
|
-
// Variants
|
|
63
|
-
<Button variant="default">Default</Button>
|
|
64
|
-
<Button variant="destructive">Delete</Button>
|
|
65
|
-
<Button variant="outline">Outline</Button>
|
|
66
|
-
<Button variant="secondary">Secondary</Button>
|
|
67
|
-
<Button variant="ghost">Ghost</Button>
|
|
68
|
-
<Button variant="link">Link</Button>
|
|
69
|
-
|
|
70
|
-
// Sizes
|
|
71
|
-
<Button size="default">Default</Button>
|
|
72
|
-
<Button size="sm">Small</Button>
|
|
73
|
-
<Button size="lg">Large</Button>
|
|
74
|
-
<Button size="icon"><IconComponent /></Button>
|
|
75
|
-
|
|
76
|
-
// With loading state
|
|
77
|
-
<Button disabled={isPending}>
|
|
78
|
-
{isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
79
|
-
Submit
|
|
80
|
-
</Button>
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### Card
|
|
84
|
-
|
|
85
|
-
```tsx
|
|
86
|
-
import {
|
|
87
|
-
Card,
|
|
88
|
-
CardContent,
|
|
89
|
-
CardDescription,
|
|
90
|
-
CardFooter,
|
|
91
|
-
CardHeader,
|
|
92
|
-
CardTitle,
|
|
93
|
-
} from '@/components/ui/card';
|
|
94
|
-
|
|
95
|
-
<Card>
|
|
96
|
-
<CardHeader>
|
|
97
|
-
<CardTitle>Card Title</CardTitle>
|
|
98
|
-
<CardDescription>Card description text</CardDescription>
|
|
99
|
-
</CardHeader>
|
|
100
|
-
<CardContent>
|
|
101
|
-
<p>Card content here</p>
|
|
102
|
-
</CardContent>
|
|
103
|
-
<CardFooter>
|
|
104
|
-
<Button>Action</Button>
|
|
105
|
-
</CardFooter>
|
|
106
|
-
</Card>;
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Dialog
|
|
110
|
-
|
|
111
|
-
```tsx
|
|
112
|
-
import {
|
|
113
|
-
Dialog,
|
|
114
|
-
DialogContent,
|
|
115
|
-
DialogDescription,
|
|
116
|
-
DialogFooter,
|
|
117
|
-
DialogHeader,
|
|
118
|
-
DialogTitle,
|
|
119
|
-
DialogTrigger,
|
|
120
|
-
} from '@/components/ui/dialog';
|
|
121
|
-
|
|
122
|
-
<Dialog>
|
|
123
|
-
<DialogTrigger asChild>
|
|
124
|
-
<Button variant="outline">Open Dialog</Button>
|
|
125
|
-
</DialogTrigger>
|
|
126
|
-
<DialogContent className="sm:max-w-[425px]">
|
|
127
|
-
<DialogHeader>
|
|
128
|
-
<DialogTitle>Edit Profile</DialogTitle>
|
|
129
|
-
<DialogDescription>Make changes to your profile here.</DialogDescription>
|
|
130
|
-
</DialogHeader>
|
|
131
|
-
<div className="grid gap-4 py-4">{/* Form fields */}</div>
|
|
132
|
-
<DialogFooter>
|
|
133
|
-
<Button type="submit">Save changes</Button>
|
|
134
|
-
</DialogFooter>
|
|
135
|
-
</DialogContent>
|
|
136
|
-
</Dialog>;
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### Form (with react-hook-form + zod)
|
|
140
|
-
|
|
141
|
-
```tsx
|
|
142
|
-
'use client';
|
|
143
|
-
|
|
144
|
-
import { zodResolver } from '@hookform/resolvers/zod';
|
|
145
|
-
import { useForm } from 'react-hook-form';
|
|
146
|
-
import { z } from 'zod';
|
|
147
|
-
import { Button } from '@/components/ui/button';
|
|
148
|
-
import {
|
|
149
|
-
Form,
|
|
150
|
-
FormControl,
|
|
151
|
-
FormDescription,
|
|
152
|
-
FormField,
|
|
153
|
-
FormItem,
|
|
154
|
-
FormLabel,
|
|
155
|
-
FormMessage,
|
|
156
|
-
} from '@/components/ui/form';
|
|
157
|
-
import { Input } from '@/components/ui/input';
|
|
158
|
-
|
|
159
|
-
const formSchema = z.object({
|
|
160
|
-
username: z.string().min(2).max(50),
|
|
161
|
-
email: z.string().email(),
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
type FormValues = z.infer<typeof formSchema>;
|
|
165
|
-
|
|
166
|
-
export function ProfileForm() {
|
|
167
|
-
const form = useForm<FormValues>({
|
|
168
|
-
resolver: zodResolver(formSchema),
|
|
169
|
-
defaultValues: {
|
|
170
|
-
username: '',
|
|
171
|
-
email: '',
|
|
172
|
-
},
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
function onSubmit(values: FormValues) {
|
|
176
|
-
console.log(values);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return (
|
|
180
|
-
<Form {...form}>
|
|
181
|
-
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
|
182
|
-
<FormField
|
|
183
|
-
control={form.control}
|
|
184
|
-
name="username"
|
|
185
|
-
render={({ field }) => (
|
|
186
|
-
<FormItem>
|
|
187
|
-
<FormLabel>Username</FormLabel>
|
|
188
|
-
<FormControl>
|
|
189
|
-
<Input placeholder="johndoe" {...field} />
|
|
190
|
-
</FormControl>
|
|
191
|
-
<FormDescription>This is your public display name.</FormDescription>
|
|
192
|
-
<FormMessage />
|
|
193
|
-
</FormItem>
|
|
194
|
-
)}
|
|
195
|
-
/>
|
|
196
|
-
<FormField
|
|
197
|
-
control={form.control}
|
|
198
|
-
name="email"
|
|
199
|
-
render={({ field }) => (
|
|
200
|
-
<FormItem>
|
|
201
|
-
<FormLabel>Email</FormLabel>
|
|
202
|
-
<FormControl>
|
|
203
|
-
<Input type="email" placeholder="john@example.com" {...field} />
|
|
204
|
-
</FormControl>
|
|
205
|
-
<FormMessage />
|
|
206
|
-
</FormItem>
|
|
207
|
-
)}
|
|
208
|
-
/>
|
|
209
|
-
<Button type="submit">Submit</Button>
|
|
210
|
-
</form>
|
|
211
|
-
</Form>
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
---
|
|
217
|
-
|
|
218
|
-
## Toast Notifications
|
|
219
|
-
|
|
220
|
-
### Setup
|
|
221
|
-
|
|
222
|
-
```tsx
|
|
223
|
-
// app/layout.tsx
|
|
224
|
-
import { Toaster } from '@/components/ui/toaster';
|
|
225
|
-
|
|
226
|
-
export default function RootLayout({ children }) {
|
|
227
|
-
return (
|
|
228
|
-
<html>
|
|
229
|
-
<body>
|
|
230
|
-
{children}
|
|
231
|
-
<Toaster />
|
|
232
|
-
</body>
|
|
233
|
-
</html>
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Usage
|
|
239
|
-
|
|
240
|
-
```tsx
|
|
241
|
-
import { useToast } from '@/hooks/use-toast';
|
|
242
|
-
|
|
243
|
-
export function MyComponent() {
|
|
244
|
-
const { toast } = useToast();
|
|
245
|
-
|
|
246
|
-
return (
|
|
247
|
-
<Button
|
|
248
|
-
onClick={() => {
|
|
249
|
-
toast({
|
|
250
|
-
title: 'Success!',
|
|
251
|
-
description: 'Your changes have been saved.',
|
|
252
|
-
});
|
|
253
|
-
}}
|
|
254
|
-
>
|
|
255
|
-
Save
|
|
256
|
-
</Button>
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// With variants
|
|
261
|
-
toast({
|
|
262
|
-
variant: 'destructive',
|
|
263
|
-
title: 'Error',
|
|
264
|
-
description: 'Something went wrong.',
|
|
265
|
-
});
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
---
|
|
269
|
-
|
|
270
|
-
## Theming
|
|
271
|
-
|
|
272
|
-
### CSS Variables
|
|
273
|
-
|
|
274
|
-
```css
|
|
275
|
-
/* globals.css */
|
|
276
|
-
@layer base {
|
|
277
|
-
:root {
|
|
278
|
-
--background: 0 0% 100%;
|
|
279
|
-
--foreground: 222.2 84% 4.9%;
|
|
280
|
-
--card: 0 0% 100%;
|
|
281
|
-
--card-foreground: 222.2 84% 4.9%;
|
|
282
|
-
--popover: 0 0% 100%;
|
|
283
|
-
--popover-foreground: 222.2 84% 4.9%;
|
|
284
|
-
--primary: 222.2 47.4% 11.2%;
|
|
285
|
-
--primary-foreground: 210 40% 98%;
|
|
286
|
-
--secondary: 210 40% 96.1%;
|
|
287
|
-
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
288
|
-
--muted: 210 40% 96.1%;
|
|
289
|
-
--muted-foreground: 215.4 16.3% 46.9%;
|
|
290
|
-
--accent: 210 40% 96.1%;
|
|
291
|
-
--accent-foreground: 222.2 47.4% 11.2%;
|
|
292
|
-
--destructive: 0 84.2% 60.2%;
|
|
293
|
-
--destructive-foreground: 210 40% 98%;
|
|
294
|
-
--border: 214.3 31.8% 91.4%;
|
|
295
|
-
--input: 214.3 31.8% 91.4%;
|
|
296
|
-
--ring: 222.2 84% 4.9%;
|
|
297
|
-
--radius: 0.5rem;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
.dark {
|
|
301
|
-
--background: 222.2 84% 4.9%;
|
|
302
|
-
--foreground: 210 40% 98%;
|
|
303
|
-
/* ... dark mode values */
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### Dark Mode Toggle
|
|
309
|
-
|
|
310
|
-
```tsx
|
|
311
|
-
'use client';
|
|
312
|
-
|
|
313
|
-
import { Moon, Sun } from 'lucide-react';
|
|
314
|
-
import { useTheme } from 'next-themes';
|
|
315
|
-
import { Button } from '@/components/ui/button';
|
|
316
|
-
|
|
317
|
-
export function ThemeToggle() {
|
|
318
|
-
const { theme, setTheme } = useTheme();
|
|
319
|
-
|
|
320
|
-
return (
|
|
321
|
-
<Button
|
|
322
|
-
variant="ghost"
|
|
323
|
-
size="icon"
|
|
324
|
-
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
|
|
325
|
-
>
|
|
326
|
-
<Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
|
327
|
-
<Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
|
328
|
-
<span className="sr-only">Toggle theme</span>
|
|
329
|
-
</Button>
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
---
|
|
335
|
-
|
|
336
|
-
## Customization Patterns
|
|
337
|
-
|
|
338
|
-
### Extend Variants
|
|
339
|
-
|
|
340
|
-
```tsx
|
|
341
|
-
// components/ui/button.tsx
|
|
342
|
-
import { cva, type VariantProps } from 'class-variance-authority';
|
|
343
|
-
|
|
344
|
-
const buttonVariants = cva(
|
|
345
|
-
'inline-flex items-center justify-center rounded-md text-sm font-medium ...',
|
|
346
|
-
{
|
|
347
|
-
variants: {
|
|
348
|
-
variant: {
|
|
349
|
-
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
350
|
-
destructive: 'bg-destructive text-destructive-foreground ...',
|
|
351
|
-
outline: 'border border-input bg-background hover:bg-accent ...',
|
|
352
|
-
// Add custom variants
|
|
353
|
-
success: 'bg-green-600 text-white hover:bg-green-700',
|
|
354
|
-
warning: 'bg-yellow-500 text-black hover:bg-yellow-600',
|
|
355
|
-
},
|
|
356
|
-
size: {
|
|
357
|
-
default: 'h-10 px-4 py-2',
|
|
358
|
-
sm: 'h-9 rounded-md px-3',
|
|
359
|
-
lg: 'h-11 rounded-md px-8',
|
|
360
|
-
icon: 'h-10 w-10',
|
|
361
|
-
// Add custom sizes
|
|
362
|
-
xl: 'h-14 rounded-lg px-10 text-lg',
|
|
363
|
-
},
|
|
364
|
-
},
|
|
365
|
-
}
|
|
366
|
-
);
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### Composition
|
|
370
|
-
|
|
371
|
-
```tsx
|
|
372
|
-
// Wrap shadcn components with project-specific defaults
|
|
373
|
-
import { Button as ShadcnButton } from '@/components/ui/button';
|
|
374
|
-
import { Loader2 } from 'lucide-react';
|
|
375
|
-
|
|
376
|
-
interface LoadingButtonProps extends React.ComponentProps<typeof ShadcnButton> {
|
|
377
|
-
isLoading?: boolean;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
export function LoadingButton({ children, isLoading, disabled, ...props }: LoadingButtonProps) {
|
|
381
|
-
return (
|
|
382
|
-
<ShadcnButton disabled={disabled || isLoading} {...props}>
|
|
383
|
-
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
384
|
-
{children}
|
|
385
|
-
</ShadcnButton>
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
---
|
|
391
|
-
|
|
392
|
-
## Accessibility
|
|
393
|
-
|
|
394
|
-
### Keyboard Navigation
|
|
395
|
-
|
|
396
|
-
```tsx
|
|
397
|
-
// All shadcn components support keyboard navigation by default
|
|
398
|
-
// Dialog: Escape to close, Tab to navigate
|
|
399
|
-
// Dropdown: Arrow keys to navigate, Enter to select
|
|
400
|
-
// Command: Type to search, arrows to navigate
|
|
401
|
-
|
|
402
|
-
// Ensure proper focus management
|
|
403
|
-
<Dialog>
|
|
404
|
-
<DialogContent>
|
|
405
|
-
{/* First focusable element receives focus */}
|
|
406
|
-
<Input autoFocus />
|
|
407
|
-
</DialogContent>
|
|
408
|
-
</Dialog>
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
### Screen Reader
|
|
412
|
-
|
|
413
|
-
```tsx
|
|
414
|
-
// Always include sr-only labels for icon-only buttons
|
|
415
|
-
<Button variant="ghost" size="icon">
|
|
416
|
-
<Sun className="h-5 w-5" />
|
|
417
|
-
<span className="sr-only">Toggle theme</span>
|
|
418
|
-
</Button>
|
|
419
|
-
|
|
420
|
-
// Use proper ARIA attributes
|
|
421
|
-
<Button aria-label="Close dialog" aria-pressed={isOpen}>
|
|
422
|
-
<X className="h-4 w-4" />
|
|
423
|
-
</Button>
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
---
|
|
427
|
-
|
|
428
|
-
## Common Patterns
|
|
429
|
-
|
|
430
|
-
### Skeleton Loading
|
|
431
|
-
|
|
432
|
-
```tsx
|
|
433
|
-
import { Skeleton } from '@/components/ui/skeleton';
|
|
434
|
-
|
|
435
|
-
export function CardSkeleton() {
|
|
436
|
-
return (
|
|
437
|
-
<Card>
|
|
438
|
-
<CardHeader>
|
|
439
|
-
<Skeleton className="h-4 w-[250px]" />
|
|
440
|
-
<Skeleton className="h-4 w-[200px]" />
|
|
441
|
-
</CardHeader>
|
|
442
|
-
<CardContent>
|
|
443
|
-
<Skeleton className="h-[125px] w-full rounded-xl" />
|
|
444
|
-
</CardContent>
|
|
445
|
-
</Card>
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
### Data Table
|
|
451
|
-
|
|
452
|
-
```tsx
|
|
453
|
-
import {
|
|
454
|
-
Table,
|
|
455
|
-
TableBody,
|
|
456
|
-
TableCell,
|
|
457
|
-
TableHead,
|
|
458
|
-
TableHeader,
|
|
459
|
-
TableRow,
|
|
460
|
-
} from '@/components/ui/table';
|
|
461
|
-
|
|
462
|
-
<Table>
|
|
463
|
-
<TableHeader>
|
|
464
|
-
<TableRow>
|
|
465
|
-
<TableHead>Name</TableHead>
|
|
466
|
-
<TableHead>Email</TableHead>
|
|
467
|
-
<TableHead className="text-right">Actions</TableHead>
|
|
468
|
-
</TableRow>
|
|
469
|
-
</TableHeader>
|
|
470
|
-
<TableBody>
|
|
471
|
-
{users.map((user) => (
|
|
472
|
-
<TableRow key={user.id}>
|
|
473
|
-
<TableCell className="font-medium">{user.name}</TableCell>
|
|
474
|
-
<TableCell>{user.email}</TableCell>
|
|
475
|
-
<TableCell className="text-right">
|
|
476
|
-
<Button variant="ghost" size="sm">
|
|
477
|
-
Edit
|
|
478
|
-
</Button>
|
|
479
|
-
</TableCell>
|
|
480
|
-
</TableRow>
|
|
481
|
-
))}
|
|
482
|
-
</TableBody>
|
|
483
|
-
</Table>;
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
---
|
|
487
|
-
|
|
488
|
-
## Agent Integration
|
|
489
|
-
|
|
490
|
-
This skill is used by:
|
|
491
|
-
|
|
492
|
-
- **ui-mobile/tablet/desktop** agents
|
|
493
|
-
- **skeleton-generator** agent
|
|
494
|
-
- **design-system-enforcer** agent
|
|
495
|
-
- **accessibility-auditor** agent
|
|
496
|
-
|
|
497
|
-
---
|
|
498
|
-
|
|
499
|
-
## FORBIDDEN
|
|
500
|
-
|
|
501
|
-
1. **Override component internals** - Extend, don't modify
|
|
502
|
-
2. **Skip accessibility labels** - Always add sr-only/aria
|
|
503
|
-
3. **Inline styles over Tailwind** - Use className
|
|
504
|
-
4. **Ignore keyboard navigation** - Test all interactions
|
|
505
|
-
5. **Custom form validation** - Use react-hook-form + zod
|
|
506
|
-
|
|
507
|
-
---
|
|
508
|
-
|
|
509
|
-
## Version
|
|
510
|
-
|
|
511
|
-
- **v1.0.0** - Initial implementation based on shadcn/ui patterns
|
|
1
|
+
---
|
|
2
|
+
name: shadcn-ui
|
|
3
|
+
description: shadcn/ui component patterns. Component installation, customization, theming, accessibility. Use when building UI with shadcn components.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Grep, Glob, Bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# shadcn/ui - Component Library Patterns
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Expert guidance for shadcn/ui:
|
|
12
|
+
|
|
13
|
+
- **Component Usage** - Proper component implementation
|
|
14
|
+
- **Customization** - Tailwind-based styling
|
|
15
|
+
- **Theming** - Dark mode, colors, variants
|
|
16
|
+
- **Accessibility** - Radix UI primitives
|
|
17
|
+
- **Best Practices** - Composition patterns
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### Initial Setup
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Initialize shadcn/ui
|
|
27
|
+
bunx --bun shadcn@latest init
|
|
28
|
+
|
|
29
|
+
# Install components
|
|
30
|
+
bunx --bun shadcn@latest add button
|
|
31
|
+
bunx --bun shadcn@latest add card
|
|
32
|
+
bunx --bun shadcn@latest add dialog
|
|
33
|
+
bunx --bun shadcn@latest add form
|
|
34
|
+
bunx --bun shadcn@latest add input
|
|
35
|
+
bunx --bun shadcn@latest add toast
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Project Structure
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
components/
|
|
42
|
+
├── ui/ # shadcn components (auto-generated)
|
|
43
|
+
│ ├── button.tsx
|
|
44
|
+
│ ├── card.tsx
|
|
45
|
+
│ ├── dialog.tsx
|
|
46
|
+
│ └── ...
|
|
47
|
+
├── features/ # Feature-specific components
|
|
48
|
+
│ └── user-card.tsx
|
|
49
|
+
└── layouts/ # Layout components
|
|
50
|
+
└── main-layout.tsx
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Core Components
|
|
56
|
+
|
|
57
|
+
### Button
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { Button } from '@/components/ui/button';
|
|
61
|
+
|
|
62
|
+
// Variants
|
|
63
|
+
<Button variant="default">Default</Button>
|
|
64
|
+
<Button variant="destructive">Delete</Button>
|
|
65
|
+
<Button variant="outline">Outline</Button>
|
|
66
|
+
<Button variant="secondary">Secondary</Button>
|
|
67
|
+
<Button variant="ghost">Ghost</Button>
|
|
68
|
+
<Button variant="link">Link</Button>
|
|
69
|
+
|
|
70
|
+
// Sizes
|
|
71
|
+
<Button size="default">Default</Button>
|
|
72
|
+
<Button size="sm">Small</Button>
|
|
73
|
+
<Button size="lg">Large</Button>
|
|
74
|
+
<Button size="icon"><IconComponent /></Button>
|
|
75
|
+
|
|
76
|
+
// With loading state
|
|
77
|
+
<Button disabled={isPending}>
|
|
78
|
+
{isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
79
|
+
Submit
|
|
80
|
+
</Button>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Card
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import {
|
|
87
|
+
Card,
|
|
88
|
+
CardContent,
|
|
89
|
+
CardDescription,
|
|
90
|
+
CardFooter,
|
|
91
|
+
CardHeader,
|
|
92
|
+
CardTitle,
|
|
93
|
+
} from '@/components/ui/card';
|
|
94
|
+
|
|
95
|
+
<Card>
|
|
96
|
+
<CardHeader>
|
|
97
|
+
<CardTitle>Card Title</CardTitle>
|
|
98
|
+
<CardDescription>Card description text</CardDescription>
|
|
99
|
+
</CardHeader>
|
|
100
|
+
<CardContent>
|
|
101
|
+
<p>Card content here</p>
|
|
102
|
+
</CardContent>
|
|
103
|
+
<CardFooter>
|
|
104
|
+
<Button>Action</Button>
|
|
105
|
+
</CardFooter>
|
|
106
|
+
</Card>;
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Dialog
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import {
|
|
113
|
+
Dialog,
|
|
114
|
+
DialogContent,
|
|
115
|
+
DialogDescription,
|
|
116
|
+
DialogFooter,
|
|
117
|
+
DialogHeader,
|
|
118
|
+
DialogTitle,
|
|
119
|
+
DialogTrigger,
|
|
120
|
+
} from '@/components/ui/dialog';
|
|
121
|
+
|
|
122
|
+
<Dialog>
|
|
123
|
+
<DialogTrigger asChild>
|
|
124
|
+
<Button variant="outline">Open Dialog</Button>
|
|
125
|
+
</DialogTrigger>
|
|
126
|
+
<DialogContent className="sm:max-w-[425px]">
|
|
127
|
+
<DialogHeader>
|
|
128
|
+
<DialogTitle>Edit Profile</DialogTitle>
|
|
129
|
+
<DialogDescription>Make changes to your profile here.</DialogDescription>
|
|
130
|
+
</DialogHeader>
|
|
131
|
+
<div className="grid gap-4 py-4">{/* Form fields */}</div>
|
|
132
|
+
<DialogFooter>
|
|
133
|
+
<Button type="submit">Save changes</Button>
|
|
134
|
+
</DialogFooter>
|
|
135
|
+
</DialogContent>
|
|
136
|
+
</Dialog>;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Form (with react-hook-form + zod)
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
'use client';
|
|
143
|
+
|
|
144
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
145
|
+
import { useForm } from 'react-hook-form';
|
|
146
|
+
import { z } from 'zod';
|
|
147
|
+
import { Button } from '@/components/ui/button';
|
|
148
|
+
import {
|
|
149
|
+
Form,
|
|
150
|
+
FormControl,
|
|
151
|
+
FormDescription,
|
|
152
|
+
FormField,
|
|
153
|
+
FormItem,
|
|
154
|
+
FormLabel,
|
|
155
|
+
FormMessage,
|
|
156
|
+
} from '@/components/ui/form';
|
|
157
|
+
import { Input } from '@/components/ui/input';
|
|
158
|
+
|
|
159
|
+
const formSchema = z.object({
|
|
160
|
+
username: z.string().min(2).max(50),
|
|
161
|
+
email: z.string().email(),
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
type FormValues = z.infer<typeof formSchema>;
|
|
165
|
+
|
|
166
|
+
export function ProfileForm() {
|
|
167
|
+
const form = useForm<FormValues>({
|
|
168
|
+
resolver: zodResolver(formSchema),
|
|
169
|
+
defaultValues: {
|
|
170
|
+
username: '',
|
|
171
|
+
email: '',
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
function onSubmit(values: FormValues) {
|
|
176
|
+
console.log(values);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<Form {...form}>
|
|
181
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
|
182
|
+
<FormField
|
|
183
|
+
control={form.control}
|
|
184
|
+
name="username"
|
|
185
|
+
render={({ field }) => (
|
|
186
|
+
<FormItem>
|
|
187
|
+
<FormLabel>Username</FormLabel>
|
|
188
|
+
<FormControl>
|
|
189
|
+
<Input placeholder="johndoe" {...field} />
|
|
190
|
+
</FormControl>
|
|
191
|
+
<FormDescription>This is your public display name.</FormDescription>
|
|
192
|
+
<FormMessage />
|
|
193
|
+
</FormItem>
|
|
194
|
+
)}
|
|
195
|
+
/>
|
|
196
|
+
<FormField
|
|
197
|
+
control={form.control}
|
|
198
|
+
name="email"
|
|
199
|
+
render={({ field }) => (
|
|
200
|
+
<FormItem>
|
|
201
|
+
<FormLabel>Email</FormLabel>
|
|
202
|
+
<FormControl>
|
|
203
|
+
<Input type="email" placeholder="john@example.com" {...field} />
|
|
204
|
+
</FormControl>
|
|
205
|
+
<FormMessage />
|
|
206
|
+
</FormItem>
|
|
207
|
+
)}
|
|
208
|
+
/>
|
|
209
|
+
<Button type="submit">Submit</Button>
|
|
210
|
+
</form>
|
|
211
|
+
</Form>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Toast Notifications
|
|
219
|
+
|
|
220
|
+
### Setup
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
// app/layout.tsx
|
|
224
|
+
import { Toaster } from '@/components/ui/toaster';
|
|
225
|
+
|
|
226
|
+
export default function RootLayout({ children }) {
|
|
227
|
+
return (
|
|
228
|
+
<html>
|
|
229
|
+
<body>
|
|
230
|
+
{children}
|
|
231
|
+
<Toaster />
|
|
232
|
+
</body>
|
|
233
|
+
</html>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Usage
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
import { useToast } from '@/hooks/use-toast';
|
|
242
|
+
|
|
243
|
+
export function MyComponent() {
|
|
244
|
+
const { toast } = useToast();
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<Button
|
|
248
|
+
onClick={() => {
|
|
249
|
+
toast({
|
|
250
|
+
title: 'Success!',
|
|
251
|
+
description: 'Your changes have been saved.',
|
|
252
|
+
});
|
|
253
|
+
}}
|
|
254
|
+
>
|
|
255
|
+
Save
|
|
256
|
+
</Button>
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// With variants
|
|
261
|
+
toast({
|
|
262
|
+
variant: 'destructive',
|
|
263
|
+
title: 'Error',
|
|
264
|
+
description: 'Something went wrong.',
|
|
265
|
+
});
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Theming
|
|
271
|
+
|
|
272
|
+
### CSS Variables
|
|
273
|
+
|
|
274
|
+
```css
|
|
275
|
+
/* globals.css */
|
|
276
|
+
@layer base {
|
|
277
|
+
:root {
|
|
278
|
+
--background: 0 0% 100%;
|
|
279
|
+
--foreground: 222.2 84% 4.9%;
|
|
280
|
+
--card: 0 0% 100%;
|
|
281
|
+
--card-foreground: 222.2 84% 4.9%;
|
|
282
|
+
--popover: 0 0% 100%;
|
|
283
|
+
--popover-foreground: 222.2 84% 4.9%;
|
|
284
|
+
--primary: 222.2 47.4% 11.2%;
|
|
285
|
+
--primary-foreground: 210 40% 98%;
|
|
286
|
+
--secondary: 210 40% 96.1%;
|
|
287
|
+
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
288
|
+
--muted: 210 40% 96.1%;
|
|
289
|
+
--muted-foreground: 215.4 16.3% 46.9%;
|
|
290
|
+
--accent: 210 40% 96.1%;
|
|
291
|
+
--accent-foreground: 222.2 47.4% 11.2%;
|
|
292
|
+
--destructive: 0 84.2% 60.2%;
|
|
293
|
+
--destructive-foreground: 210 40% 98%;
|
|
294
|
+
--border: 214.3 31.8% 91.4%;
|
|
295
|
+
--input: 214.3 31.8% 91.4%;
|
|
296
|
+
--ring: 222.2 84% 4.9%;
|
|
297
|
+
--radius: 0.5rem;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.dark {
|
|
301
|
+
--background: 222.2 84% 4.9%;
|
|
302
|
+
--foreground: 210 40% 98%;
|
|
303
|
+
/* ... dark mode values */
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Dark Mode Toggle
|
|
309
|
+
|
|
310
|
+
```tsx
|
|
311
|
+
'use client';
|
|
312
|
+
|
|
313
|
+
import { Moon, Sun } from 'lucide-react';
|
|
314
|
+
import { useTheme } from 'next-themes';
|
|
315
|
+
import { Button } from '@/components/ui/button';
|
|
316
|
+
|
|
317
|
+
export function ThemeToggle() {
|
|
318
|
+
const { theme, setTheme } = useTheme();
|
|
319
|
+
|
|
320
|
+
return (
|
|
321
|
+
<Button
|
|
322
|
+
variant="ghost"
|
|
323
|
+
size="icon"
|
|
324
|
+
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
|
|
325
|
+
>
|
|
326
|
+
<Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
|
327
|
+
<Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
|
328
|
+
<span className="sr-only">Toggle theme</span>
|
|
329
|
+
</Button>
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Customization Patterns
|
|
337
|
+
|
|
338
|
+
### Extend Variants
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
// components/ui/button.tsx
|
|
342
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
343
|
+
|
|
344
|
+
const buttonVariants = cva(
|
|
345
|
+
'inline-flex items-center justify-center rounded-md text-sm font-medium ...',
|
|
346
|
+
{
|
|
347
|
+
variants: {
|
|
348
|
+
variant: {
|
|
349
|
+
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
350
|
+
destructive: 'bg-destructive text-destructive-foreground ...',
|
|
351
|
+
outline: 'border border-input bg-background hover:bg-accent ...',
|
|
352
|
+
// Add custom variants
|
|
353
|
+
success: 'bg-green-600 text-white hover:bg-green-700',
|
|
354
|
+
warning: 'bg-yellow-500 text-black hover:bg-yellow-600',
|
|
355
|
+
},
|
|
356
|
+
size: {
|
|
357
|
+
default: 'h-10 px-4 py-2',
|
|
358
|
+
sm: 'h-9 rounded-md px-3',
|
|
359
|
+
lg: 'h-11 rounded-md px-8',
|
|
360
|
+
icon: 'h-10 w-10',
|
|
361
|
+
// Add custom sizes
|
|
362
|
+
xl: 'h-14 rounded-lg px-10 text-lg',
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
}
|
|
366
|
+
);
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Composition
|
|
370
|
+
|
|
371
|
+
```tsx
|
|
372
|
+
// Wrap shadcn components with project-specific defaults
|
|
373
|
+
import { Button as ShadcnButton } from '@/components/ui/button';
|
|
374
|
+
import { Loader2 } from 'lucide-react';
|
|
375
|
+
|
|
376
|
+
interface LoadingButtonProps extends React.ComponentProps<typeof ShadcnButton> {
|
|
377
|
+
isLoading?: boolean;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export function LoadingButton({ children, isLoading, disabled, ...props }: LoadingButtonProps) {
|
|
381
|
+
return (
|
|
382
|
+
<ShadcnButton disabled={disabled || isLoading} {...props}>
|
|
383
|
+
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
384
|
+
{children}
|
|
385
|
+
</ShadcnButton>
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## Accessibility
|
|
393
|
+
|
|
394
|
+
### Keyboard Navigation
|
|
395
|
+
|
|
396
|
+
```tsx
|
|
397
|
+
// All shadcn components support keyboard navigation by default
|
|
398
|
+
// Dialog: Escape to close, Tab to navigate
|
|
399
|
+
// Dropdown: Arrow keys to navigate, Enter to select
|
|
400
|
+
// Command: Type to search, arrows to navigate
|
|
401
|
+
|
|
402
|
+
// Ensure proper focus management
|
|
403
|
+
<Dialog>
|
|
404
|
+
<DialogContent>
|
|
405
|
+
{/* First focusable element receives focus */}
|
|
406
|
+
<Input autoFocus />
|
|
407
|
+
</DialogContent>
|
|
408
|
+
</Dialog>
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Screen Reader
|
|
412
|
+
|
|
413
|
+
```tsx
|
|
414
|
+
// Always include sr-only labels for icon-only buttons
|
|
415
|
+
<Button variant="ghost" size="icon">
|
|
416
|
+
<Sun className="h-5 w-5" />
|
|
417
|
+
<span className="sr-only">Toggle theme</span>
|
|
418
|
+
</Button>
|
|
419
|
+
|
|
420
|
+
// Use proper ARIA attributes
|
|
421
|
+
<Button aria-label="Close dialog" aria-pressed={isOpen}>
|
|
422
|
+
<X className="h-4 w-4" />
|
|
423
|
+
</Button>
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Common Patterns
|
|
429
|
+
|
|
430
|
+
### Skeleton Loading
|
|
431
|
+
|
|
432
|
+
```tsx
|
|
433
|
+
import { Skeleton } from '@/components/ui/skeleton';
|
|
434
|
+
|
|
435
|
+
export function CardSkeleton() {
|
|
436
|
+
return (
|
|
437
|
+
<Card>
|
|
438
|
+
<CardHeader>
|
|
439
|
+
<Skeleton className="h-4 w-[250px]" />
|
|
440
|
+
<Skeleton className="h-4 w-[200px]" />
|
|
441
|
+
</CardHeader>
|
|
442
|
+
<CardContent>
|
|
443
|
+
<Skeleton className="h-[125px] w-full rounded-xl" />
|
|
444
|
+
</CardContent>
|
|
445
|
+
</Card>
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### Data Table
|
|
451
|
+
|
|
452
|
+
```tsx
|
|
453
|
+
import {
|
|
454
|
+
Table,
|
|
455
|
+
TableBody,
|
|
456
|
+
TableCell,
|
|
457
|
+
TableHead,
|
|
458
|
+
TableHeader,
|
|
459
|
+
TableRow,
|
|
460
|
+
} from '@/components/ui/table';
|
|
461
|
+
|
|
462
|
+
<Table>
|
|
463
|
+
<TableHeader>
|
|
464
|
+
<TableRow>
|
|
465
|
+
<TableHead>Name</TableHead>
|
|
466
|
+
<TableHead>Email</TableHead>
|
|
467
|
+
<TableHead className="text-right">Actions</TableHead>
|
|
468
|
+
</TableRow>
|
|
469
|
+
</TableHeader>
|
|
470
|
+
<TableBody>
|
|
471
|
+
{users.map((user) => (
|
|
472
|
+
<TableRow key={user.id}>
|
|
473
|
+
<TableCell className="font-medium">{user.name}</TableCell>
|
|
474
|
+
<TableCell>{user.email}</TableCell>
|
|
475
|
+
<TableCell className="text-right">
|
|
476
|
+
<Button variant="ghost" size="sm">
|
|
477
|
+
Edit
|
|
478
|
+
</Button>
|
|
479
|
+
</TableCell>
|
|
480
|
+
</TableRow>
|
|
481
|
+
))}
|
|
482
|
+
</TableBody>
|
|
483
|
+
</Table>;
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## Agent Integration
|
|
489
|
+
|
|
490
|
+
This skill is used by:
|
|
491
|
+
|
|
492
|
+
- **ui-mobile/tablet/desktop** agents
|
|
493
|
+
- **skeleton-generator** agent
|
|
494
|
+
- **design-system-enforcer** agent
|
|
495
|
+
- **accessibility-auditor** agent
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## FORBIDDEN
|
|
500
|
+
|
|
501
|
+
1. **Override component internals** - Extend, don't modify
|
|
502
|
+
2. **Skip accessibility labels** - Always add sr-only/aria
|
|
503
|
+
3. **Inline styles over Tailwind** - Use className
|
|
504
|
+
4. **Ignore keyboard navigation** - Test all interactions
|
|
505
|
+
5. **Custom form validation** - Use react-hook-form + zod
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## Version
|
|
510
|
+
|
|
511
|
+
- **v1.0.0** - Initial implementation based on shadcn/ui patterns
|