oxycode-skills 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/LICENSE +21 -0
- package/README.md +143 -0
- package/index.js +17 -0
- package/package.json +49 -0
- package/skills/anti-slop/README.md +106 -0
- package/skills/anti-slop/SKILL.md +311 -0
- package/skills/anti-slop/examples/before-after.md +380 -0
- package/skills/anti-slop/references/quality-rubric.md +313 -0
- package/skills/anti-slop/references/slop-patterns.md +433 -0
- package/skills/component-architect/README.md +186 -0
- package/skills/component-architect/SKILL.md +644 -0
- package/skills/component-architect/examples/component-patterns.md +569 -0
- package/skills/component-architect/references/atomic-design.md +516 -0
- package/skills/design-audit/README.md +114 -0
- package/skills/design-audit/SKILL.md +305 -0
- package/skills/design-audit/examples/audit-report.md +424 -0
- package/skills/design-audit/references/scoring-rubric.md +498 -0
- package/skills/design-md/README.md +106 -0
- package/skills/design-md/SKILL.md +262 -0
- package/skills/design-md/examples/bad-design.md +195 -0
- package/skills/design-md/examples/good-design.md +250 -0
- package/skills/design-md/references/design-md-spec.md +267 -0
- package/skills/design-md/scripts/validate.sh +134 -0
- package/skills/ui-builder/README.md +169 -0
- package/skills/ui-builder/SKILL.md +357 -0
- package/skills/ui-builder/examples/dashboard.md +323 -0
- package/skills/ui-builder/examples/landing-page.md +396 -0
- package/skills/ui-builder/references/component-patterns.md +396 -0
- package/skills/ui-builder/references/layout-system.md +423 -0
- package/skills/ui-builder/references/polish-checklist.md +221 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# ui-builder
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Generate production-ready UI components and pages from text prompts. Creates Vercel-quality React/TypeScript code with Tailwind CSS, following design system tokens from DESIGN.md files.
|
|
6
|
+
|
|
7
|
+
## Trigger
|
|
8
|
+
|
|
9
|
+
Use this skill when:
|
|
10
|
+
- User asks to create UI components
|
|
11
|
+
- User wants to build a page or layout
|
|
12
|
+
- User says "build a dashboard" or "create a landing page"
|
|
13
|
+
- User provides a DESIGN.md and wants UI generated from it
|
|
14
|
+
- User says "ui-builder" followed by a description
|
|
15
|
+
|
|
16
|
+
## Instructions
|
|
17
|
+
|
|
18
|
+
### Step 1: Understand the Request
|
|
19
|
+
|
|
20
|
+
Before generating UI, understand:
|
|
21
|
+
- What type of UI (dashboard, landing page, form, etc.)
|
|
22
|
+
- Components needed (sidebar, cards, tables, etc.)
|
|
23
|
+
- Layout requirements (grid, flex, etc.)
|
|
24
|
+
- Responsive breakpoints
|
|
25
|
+
- Dark/light theme support
|
|
26
|
+
|
|
27
|
+
### Step 2: Read DESIGN.md (If Provided)
|
|
28
|
+
|
|
29
|
+
If a DESIGN.md file exists:
|
|
30
|
+
1. Parse design tokens (colors, typography, spacing)
|
|
31
|
+
2. Apply tokens to all generated code
|
|
32
|
+
3. Ensure consistency with existing design system
|
|
33
|
+
|
|
34
|
+
If no DESIGN.md exists:
|
|
35
|
+
1. Use default Vercel-style tokens
|
|
36
|
+
2. Generate a DESIGN.md for future reference
|
|
37
|
+
|
|
38
|
+
### Step 3: Generate Components
|
|
39
|
+
|
|
40
|
+
Generate React/TypeScript components with:
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
// Component structure
|
|
44
|
+
import { cn } from '@/lib/utils'
|
|
45
|
+
|
|
46
|
+
interface ComponentProps {
|
|
47
|
+
// TypeScript props
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function Component({ ...props }: ComponentProps) {
|
|
51
|
+
return (
|
|
52
|
+
<div className={cn(
|
|
53
|
+
// Base styles
|
|
54
|
+
"base-classes",
|
|
55
|
+
// Variant classes
|
|
56
|
+
variant && "variant-classes",
|
|
57
|
+
// Custom classes
|
|
58
|
+
className
|
|
59
|
+
)}>
|
|
60
|
+
{/* Content */}
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Step 4: Apply Design Tokens
|
|
67
|
+
|
|
68
|
+
Use design tokens consistently:
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
// ✅ CORRECT: Using design tokens
|
|
72
|
+
<div className="bg-zinc-950 border border-zinc-800 rounded-lg p-6">
|
|
73
|
+
|
|
74
|
+
// ❌ WRONG: Hardcoded values
|
|
75
|
+
<div className="bg-[#09090b] border border-[#27272a] rounded-[12px] p-6">
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Step 5: Ensure Quality
|
|
79
|
+
|
|
80
|
+
Check for:
|
|
81
|
+
- ✅ Semantic HTML (section, article, nav, etc.)
|
|
82
|
+
- ✅ Accessibility (aria-labels, roles, etc.)
|
|
83
|
+
- ✅ Responsive design (mobile-first)
|
|
84
|
+
- ✅ TypeScript types
|
|
85
|
+
- ✅ Proper component composition
|
|
86
|
+
- ✅ No hardcoded values
|
|
87
|
+
|
|
88
|
+
### Step 6: Generate Before/After
|
|
89
|
+
|
|
90
|
+
Show the difference:
|
|
91
|
+
|
|
92
|
+
**Before (AI Slop):**
|
|
93
|
+
```tsx
|
|
94
|
+
<div className="bg-gradient-to-br from-purple-500 to-blue-500 p-8 rounded-2xl">
|
|
95
|
+
<h1 className="text-3xl font-bold text-white text-center">Welcome</h1>
|
|
96
|
+
</div>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**After (Vercel-Quality):**
|
|
100
|
+
```tsx
|
|
101
|
+
<section className="bg-zinc-950 border border-zinc-800 rounded-xl p-6">
|
|
102
|
+
<h2 className="text-xl font-semibold text-zinc-100 tracking-tight">
|
|
103
|
+
Welcome
|
|
104
|
+
</h2>
|
|
105
|
+
<p className="mt-2 text-sm text-zinc-400 leading-relaxed">
|
|
106
|
+
Description here
|
|
107
|
+
</p>
|
|
108
|
+
</section>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Component Patterns
|
|
112
|
+
|
|
113
|
+
### Card Pattern
|
|
114
|
+
```tsx
|
|
115
|
+
<div className="bg-white border border-zinc-200 rounded-lg p-6 shadow-sm">
|
|
116
|
+
<h3 className="text-lg font-semibold text-zinc-900">Title</h3>
|
|
117
|
+
<p className="mt-2 text-sm text-zinc-500">Description</p>
|
|
118
|
+
</div>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Button Pattern
|
|
122
|
+
```tsx
|
|
123
|
+
<button className="inline-flex items-center justify-center px-4 py-2 text-sm font-medium text-white bg-zinc-900 rounded-lg hover:bg-zinc-800 transition-colors">
|
|
124
|
+
Click me
|
|
125
|
+
</button>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Input Pattern
|
|
129
|
+
```tsx
|
|
130
|
+
<input
|
|
131
|
+
type="text"
|
|
132
|
+
className="w-full px-3 py-2 text-sm border border-zinc-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-zinc-500 focus:border-transparent"
|
|
133
|
+
placeholder="Enter text..."
|
|
134
|
+
/>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Navigation Pattern
|
|
138
|
+
```tsx
|
|
139
|
+
<nav className="flex items-center gap-6 px-6 py-4 border-b border-zinc-200">
|
|
140
|
+
<a href="/" className="text-sm font-medium text-zinc-900">Home</a>
|
|
141
|
+
<a href="/about" className="text-sm text-zinc-500 hover:text-zinc-900">About</a>
|
|
142
|
+
</nav>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Table Pattern
|
|
146
|
+
```tsx
|
|
147
|
+
<table className="w-full text-sm text-left">
|
|
148
|
+
<thead className="text-xs text-zinc-500 uppercase bg-zinc-50">
|
|
149
|
+
<tr>
|
|
150
|
+
<th className="px-6 py-3">Name</th>
|
|
151
|
+
<th className="px-6 py-3">Status</th>
|
|
152
|
+
</tr>
|
|
153
|
+
</thead>
|
|
154
|
+
<tbody className="divide-y divide-zinc-200">
|
|
155
|
+
<tr>
|
|
156
|
+
<td className="px-6 py-4">Item 1</td>
|
|
157
|
+
<td className="px-6 py-4">Active</td>
|
|
158
|
+
</tr>
|
|
159
|
+
</tbody>
|
|
160
|
+
</table>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Layout Patterns
|
|
164
|
+
|
|
165
|
+
### Dashboard Layout
|
|
166
|
+
```tsx
|
|
167
|
+
<div className="flex h-screen">
|
|
168
|
+
{/* Sidebar */}
|
|
169
|
+
<aside className="w-64 border-r border-zinc-200 bg-zinc-50">
|
|
170
|
+
{/* Navigation */}
|
|
171
|
+
</aside>
|
|
172
|
+
|
|
173
|
+
{/* Main Content */}
|
|
174
|
+
<main className="flex-1 overflow-auto">
|
|
175
|
+
{/* Header */}
|
|
176
|
+
<header className="border-b border-zinc-200 px-6 py-4">
|
|
177
|
+
<h1 className="text-lg font-semibold">Dashboard</h1>
|
|
178
|
+
</header>
|
|
179
|
+
|
|
180
|
+
{/* Content */}
|
|
181
|
+
<div className="p-6">
|
|
182
|
+
{/* Cards, tables, etc. */}
|
|
183
|
+
</div>
|
|
184
|
+
</main>
|
|
185
|
+
</div>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Landing Page Layout
|
|
189
|
+
```tsx
|
|
190
|
+
<div className="min-h-screen">
|
|
191
|
+
{/* Navigation */}
|
|
192
|
+
<nav className="border-b border-zinc-200">
|
|
193
|
+
{/* Logo, links, CTA */}
|
|
194
|
+
</nav>
|
|
195
|
+
|
|
196
|
+
{/* Hero Section */}
|
|
197
|
+
<section className="py-20 px-6 text-center">
|
|
198
|
+
<h1 className="text-4xl font-bold">Headline</h1>
|
|
199
|
+
<p className="mt-4 text-lg text-zinc-500">Subheadline</p>
|
|
200
|
+
</section>
|
|
201
|
+
|
|
202
|
+
{/* Features */}
|
|
203
|
+
<section className="py-20 px-6 bg-zinc-50">
|
|
204
|
+
{/* Feature cards */}
|
|
205
|
+
</section>
|
|
206
|
+
|
|
207
|
+
{/* Footer */}
|
|
208
|
+
<footer className="py-12 px-6 border-t border-zinc-200">
|
|
209
|
+
{/* Footer content */}
|
|
210
|
+
</footer>
|
|
211
|
+
</div>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Examples
|
|
215
|
+
|
|
216
|
+
### Example 1: Analytics Dashboard
|
|
217
|
+
|
|
218
|
+
**Input:**
|
|
219
|
+
"Create an analytics dashboard with sidebar navigation, stats cards, and a data table"
|
|
220
|
+
|
|
221
|
+
**Output:**
|
|
222
|
+
```tsx
|
|
223
|
+
// app/dashboard/page.tsx
|
|
224
|
+
import { Sidebar } from '@/components/sidebar'
|
|
225
|
+
import { StatsCard } from '@/components/stats-card'
|
|
226
|
+
import { DataTable } from '@/components/data-table'
|
|
227
|
+
|
|
228
|
+
export default function Dashboard() {
|
|
229
|
+
return (
|
|
230
|
+
<div className="flex h-screen bg-zinc-950">
|
|
231
|
+
<Sidebar />
|
|
232
|
+
|
|
233
|
+
<main className="flex-1 overflow-auto">
|
|
234
|
+
<header className="border-b border-zinc-800 px-6 py-4">
|
|
235
|
+
<h1 className="text-lg font-semibold text-zinc-100">Analytics</h1>
|
|
236
|
+
</header>
|
|
237
|
+
|
|
238
|
+
<div className="p-6 space-y-6">
|
|
239
|
+
{/* Stats Grid */}
|
|
240
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
241
|
+
<StatsCard title="Total Users" value="12,345" change="+12%" />
|
|
242
|
+
<StatsCard title="Revenue" value="$45,678" change="+8%" />
|
|
243
|
+
<StatsCard title="Orders" value="1,234" change="+5%" />
|
|
244
|
+
<StatsCard title="Conversion" value="3.2%" change="+0.5%" />
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
{/* Data Table */}
|
|
248
|
+
<div className="bg-zinc-900 border border-zinc-800 rounded-lg">
|
|
249
|
+
<DataTable />
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
</main>
|
|
253
|
+
</div>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Example 2: SaaS Landing Page
|
|
259
|
+
|
|
260
|
+
**Input:**
|
|
261
|
+
"Build a SaaS landing page with hero section, pricing table, and testimonials"
|
|
262
|
+
|
|
263
|
+
**Output:**
|
|
264
|
+
```tsx
|
|
265
|
+
// app/page.tsx
|
|
266
|
+
import { Hero } from '@/components/hero'
|
|
267
|
+
import { Pricing } from '@/components/pricing'
|
|
268
|
+
import { Testimonials } from '@/components/testimonials'
|
|
269
|
+
|
|
270
|
+
export default function LandingPage() {
|
|
271
|
+
return (
|
|
272
|
+
<div className="min-h-screen bg-white">
|
|
273
|
+
{/* Navigation */}
|
|
274
|
+
<nav className="border-b border-zinc-200">
|
|
275
|
+
<div className="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between">
|
|
276
|
+
<span className="text-xl font-bold">SaaS</span>
|
|
277
|
+
<div className="flex items-center gap-6">
|
|
278
|
+
<a href="#features" className="text-sm text-zinc-600 hover:text-zinc-900">Features</a>
|
|
279
|
+
<a href="#pricing" className="text-sm text-zinc-600 hover:text-zinc-900">Pricing</a>
|
|
280
|
+
<button className="px-4 py-2 text-sm font-medium text-white bg-zinc-900 rounded-lg hover:bg-zinc-800">
|
|
281
|
+
Get Started
|
|
282
|
+
</button>
|
|
283
|
+
</div>
|
|
284
|
+
</div>
|
|
285
|
+
</nav>
|
|
286
|
+
|
|
287
|
+
<Hero />
|
|
288
|
+
<Pricing />
|
|
289
|
+
<Testimonials />
|
|
290
|
+
|
|
291
|
+
{/* Footer */}
|
|
292
|
+
<footer className="border-t border-zinc-200 py-12">
|
|
293
|
+
<div className="max-w-7xl mx-auto px-6 text-center text-sm text-zinc-500">
|
|
294
|
+
© 2026 SaaS. All rights reserved.
|
|
295
|
+
</div>
|
|
296
|
+
</footer>
|
|
297
|
+
</div>
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Anti-Patterns to Avoid
|
|
303
|
+
|
|
304
|
+
### ❌ Don't Use Gradients
|
|
305
|
+
```tsx
|
|
306
|
+
// BAD
|
|
307
|
+
<div className="bg-gradient-to-br from-purple-500 to-blue-500">
|
|
308
|
+
|
|
309
|
+
// GOOD
|
|
310
|
+
<div className="bg-zinc-900">
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### ❌ Don't Center Everything
|
|
314
|
+
```tsx
|
|
315
|
+
// BAD
|
|
316
|
+
<div className="text-center">
|
|
317
|
+
|
|
318
|
+
// GOOD (when appropriate)
|
|
319
|
+
<div className="text-left">
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### ❌ Don't Use Rainbow Colors
|
|
323
|
+
```tsx
|
|
324
|
+
// BAD
|
|
325
|
+
<div className="text-red-500 text-blue-500 text-green-500">
|
|
326
|
+
|
|
327
|
+
// GOOD
|
|
328
|
+
<div className="text-zinc-100">
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### ❌ Don't Use Large Border Radius
|
|
332
|
+
```tsx
|
|
333
|
+
// BAD
|
|
334
|
+
<div className="rounded-3xl">
|
|
335
|
+
|
|
336
|
+
// GOOD
|
|
337
|
+
<div className="rounded-lg">
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## References
|
|
341
|
+
|
|
342
|
+
- [Vercel Design System](https://vercel.com/design)
|
|
343
|
+
- [shadcn/ui](https://ui.shadcn.com/)
|
|
344
|
+
- [Tailwind CSS](https://tailwindcss.com)
|
|
345
|
+
- [DESIGN.md Spec](https://github.com/google-labs-code/design.md)
|
|
346
|
+
|
|
347
|
+
## Validation Checklist
|
|
348
|
+
|
|
349
|
+
Before delivering UI code, verify:
|
|
350
|
+
- [ ] Uses design tokens (no hardcoded values)
|
|
351
|
+
- [ ] Semantic HTML elements
|
|
352
|
+
- [ ] Accessibility attributes
|
|
353
|
+
- [ ] Responsive design
|
|
354
|
+
- [ ] TypeScript types
|
|
355
|
+
- [ ] No anti-patterns (gradients, centered everything, rainbow colors)
|
|
356
|
+
- [ ] Consistent spacing
|
|
357
|
+
- [ ] Proper color hierarchy
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# Dashboard Example
|
|
2
|
+
|
|
3
|
+
## Input Prompt
|
|
4
|
+
"Create an analytics dashboard with sidebar navigation, stats cards, and a data table"
|
|
5
|
+
|
|
6
|
+
## Output: React/TypeScript + Tailwind CSS
|
|
7
|
+
|
|
8
|
+
### app/dashboard/page.tsx
|
|
9
|
+
```tsx
|
|
10
|
+
import { Sidebar } from '@/components/sidebar'
|
|
11
|
+
import { StatsCard } from '@/components/stats-card'
|
|
12
|
+
import { DataTable } from '@/components/data-table'
|
|
13
|
+
|
|
14
|
+
export default function Dashboard() {
|
|
15
|
+
return (
|
|
16
|
+
<div className="flex h-screen bg-zinc-50">
|
|
17
|
+
{/* Sidebar */}
|
|
18
|
+
<Sidebar />
|
|
19
|
+
|
|
20
|
+
{/* Main Content */}
|
|
21
|
+
<div className="flex-1 flex flex-col overflow-hidden">
|
|
22
|
+
{/* Header */}
|
|
23
|
+
<header className="h-16 border-b border-zinc-200 bg-white flex items-center justify-between px-6">
|
|
24
|
+
<div>
|
|
25
|
+
<h1 className="text-lg font-semibold text-zinc-900">Analytics</h1>
|
|
26
|
+
<p className="text-sm text-zinc-500">Overview of your performance</p>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div className="flex items-center gap-4">
|
|
30
|
+
<button className="px-4 py-2 text-sm font-medium text-zinc-700 border border-zinc-300 rounded-lg hover:bg-zinc-50 transition-colors">
|
|
31
|
+
Export
|
|
32
|
+
</button>
|
|
33
|
+
<button className="px-4 py-2 text-sm font-medium text-white bg-zinc-900 rounded-lg hover:bg-zinc-800 transition-colors">
|
|
34
|
+
Invite
|
|
35
|
+
</button>
|
|
36
|
+
</div>
|
|
37
|
+
</header>
|
|
38
|
+
|
|
39
|
+
{/* Content */}
|
|
40
|
+
<main className="flex-1 overflow-auto p-6">
|
|
41
|
+
{/* Stats Grid */}
|
|
42
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
|
43
|
+
<StatsCard
|
|
44
|
+
title="Total Users"
|
|
45
|
+
value="12,345"
|
|
46
|
+
change="+12%"
|
|
47
|
+
trend="up"
|
|
48
|
+
/>
|
|
49
|
+
<StatsCard
|
|
50
|
+
title="Revenue"
|
|
51
|
+
value="$45,678"
|
|
52
|
+
change="+8%"
|
|
53
|
+
trend="up"
|
|
54
|
+
/>
|
|
55
|
+
<StatsCard
|
|
56
|
+
title="Orders"
|
|
57
|
+
value="1,234"
|
|
58
|
+
change="+5%"
|
|
59
|
+
trend="up"
|
|
60
|
+
/>
|
|
61
|
+
<StatsCard
|
|
62
|
+
title="Conversion"
|
|
63
|
+
value="3.2%"
|
|
64
|
+
change="-0.5%"
|
|
65
|
+
trend="down"
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
{/* Data Table */}
|
|
70
|
+
<div className="bg-white border border-zinc-200 rounded-lg">
|
|
71
|
+
<div className="px-6 py-4 border-b border-zinc-200">
|
|
72
|
+
<h2 className="text-base font-semibold text-zinc-900">Recent Transactions</h2>
|
|
73
|
+
<p className="text-sm text-zinc-500">Your latest orders and transactions</p>
|
|
74
|
+
</div>
|
|
75
|
+
<DataTable />
|
|
76
|
+
</div>
|
|
77
|
+
</main>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### components/sidebar.tsx
|
|
85
|
+
```tsx
|
|
86
|
+
'use client'
|
|
87
|
+
|
|
88
|
+
import Link from 'next/link'
|
|
89
|
+
import { usePathname } from 'next/navigation'
|
|
90
|
+
import { cn } from '@/lib/utils'
|
|
91
|
+
|
|
92
|
+
const navigation = [
|
|
93
|
+
{ name: 'Dashboard', href: '/dashboard', icon: HomeIcon },
|
|
94
|
+
{ name: 'Analytics', href: '/analytics', icon: ChartIcon },
|
|
95
|
+
{ name: 'Customers', href: '/customers', icon: UsersIcon },
|
|
96
|
+
{ name: 'Products', href: '/products', icon: BoxIcon },
|
|
97
|
+
{ name: 'Settings', href: '/settings', icon: CogIcon },
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
export function Sidebar() {
|
|
101
|
+
const pathname = usePathname()
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<aside className="w-64 border-r border-zinc-200 bg-white flex flex-col">
|
|
105
|
+
{/* Logo */}
|
|
106
|
+
<div className="h-16 flex items-center px-6 border-b border-zinc-200">
|
|
107
|
+
<div className="flex items-center gap-2">
|
|
108
|
+
<div className="w-8 h-8 bg-zinc-900 rounded-lg flex items-center justify-center">
|
|
109
|
+
<span className="text-white text-sm font-bold">A</span>
|
|
110
|
+
</div>
|
|
111
|
+
<span className="text-xl font-bold text-zinc-900">Analytics</span>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
{/* Navigation */}
|
|
116
|
+
<nav className="flex-1 p-4 space-y-1">
|
|
117
|
+
{navigation.map((item) => {
|
|
118
|
+
const isActive = pathname === item.href
|
|
119
|
+
return (
|
|
120
|
+
<Link
|
|
121
|
+
key={item.name}
|
|
122
|
+
href={item.href}
|
|
123
|
+
className={cn(
|
|
124
|
+
'flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-lg transition-colors',
|
|
125
|
+
isActive
|
|
126
|
+
? 'bg-zinc-100 text-zinc-900'
|
|
127
|
+
: 'text-zinc-600 hover:text-zinc-900 hover:bg-zinc-50'
|
|
128
|
+
)}
|
|
129
|
+
>
|
|
130
|
+
<item.icon className="w-5 h-5" />
|
|
131
|
+
{item.name}
|
|
132
|
+
</Link>
|
|
133
|
+
)
|
|
134
|
+
})}
|
|
135
|
+
</nav>
|
|
136
|
+
|
|
137
|
+
{/* User */}
|
|
138
|
+
<div className="p-4 border-t border-zinc-200">
|
|
139
|
+
<div className="flex items-center gap-3">
|
|
140
|
+
<div className="w-10 h-10 bg-zinc-200 rounded-full flex items-center justify-center">
|
|
141
|
+
<span className="text-sm font-medium text-zinc-600">JD</span>
|
|
142
|
+
</div>
|
|
143
|
+
<div className="flex-1 min-w-0">
|
|
144
|
+
<p className="text-sm font-medium text-zinc-900 truncate">John Doe</p>
|
|
145
|
+
<p className="text-xs text-zinc-500 truncate">john@example.com</p>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</aside>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Icon components (simplified)
|
|
154
|
+
function HomeIcon({ className }: { className?: string }) {
|
|
155
|
+
return (
|
|
156
|
+
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
157
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
|
158
|
+
</svg>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function ChartIcon({ className }: { className?: string }) {
|
|
163
|
+
return (
|
|
164
|
+
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
165
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
|
166
|
+
</svg>
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function UsersIcon({ className }: { className?: string }) {
|
|
171
|
+
return (
|
|
172
|
+
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
173
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
|
|
174
|
+
</svg>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function BoxIcon({ className }: { className?: string }) {
|
|
179
|
+
return (
|
|
180
|
+
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
181
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
|
|
182
|
+
</svg>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function CogIcon({ className }: { className?: string }) {
|
|
187
|
+
return (
|
|
188
|
+
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
189
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
|
190
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
191
|
+
</svg>
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### components/stats-card.tsx
|
|
197
|
+
```tsx
|
|
198
|
+
import { cn } from '@/lib/utils'
|
|
199
|
+
|
|
200
|
+
interface StatsCardProps {
|
|
201
|
+
title: string
|
|
202
|
+
value: string
|
|
203
|
+
change: string
|
|
204
|
+
trend: 'up' | 'down'
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function StatsCard({ title, value, change, trend }: StatsCardProps) {
|
|
208
|
+
return (
|
|
209
|
+
<div className="bg-white border border-zinc-200 rounded-lg p-6">
|
|
210
|
+
<div className="flex items-center justify-between">
|
|
211
|
+
<p className="text-sm font-medium text-zinc-500">{title}</p>
|
|
212
|
+
<span
|
|
213
|
+
className={cn(
|
|
214
|
+
'text-xs font-medium px-2 py-1 rounded-full',
|
|
215
|
+
trend === 'up'
|
|
216
|
+
? 'text-green-700 bg-green-100'
|
|
217
|
+
: 'text-red-700 bg-red-100'
|
|
218
|
+
)}
|
|
219
|
+
>
|
|
220
|
+
{change}
|
|
221
|
+
</span>
|
|
222
|
+
</div>
|
|
223
|
+
<p className="mt-2 text-3xl font-bold text-zinc-900">{value}</p>
|
|
224
|
+
</div>
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### components/data-table.tsx
|
|
230
|
+
```tsx
|
|
231
|
+
const data = [
|
|
232
|
+
{ id: 1, customer: 'John Doe', amount: '$123.45', status: 'Completed', date: '2026-08-24' },
|
|
233
|
+
{ id: 2, customer: 'Jane Smith', amount: '$67.89', status: 'Pending', date: '2026-08-23' },
|
|
234
|
+
{ id: 3, customer: 'Bob Johnson', amount: '$234.56', status: 'Completed', date: '2026-08-22' },
|
|
235
|
+
{ id: 4, customer: 'Alice Brown', amount: '$89.01', status: 'Failed', date: '2026-08-21' },
|
|
236
|
+
{ id: 5, customer: 'Charlie Wilson', amount: '$345.67', status: 'Completed', date: '2026-08-20' },
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
const statusStyles: Record<string, string> = {
|
|
240
|
+
Completed: 'bg-green-100 text-green-800',
|
|
241
|
+
Pending: 'bg-yellow-100 text-yellow-800',
|
|
242
|
+
Failed: 'bg-red-100 text-red-800',
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function DataTable() {
|
|
246
|
+
return (
|
|
247
|
+
<div className="overflow-x-auto">
|
|
248
|
+
<table className="w-full text-sm text-left">
|
|
249
|
+
<thead className="text-xs text-zinc-500 uppercase bg-zinc-50">
|
|
250
|
+
<tr>
|
|
251
|
+
<th className="px-6 py-3 font-medium">Customer</th>
|
|
252
|
+
<th className="px-6 py-3 font-medium">Amount</th>
|
|
253
|
+
<th className="px-6 py-3 font-medium">Status</th>
|
|
254
|
+
<th className="px-6 py-3 font-medium">Date</th>
|
|
255
|
+
</tr>
|
|
256
|
+
</thead>
|
|
257
|
+
<tbody className="divide-y divide-zinc-200">
|
|
258
|
+
{data.map((row) => (
|
|
259
|
+
<tr key={row.id} className="hover:bg-zinc-50 transition-colors">
|
|
260
|
+
<td className="px-6 py-4 font-medium text-zinc-900">
|
|
261
|
+
{row.customer}
|
|
262
|
+
</td>
|
|
263
|
+
<td className="px-6 py-4 text-zinc-600">
|
|
264
|
+
{row.amount}
|
|
265
|
+
</td>
|
|
266
|
+
<td className="px-6 py-4">
|
|
267
|
+
<span
|
|
268
|
+
className={cn(
|
|
269
|
+
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium',
|
|
270
|
+
statusStyles[row.status]
|
|
271
|
+
)}
|
|
272
|
+
>
|
|
273
|
+
{row.status}
|
|
274
|
+
</span>
|
|
275
|
+
</td>
|
|
276
|
+
<td className="px-6 py-4 text-zinc-500">
|
|
277
|
+
{row.date}
|
|
278
|
+
</td>
|
|
279
|
+
</tr>
|
|
280
|
+
))}
|
|
281
|
+
</tbody>
|
|
282
|
+
</table>
|
|
283
|
+
</div>
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### lib/utils.ts
|
|
289
|
+
```tsx
|
|
290
|
+
import { type ClassValue, clsx } from 'clsx'
|
|
291
|
+
import { twMerge } from 'tailwind-merge'
|
|
292
|
+
|
|
293
|
+
export function cn(...inputs: ClassValue[]) {
|
|
294
|
+
return twMerge(clsx(inputs))
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Why This Is Good
|
|
299
|
+
|
|
300
|
+
✅ **No AI Slop Patterns**
|
|
301
|
+
- No purple/blue gradients
|
|
302
|
+
- No centered everything
|
|
303
|
+
- No rainbow colors
|
|
304
|
+
- No oversized border radius
|
|
305
|
+
|
|
306
|
+
✅ **Vercel-Quality Design**
|
|
307
|
+
- Clean zinc color palette
|
|
308
|
+
- Proper typography hierarchy
|
|
309
|
+
- Consistent spacing
|
|
310
|
+
- Subtle borders and shadows
|
|
311
|
+
|
|
312
|
+
✅ **Production-Ready**
|
|
313
|
+
- Responsive design
|
|
314
|
+
- Proper semantic HTML
|
|
315
|
+
- Accessibility attributes
|
|
316
|
+
- TypeScript types
|
|
317
|
+
- Client components where needed
|
|
318
|
+
|
|
319
|
+
✅ **Dashboard Best Practices**
|
|
320
|
+
- Sidebar navigation
|
|
321
|
+
- Stats cards with trends
|
|
322
|
+
- Data table with status badges
|
|
323
|
+
- Proper header with actions
|