beth-copilot 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +224 -0
- package/bin/cli.js +223 -0
- package/package.json +36 -0
- package/templates/.github/agents/beth.agent.md +279 -0
- package/templates/.github/agents/developer.agent.md +493 -0
- package/templates/.github/agents/frontend-engineer.agent.md +556 -0
- package/templates/.github/agents/product-manager.agent.md +253 -0
- package/templates/.github/agents/researcher.agent.md +319 -0
- package/templates/.github/agents/security-reviewer.agent.md +452 -0
- package/templates/.github/agents/tester.agent.md +477 -0
- package/templates/.github/agents/ux-designer.agent.md +374 -0
- package/templates/.github/copilot-instructions.md +191 -0
- package/templates/.github/skills/framer-components/SKILL.md +564 -0
- package/templates/.github/skills/prd/SKILL.md +244 -0
- package/templates/.github/skills/security-analysis/SKILL.md +799 -0
- package/templates/.github/skills/shadcn-ui/SKILL.md +562 -0
- package/templates/.github/skills/vercel-react-best-practices/AGENTS.md +2516 -0
- package/templates/.github/skills/vercel-react-best-practices/SKILL.md +125 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/advanced-use-latest.md +49 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-dependencies.md +36 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +57 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/templates/.github/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/templates/.github/skills/web-design-guidelines/SKILL.md +39 -0
- package/templates/AGENTS.md +70 -0
- package/templates/Backlog.md +80 -0
- package/templates/mcp.json.example +9 -0
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
````skill
|
|
2
|
+
---
|
|
3
|
+
name: shadcn-ui
|
|
4
|
+
description: shadcn/ui component library patterns, best practices, and MCP integration. Use when building UI with shadcn/ui components, customizing components, or using the shadcn MCP server. Triggers on tasks involving component installation, UI composition, theming, or when explicitly asked about shadcn patterns.
|
|
5
|
+
license: MIT
|
|
6
|
+
metadata:
|
|
7
|
+
author: beth-team
|
|
8
|
+
version: "1.0.0"
|
|
9
|
+
registry: https://ui.shadcn.com
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# shadcn/ui Component Skill
|
|
13
|
+
|
|
14
|
+
This skill covers shadcn/ui component patterns, installation, customization, and MCP server integration for AI-assisted development.
|
|
15
|
+
|
|
16
|
+
## What is shadcn/ui?
|
|
17
|
+
|
|
18
|
+
shadcn/ui is **not a component library**—it's a code distribution system. You own the code. Components are copied into your project where you can customize them freely.
|
|
19
|
+
|
|
20
|
+
Key principles:
|
|
21
|
+
- **Open Code**: You get the actual component source code
|
|
22
|
+
- **Composition**: Components share a consistent, composable API
|
|
23
|
+
- **Beautiful Defaults**: Carefully designed defaults that work together
|
|
24
|
+
- **AI-Ready**: Open code for LLMs to read, understand, and modify
|
|
25
|
+
|
|
26
|
+
## MCP Server Integration
|
|
27
|
+
|
|
28
|
+
The shadcn MCP server enables AI assistants to interact directly with component registries.
|
|
29
|
+
|
|
30
|
+
### Configuration
|
|
31
|
+
|
|
32
|
+
**VS Code (GitHub Copilot):**
|
|
33
|
+
```bash
|
|
34
|
+
npx shadcn@latest mcp init --client vscode
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Creates `.vscode/mcp.json`:
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"servers": {
|
|
41
|
+
"shadcn": {
|
|
42
|
+
"command": "npx",
|
|
43
|
+
"args": ["shadcn@latest", "mcp"]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Other Clients:**
|
|
50
|
+
- Claude Code: `npx shadcn@latest mcp init --client claude`
|
|
51
|
+
- Cursor: `npx shadcn@latest mcp init --client cursor`
|
|
52
|
+
- Codex: Manual config in `~/.codex/config.toml`
|
|
53
|
+
|
|
54
|
+
### MCP Tools
|
|
55
|
+
|
|
56
|
+
| Tool | Purpose | Example Use |
|
|
57
|
+
|------|---------|-------------|
|
|
58
|
+
| `get_project_registries` | List configured registries | Verify setup |
|
|
59
|
+
| `search_items_in_registries` | Search by name/function | "Find data table component" |
|
|
60
|
+
| `list_items_in_registries` | Browse all components | Explore options |
|
|
61
|
+
| `get_item_details_from_registries` | Get full source code | Understand implementation |
|
|
62
|
+
| `get_item_examples_from_registries` | Get usage examples | Learn patterns |
|
|
63
|
+
| `add_items_to_project` | Install components | "Add button and card" |
|
|
64
|
+
|
|
65
|
+
### Example MCP Prompts
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
# Discovery
|
|
69
|
+
"Show me all available components in the shadcn registry"
|
|
70
|
+
"What form components are available?"
|
|
71
|
+
"Find me a date picker"
|
|
72
|
+
|
|
73
|
+
# Details
|
|
74
|
+
"Show me the code for the dialog component"
|
|
75
|
+
"Get examples of the data-table component"
|
|
76
|
+
"What are the variants for the button component?"
|
|
77
|
+
|
|
78
|
+
# Installation
|
|
79
|
+
"Add button, card, and dialog to this project"
|
|
80
|
+
"Install all the form components"
|
|
81
|
+
"Add the chart component"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Project Setup
|
|
85
|
+
|
|
86
|
+
### Initialize a new project
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npx shadcn@latest init
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This creates:
|
|
93
|
+
- `components.json` - Configuration
|
|
94
|
+
- `lib/utils.ts` - Utility functions (cn helper)
|
|
95
|
+
- `tailwind.config.ts` - Tailwind configuration
|
|
96
|
+
- `globals.css` - CSS variables for theming
|
|
97
|
+
|
|
98
|
+
### Configuration (components.json)
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
103
|
+
"style": "new-york",
|
|
104
|
+
"rsc": true,
|
|
105
|
+
"tsx": true,
|
|
106
|
+
"tailwind": {
|
|
107
|
+
"config": "tailwind.config.ts",
|
|
108
|
+
"css": "app/globals.css",
|
|
109
|
+
"baseColor": "zinc",
|
|
110
|
+
"cssVariables": true
|
|
111
|
+
},
|
|
112
|
+
"aliases": {
|
|
113
|
+
"components": "@/components",
|
|
114
|
+
"utils": "@/lib/utils",
|
|
115
|
+
"ui": "@/components/ui"
|
|
116
|
+
},
|
|
117
|
+
"registries": {
|
|
118
|
+
"@shadcn": "https://ui.shadcn.com/r/{name}.json"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Add components
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Single component
|
|
127
|
+
npx shadcn@latest add button
|
|
128
|
+
|
|
129
|
+
# Multiple components
|
|
130
|
+
npx shadcn@latest add button card dialog
|
|
131
|
+
|
|
132
|
+
# All components
|
|
133
|
+
npx shadcn@latest add --all
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Component Patterns
|
|
137
|
+
|
|
138
|
+
### Basic Usage
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
import { Button } from "@/components/ui/button";
|
|
142
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
143
|
+
|
|
144
|
+
export function Example() {
|
|
145
|
+
return (
|
|
146
|
+
<Card>
|
|
147
|
+
<CardHeader>
|
|
148
|
+
<CardTitle>Hello World</CardTitle>
|
|
149
|
+
</CardHeader>
|
|
150
|
+
<CardContent>
|
|
151
|
+
<Button>Click Me</Button>
|
|
152
|
+
</CardContent>
|
|
153
|
+
</Card>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Composition Pattern
|
|
159
|
+
|
|
160
|
+
shadcn/ui components are composable by design:
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
// Dialog with custom trigger
|
|
164
|
+
<Dialog>
|
|
165
|
+
<DialogTrigger asChild>
|
|
166
|
+
<Button variant="outline">Open Settings</Button>
|
|
167
|
+
</DialogTrigger>
|
|
168
|
+
<DialogContent>
|
|
169
|
+
<DialogHeader>
|
|
170
|
+
<DialogTitle>Settings</DialogTitle>
|
|
171
|
+
<DialogDescription>Configure your preferences.</DialogDescription>
|
|
172
|
+
</DialogHeader>
|
|
173
|
+
{/* Content */}
|
|
174
|
+
<DialogFooter>
|
|
175
|
+
<Button type="submit">Save</Button>
|
|
176
|
+
</DialogFooter>
|
|
177
|
+
</DialogContent>
|
|
178
|
+
</Dialog>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### The cn() Utility
|
|
182
|
+
|
|
183
|
+
The `cn()` helper merges Tailwind classes intelligently:
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { cn } from "@/lib/utils";
|
|
187
|
+
|
|
188
|
+
function MyComponent({ className, ...props }) {
|
|
189
|
+
return (
|
|
190
|
+
<div className={cn("base-styles", className)} {...props} />
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Implementation (uses clsx + tailwind-merge):
|
|
196
|
+
```ts
|
|
197
|
+
import { type ClassValue, clsx } from "clsx";
|
|
198
|
+
import { twMerge } from "tailwind-merge";
|
|
199
|
+
|
|
200
|
+
export function cn(...inputs: ClassValue[]) {
|
|
201
|
+
return twMerge(clsx(inputs));
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Component Variants with cva
|
|
206
|
+
|
|
207
|
+
Use `class-variance-authority` for component variants:
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
211
|
+
import { cn } from "@/lib/utils";
|
|
212
|
+
|
|
213
|
+
const badgeVariants = cva(
|
|
214
|
+
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors",
|
|
215
|
+
{
|
|
216
|
+
variants: {
|
|
217
|
+
variant: {
|
|
218
|
+
default: "border-transparent bg-primary text-primary-foreground",
|
|
219
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground",
|
|
220
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground",
|
|
221
|
+
outline: "text-foreground",
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
defaultVariants: {
|
|
225
|
+
variant: "default",
|
|
226
|
+
},
|
|
227
|
+
}
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
interface BadgeProps
|
|
231
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
232
|
+
VariantProps<typeof badgeVariants> {}
|
|
233
|
+
|
|
234
|
+
function Badge({ className, variant, ...props }: BadgeProps) {
|
|
235
|
+
return (
|
|
236
|
+
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Theming
|
|
242
|
+
|
|
243
|
+
### CSS Variables
|
|
244
|
+
|
|
245
|
+
shadcn/ui uses CSS variables for theming:
|
|
246
|
+
|
|
247
|
+
```css
|
|
248
|
+
:root {
|
|
249
|
+
--background: 0 0% 100%;
|
|
250
|
+
--foreground: 222.2 84% 4.9%;
|
|
251
|
+
--card: 0 0% 100%;
|
|
252
|
+
--card-foreground: 222.2 84% 4.9%;
|
|
253
|
+
--primary: 222.2 47.4% 11.2%;
|
|
254
|
+
--primary-foreground: 210 40% 98%;
|
|
255
|
+
/* ... more variables */
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.dark {
|
|
259
|
+
--background: 222.2 84% 4.9%;
|
|
260
|
+
--foreground: 210 40% 98%;
|
|
261
|
+
/* ... dark mode overrides */
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Using Theme Colors
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
// In Tailwind classes
|
|
269
|
+
<div className="bg-background text-foreground">
|
|
270
|
+
<button className="bg-primary text-primary-foreground">
|
|
271
|
+
Primary Button
|
|
272
|
+
</button>
|
|
273
|
+
</div>
|
|
274
|
+
|
|
275
|
+
// Accessing in JS
|
|
276
|
+
const styles = {
|
|
277
|
+
backgroundColor: 'hsl(var(--background))',
|
|
278
|
+
color: 'hsl(var(--foreground))',
|
|
279
|
+
};
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Dark Mode
|
|
283
|
+
|
|
284
|
+
Configure dark mode in `tailwind.config.ts`:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
module.exports = {
|
|
288
|
+
darkMode: ["class"],
|
|
289
|
+
// ...
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Toggle with a theme provider:
|
|
294
|
+
|
|
295
|
+
```tsx
|
|
296
|
+
'use client';
|
|
297
|
+
|
|
298
|
+
import { ThemeProvider } from "next-themes";
|
|
299
|
+
|
|
300
|
+
export function Providers({ children }) {
|
|
301
|
+
return (
|
|
302
|
+
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
|
303
|
+
{children}
|
|
304
|
+
</ThemeProvider>
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Form Patterns
|
|
310
|
+
|
|
311
|
+
### With React Hook Form + Zod
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
'use client';
|
|
315
|
+
|
|
316
|
+
import { useForm } from "react-hook-form";
|
|
317
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
318
|
+
import { z } from "zod";
|
|
319
|
+
import { Button } from "@/components/ui/button";
|
|
320
|
+
import {
|
|
321
|
+
Form,
|
|
322
|
+
FormControl,
|
|
323
|
+
FormField,
|
|
324
|
+
FormItem,
|
|
325
|
+
FormLabel,
|
|
326
|
+
FormMessage,
|
|
327
|
+
} from "@/components/ui/form";
|
|
328
|
+
import { Input } from "@/components/ui/input";
|
|
329
|
+
|
|
330
|
+
const schema = z.object({
|
|
331
|
+
email: z.string().email(),
|
|
332
|
+
password: z.string().min(8),
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
export function LoginForm() {
|
|
336
|
+
const form = useForm({
|
|
337
|
+
resolver: zodResolver(schema),
|
|
338
|
+
defaultValues: { email: "", password: "" },
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
async function onSubmit(values: z.infer<typeof schema>) {
|
|
342
|
+
// Handle submission
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return (
|
|
346
|
+
<Form {...form}>
|
|
347
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
|
348
|
+
<FormField
|
|
349
|
+
control={form.control}
|
|
350
|
+
name="email"
|
|
351
|
+
render={({ field }) => (
|
|
352
|
+
<FormItem>
|
|
353
|
+
<FormLabel>Email</FormLabel>
|
|
354
|
+
<FormControl>
|
|
355
|
+
<Input type="email" {...field} />
|
|
356
|
+
</FormControl>
|
|
357
|
+
<FormMessage />
|
|
358
|
+
</FormItem>
|
|
359
|
+
)}
|
|
360
|
+
/>
|
|
361
|
+
<FormField
|
|
362
|
+
control={form.control}
|
|
363
|
+
name="password"
|
|
364
|
+
render={({ field }) => (
|
|
365
|
+
<FormItem>
|
|
366
|
+
<FormLabel>Password</FormLabel>
|
|
367
|
+
<FormControl>
|
|
368
|
+
<Input type="password" {...field} />
|
|
369
|
+
</FormControl>
|
|
370
|
+
<FormMessage />
|
|
371
|
+
</FormItem>
|
|
372
|
+
)}
|
|
373
|
+
/>
|
|
374
|
+
<Button type="submit">Login</Button>
|
|
375
|
+
</form>
|
|
376
|
+
</Form>
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Data Display
|
|
382
|
+
|
|
383
|
+
### DataTable Pattern
|
|
384
|
+
|
|
385
|
+
```tsx
|
|
386
|
+
import {
|
|
387
|
+
Table,
|
|
388
|
+
TableBody,
|
|
389
|
+
TableCell,
|
|
390
|
+
TableHead,
|
|
391
|
+
TableHeader,
|
|
392
|
+
TableRow,
|
|
393
|
+
} from "@/components/ui/table";
|
|
394
|
+
|
|
395
|
+
interface Column<T> {
|
|
396
|
+
header: string;
|
|
397
|
+
accessor: keyof T | ((row: T) => React.ReactNode);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
interface DataTableProps<T> {
|
|
401
|
+
columns: Column<T>[];
|
|
402
|
+
data: T[];
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function DataTable<T extends { id: string }>({
|
|
406
|
+
columns,
|
|
407
|
+
data
|
|
408
|
+
}: DataTableProps<T>) {
|
|
409
|
+
return (
|
|
410
|
+
<Table>
|
|
411
|
+
<TableHeader>
|
|
412
|
+
<TableRow>
|
|
413
|
+
{columns.map((col, i) => (
|
|
414
|
+
<TableHead key={i}>{col.header}</TableHead>
|
|
415
|
+
))}
|
|
416
|
+
</TableRow>
|
|
417
|
+
</TableHeader>
|
|
418
|
+
<TableBody>
|
|
419
|
+
{data.map((row) => (
|
|
420
|
+
<TableRow key={row.id}>
|
|
421
|
+
{columns.map((col, i) => (
|
|
422
|
+
<TableCell key={i}>
|
|
423
|
+
{typeof col.accessor === 'function'
|
|
424
|
+
? col.accessor(row)
|
|
425
|
+
: String(row[col.accessor])}
|
|
426
|
+
</TableCell>
|
|
427
|
+
))}
|
|
428
|
+
</TableRow>
|
|
429
|
+
))}
|
|
430
|
+
</TableBody>
|
|
431
|
+
</Table>
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Accessibility
|
|
437
|
+
|
|
438
|
+
shadcn/ui components are built on Radix UI primitives which handle:
|
|
439
|
+
|
|
440
|
+
- Keyboard navigation
|
|
441
|
+
- Focus management
|
|
442
|
+
- ARIA attributes
|
|
443
|
+
- Screen reader announcements
|
|
444
|
+
|
|
445
|
+
### Ensure Accessibility in Custom Code
|
|
446
|
+
|
|
447
|
+
```tsx
|
|
448
|
+
// Always provide accessible labels
|
|
449
|
+
<Button aria-label="Close dialog">
|
|
450
|
+
<X className="h-4 w-4" />
|
|
451
|
+
</Button>
|
|
452
|
+
|
|
453
|
+
// Use sr-only for screen reader text
|
|
454
|
+
<span className="sr-only">Loading</span>
|
|
455
|
+
|
|
456
|
+
// Handle focus management
|
|
457
|
+
<Dialog onOpenChange={(open) => {
|
|
458
|
+
if (!open) {
|
|
459
|
+
// Return focus to trigger
|
|
460
|
+
triggerRef.current?.focus();
|
|
461
|
+
}
|
|
462
|
+
}}>
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## Best Practices
|
|
466
|
+
|
|
467
|
+
### DO
|
|
468
|
+
|
|
469
|
+
- ✅ Accept `className` prop for composition
|
|
470
|
+
- ✅ Use `cn()` to merge classes
|
|
471
|
+
- ✅ Forward refs when needed
|
|
472
|
+
- ✅ Keep components focused and composable
|
|
473
|
+
- ✅ Use TypeScript for props
|
|
474
|
+
- ✅ Add loading/disabled states
|
|
475
|
+
|
|
476
|
+
### DON'T
|
|
477
|
+
|
|
478
|
+
- ❌ Modify components in `ui/` directly for one-off changes
|
|
479
|
+
- ❌ Override styles with `!important`
|
|
480
|
+
- ❌ Create wrapper components when composition works
|
|
481
|
+
- ❌ Skip accessibility testing
|
|
482
|
+
- ❌ Use deprecated patterns
|
|
483
|
+
|
|
484
|
+
### When to Customize vs Compose
|
|
485
|
+
|
|
486
|
+
**Compose** (use as-is with props):
|
|
487
|
+
```tsx
|
|
488
|
+
<Button size="lg" variant="outline">Large Outline</Button>
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
**Extend** (create specialized version):
|
|
492
|
+
```tsx
|
|
493
|
+
// components/IconButton.tsx
|
|
494
|
+
import { Button, ButtonProps } from "@/components/ui/button";
|
|
495
|
+
import { cn } from "@/lib/utils";
|
|
496
|
+
|
|
497
|
+
interface IconButtonProps extends ButtonProps {
|
|
498
|
+
icon: React.ReactNode;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
export function IconButton({ icon, className, ...props }: IconButtonProps) {
|
|
502
|
+
return (
|
|
503
|
+
<Button className={cn("p-2", className)} {...props}>
|
|
504
|
+
{icon}
|
|
505
|
+
</Button>
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
**Customize** (modify source when pattern doesn't exist):
|
|
511
|
+
- Fork `ui/button.tsx` only for project-wide changes
|
|
512
|
+
- Document the customization
|
|
513
|
+
|
|
514
|
+
## Common Components Reference
|
|
515
|
+
|
|
516
|
+
| Component | Use For |
|
|
517
|
+
|-----------|---------|
|
|
518
|
+
| `Button` | Actions, form submissions |
|
|
519
|
+
| `Card` | Content containers |
|
|
520
|
+
| `Dialog` | Modal interactions |
|
|
521
|
+
| `Sheet` | Side panels |
|
|
522
|
+
| `Form` | Form containers with validation |
|
|
523
|
+
| `Input`, `Textarea` | Text inputs |
|
|
524
|
+
| `Select` | Dropdowns |
|
|
525
|
+
| `Checkbox`, `Radio`, `Switch` | Toggles |
|
|
526
|
+
| `Table` | Data display |
|
|
527
|
+
| `Tabs` | Content organization |
|
|
528
|
+
| `Toast` | Notifications |
|
|
529
|
+
| `Tooltip` | Contextual info |
|
|
530
|
+
| `Command` | Command palettes |
|
|
531
|
+
| `DropdownMenu` | Action menus |
|
|
532
|
+
| `NavigationMenu` | Site navigation |
|
|
533
|
+
| `Avatar` | User images |
|
|
534
|
+
| `Badge` | Status indicators |
|
|
535
|
+
| `Skeleton` | Loading placeholders |
|
|
536
|
+
|
|
537
|
+
## Troubleshooting
|
|
538
|
+
|
|
539
|
+
### Component not found
|
|
540
|
+
```bash
|
|
541
|
+
# Check available components
|
|
542
|
+
npx shadcn@latest add --help
|
|
543
|
+
|
|
544
|
+
# Search via MCP
|
|
545
|
+
"List all components in the shadcn registry"
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### Styling conflicts
|
|
549
|
+
- Ensure Tailwind is properly configured
|
|
550
|
+
- Check CSS variable definitions
|
|
551
|
+
- Use `cn()` for class merging
|
|
552
|
+
|
|
553
|
+
### Dark mode not working
|
|
554
|
+
- Verify `darkMode: ["class"]` in tailwind.config
|
|
555
|
+
- Check ThemeProvider is wrapping your app
|
|
556
|
+
- Ensure CSS variables have dark mode overrides
|
|
557
|
+
|
|
558
|
+
### TypeScript errors
|
|
559
|
+
- Run `npx shadcn@latest add [component]` to get latest types
|
|
560
|
+
- Check component is exported from `ui/` index
|
|
561
|
+
|
|
562
|
+
````
|