@fr0mpy/component-system 2.1.1 → 3.1.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 +86 -27
- package/bin/cli.js +155 -50
- package/bin/validate-compliance.js +447 -0
- package/bin/validate-config.js +175 -0
- package/bin/validate-harness.js +215 -0
- package/index.js +15 -0
- package/package.json +7 -4
- package/templates/agents/component-auditor.md +77 -0
- package/templates/agents/design-token-validator.md +72 -0
- package/templates/agents/harness-scaffolder.md +146 -0
- package/templates/agents/playwright-tester.md +437 -0
- package/templates/agents/style-inspector.md +81 -0
- package/templates/commands/component-harness.md +104 -10
- package/templates/commands/retheme.md +142 -0
- package/templates/commands/save-theme.md +50 -0
- package/templates/commands/setup-styling.md +386 -57
- package/templates/component-recipes/accordion.md +9 -9
- package/templates/component-recipes/alert.md +36 -23
- package/templates/component-recipes/avatar.md +14 -12
- package/templates/component-recipes/badge.md +9 -6
- package/templates/component-recipes/breadcrumb.md +4 -4
- package/templates/component-recipes/button.md +2 -2
- package/templates/component-recipes/checkbox.md +5 -5
- package/templates/component-recipes/collapsible.md +13 -13
- package/templates/component-recipes/combobox.md +2 -2
- package/templates/component-recipes/context-menu.md +11 -11
- package/templates/component-recipes/dialog.md +16 -16
- package/templates/component-recipes/drawer.md +18 -18
- package/templates/component-recipes/dropdown-menu.md +12 -12
- package/templates/component-recipes/hover-card.md +11 -11
- package/templates/component-recipes/label.md +4 -4
- package/templates/component-recipes/modal.md +9 -9
- package/templates/component-recipes/navigation-menu.md +19 -19
- package/templates/component-recipes/popover.md +10 -10
- package/templates/component-recipes/progress.md +10 -8
- package/templates/component-recipes/radio.md +6 -6
- package/templates/component-recipes/select.md +5 -5
- package/templates/component-recipes/separator.md +2 -2
- package/templates/component-recipes/slider.md +2 -2
- package/templates/component-recipes/switch.md +6 -6
- package/templates/component-recipes/table.md +1 -1
- package/templates/component-recipes/tabs.md +6 -6
- package/templates/component-recipes/toast.md +56 -42
- package/templates/component-recipes/toggle-group.md +4 -4
- package/templates/component-recipes/tooltip.md +5 -5
- package/templates/mcp/mcp.json +8 -0
- package/templates/playwright/playwright.config.ts +31 -0
- package/templates/playwright/tests/components.spec.ts +104 -0
- package/templates/skills/react-patterns.md +141 -0
- package/templates/skills/styling.md +141 -52
- package/templates/hooks/triggers.d/styling.json +0 -23
|
@@ -4,80 +4,279 @@ This command creates a personalized styling system. Once configured, the `stylin
|
|
|
4
4
|
|
|
5
5
|
## Workflow
|
|
6
6
|
|
|
7
|
-
1. **Check
|
|
8
|
-
2. **Ask aesthetic questions** using AskUserQuestion tool:
|
|
7
|
+
1. **Check existing progress** - Resume from where we left off:
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
|
|
9
|
+
```bash
|
|
10
|
+
npx validate-config
|
|
11
|
+
```
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
-
|
|
13
|
+
- If config valid → check recipes
|
|
14
|
+
- If config missing/invalid → start fresh from step 2
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
- If
|
|
16
|
+
For recipes, check each required file exists and has content (not empty):
|
|
17
|
+
- Required: button.md, card.md, input.md, badge.md, dialog.md
|
|
18
|
+
- If all exist with content → skip to step 8 (harness question)
|
|
19
|
+
- If some missing → generate only the missing ones in step 6
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
- Options: [Modern sans (Inter/system)] [Classic serif] [Monospace/technical] [Custom font family]
|
|
21
|
+
2. **Ask setup method** using AskUserQuestion tool:
|
|
23
22
|
|
|
24
|
-
**
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
**Q0: How do you want to define your styling?**
|
|
24
|
+
- Options:
|
|
25
|
+
- [Extract from URL (Recommended)] - Analyze an existing website's design system
|
|
26
|
+
- [Manual configuration] - Answer questions about colors, typography, etc.
|
|
27
|
+
|
|
28
|
+
### If "Extract from URL" selected:
|
|
29
|
+
|
|
30
|
+
Delegate to the `style-inspector` agent:
|
|
31
|
+
|
|
32
|
+
1. **Ask for URL**: "What website do you want to extract styling from?"
|
|
33
|
+
|
|
34
|
+
2. **Run the style-inspector agent** — This agent handles the full extraction workflow:
|
|
35
|
+
- Technology detection (Wappalyzer, CRFT Lookup, or browser script)
|
|
36
|
+
- Design token extraction (Dembrandt CLI → Superposition → browser script fallback)
|
|
37
|
+
- Semantic interpretation with confidence scoring
|
|
38
|
+
- Translation to styling-config.json format with user's aesthetic flair
|
|
39
|
+
|
|
40
|
+
3. **Review agent output** — The style-inspector returns structured findings with extracted tokens, detected patterns, and any gaps that need manual resolution.
|
|
41
|
+
|
|
42
|
+
4. **Generate themeName** from the source (e.g., "stripe-inspired", "vercel-dark")
|
|
43
|
+
|
|
44
|
+
5. **Generate config** with source attribution, then skip to step 5 (generate recipes).
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
### If "Manual configuration" selected:
|
|
49
|
+
|
|
50
|
+
3. **Ask styling questions** using AskUserQuestion tool:
|
|
51
|
+
|
|
52
|
+
**Q1: Describe your desired aesthetic** (Free text)
|
|
53
|
+
- "Describe the look and feel you want (e.g., 'dark terminal hacker vibe', 'soft pastels for kids app', 'clean minimal SaaS')"
|
|
54
|
+
|
|
55
|
+
**Q2: Typography Configuration**
|
|
56
|
+
|
|
57
|
+
Q2a: Heading font style
|
|
58
|
+
- Options: [Geometric sans (modern, clean)] [Humanist sans (friendly, approachable)] [Serif (editorial, classic)] [Monospace (technical, retro)] [Enter custom font name]
|
|
59
|
+
|
|
60
|
+
Q2b: Body font style
|
|
61
|
+
- Options: [System stack (fastest loading)] [Clean sans (readable)] [Match heading font] [Enter custom font name]
|
|
62
|
+
|
|
63
|
+
4. **Generate tokens from description**
|
|
64
|
+
|
|
65
|
+
Interpret the user's description and generate appropriate token values directly. Use these reference tables as guidance:
|
|
66
|
+
|
|
67
|
+
### Color Keyword Reference
|
|
68
|
+
|
|
69
|
+
| Keywords | Base Color |
|
|
70
|
+
|----------|------------|
|
|
71
|
+
| ocean, sky, clean, calm | #0EA5E9 |
|
|
72
|
+
| navy, corporate, trust | #1E3A8A |
|
|
73
|
+
| forest, nature | #166534 |
|
|
74
|
+
| emerald, fresh, growth | #10B981 |
|
|
75
|
+
| coral, warm, friendly | #F97316 |
|
|
76
|
+
| violet, creative, innovative | #8B5CF6 |
|
|
77
|
+
| rose, playful, bold | #F43F5E |
|
|
78
|
+
| slate, minimal, neutral | #64748B |
|
|
79
|
+
| professional, modern, tech | #3B82F6 |
|
|
80
|
+
| luxury, premium, elegant | #7C3AED |
|
|
81
|
+
| earthy, natural, organic | #78716C |
|
|
82
|
+
| teal, balanced, health | #14B8A6 |
|
|
83
|
+
| indigo, deep, focused | #6366F1 |
|
|
84
|
+
| amber, energy, attention | #F59E0B |
|
|
85
|
+
| terminal, hacker, matrix | #00FF41 |
|
|
86
|
+
| cyberpunk, neon | #FF00FF |
|
|
87
|
+
|
|
88
|
+
### Radii Reference
|
|
89
|
+
|
|
90
|
+
| Feel | sm | md | lg | xl | 2xl | default |
|
|
91
|
+
|------|-----|-----|-----|-----|------|---------|
|
|
92
|
+
| Sharp/pixel | 0 | 0 | 0 | 0 | 0 | none |
|
|
93
|
+
| Subtle | 0.125rem | 0.25rem | 0.375rem | 0.5rem | 0.75rem | sm |
|
|
94
|
+
| Rounded | 0.25rem | 0.375rem | 0.5rem | 0.75rem | 1rem | md |
|
|
95
|
+
| Pill/playful | 0.5rem | 0.75rem | 1rem | 1.5rem | 2rem | xl |
|
|
96
|
+
|
|
97
|
+
### Shadow Reference
|
|
98
|
+
|
|
99
|
+
| Feel | sm | md | lg | glow |
|
|
100
|
+
|------|-----|-----|-----|------|
|
|
101
|
+
| Flat | none | none | none | none |
|
|
102
|
+
| Subtle | 0 1px 2px 0 rgb(0 0 0 / 0.05) | 0 4px 6px -1px rgb(0 0 0 / 0.1) | 0 10px 15px -3px rgb(0 0 0 / 0.1) | 0 0 10px rgb(0 0 0 / 0.1) |
|
|
103
|
+
| Pronounced | 0 2px 4px rgb(0 0 0 / 0.15) | 0 8px 12px rgb(0 0 0 / 0.2) | 0 20px 30px rgb(0 0 0 / 0.25) | 0 0 20px rgb(0 0 0 / 0.3) |
|
|
104
|
+
|
|
105
|
+
### Typography Reference
|
|
106
|
+
|
|
107
|
+
**Heading Fonts:**
|
|
108
|
+
| Style | Font Stack |
|
|
109
|
+
|-------|------------|
|
|
110
|
+
| Geometric sans | `"Plus Jakarta Sans", "Outfit", sans-serif` |
|
|
111
|
+
| Humanist sans | `"Source Sans 3", "Nunito Sans", sans-serif` |
|
|
112
|
+
| Serif | `"Lora", "Merriweather", serif` |
|
|
113
|
+
| Monospace | `"JetBrains Mono", "Fira Code", monospace` |
|
|
114
|
+
| Terminal/retro | `"VT323", monospace` |
|
|
115
|
+
|
|
116
|
+
**Body Fonts:**
|
|
117
|
+
| Style | Font Stack |
|
|
118
|
+
|-------|------------|
|
|
119
|
+
| System stack | `ui-sans-serif, system-ui, -apple-system, sans-serif` |
|
|
120
|
+
| Clean sans | `"Inter var", "DM Sans", sans-serif` |
|
|
121
|
+
|
|
122
|
+
### Color Scale Generation
|
|
123
|
+
|
|
124
|
+
From the base color, generate a 50-950 scale by adjusting lightness:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
50: 97% lightness (very light tint)
|
|
128
|
+
100: 94% lightness
|
|
129
|
+
200: 86% lightness
|
|
130
|
+
300: 76% lightness
|
|
131
|
+
400: 62% lightness
|
|
132
|
+
500: Base color lightness
|
|
133
|
+
600: Base - 10% lightness
|
|
134
|
+
700: Base - 20% lightness
|
|
135
|
+
800: Base - 30% lightness
|
|
136
|
+
900: Base - 40% lightness
|
|
137
|
+
950: Base - 50% lightness (very dark shade)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Color Resolution
|
|
141
|
+
|
|
142
|
+
**Light theme** (light backgrounds):
|
|
143
|
+
- `background` → gray.50
|
|
144
|
+
- `foreground` → gray.900
|
|
145
|
+
- `primary` → primary.600
|
|
146
|
+
- `primary-foreground` → gray.50
|
|
147
|
+
- `surface` → gray.100
|
|
148
|
+
- `muted` → gray.200
|
|
149
|
+
- `muted-foreground` → gray.500
|
|
150
|
+
- `border` → gray.200
|
|
151
|
+
|
|
152
|
+
**Dark theme** (dark backgrounds):
|
|
153
|
+
- `background` → gray.950
|
|
154
|
+
- `foreground` → gray.50
|
|
155
|
+
- `primary` → primary.500
|
|
156
|
+
- `primary-foreground` → gray.950
|
|
157
|
+
- `surface` → gray.900
|
|
158
|
+
- `muted` → gray.800
|
|
159
|
+
- `muted-foreground` → gray.400
|
|
160
|
+
- `border` → gray.700
|
|
161
|
+
|
|
162
|
+
5. **Generate config and save theme**
|
|
163
|
+
|
|
164
|
+
Generate a `themeName` from the user's description (e.g., "ocean-minimal", "terminal-dark", "pastel-playful").
|
|
165
|
+
|
|
166
|
+
**Save to TWO locations:**
|
|
167
|
+
- `.claude/styling-config.json` - active config (full structure)
|
|
168
|
+
- `.claude/themes/{themeName}.json` - saved theme (for theme switcher)
|
|
169
|
+
|
|
170
|
+
Config structure:
|
|
28
171
|
|
|
29
|
-
3. **Generate `.claude/styling-config.json`** with structure:
|
|
30
172
|
```json
|
|
31
173
|
{
|
|
32
|
-
"
|
|
174
|
+
"themeName": "<generated-from-prompt>",
|
|
33
175
|
"tokens": {
|
|
34
176
|
"colors": {
|
|
35
|
-
"primary": "
|
|
36
|
-
"primary-foreground": "
|
|
37
|
-
"secondary": "
|
|
38
|
-
"secondary-foreground": "
|
|
39
|
-
"background": "
|
|
40
|
-
"foreground": "
|
|
41
|
-
"surface": "
|
|
42
|
-
"muted": "
|
|
43
|
-
"muted-foreground": "
|
|
44
|
-
"border": "
|
|
45
|
-
"destructive": "#
|
|
46
|
-
"destructive-foreground": "
|
|
177
|
+
"primary": "#...",
|
|
178
|
+
"primary-foreground": "#...",
|
|
179
|
+
"secondary": "#...",
|
|
180
|
+
"secondary-foreground": "#...",
|
|
181
|
+
"background": "#...",
|
|
182
|
+
"foreground": "#...",
|
|
183
|
+
"surface": "#...",
|
|
184
|
+
"muted": "#...",
|
|
185
|
+
"muted-foreground": "#...",
|
|
186
|
+
"border": "#...",
|
|
187
|
+
"destructive": "#dc2626",
|
|
188
|
+
"destructive-foreground": "#...",
|
|
189
|
+
"accent": "#...",
|
|
190
|
+
"accent-foreground": "#...",
|
|
191
|
+
"success": "#16a34a",
|
|
192
|
+
"success-foreground": "#...",
|
|
193
|
+
"warning": "#f59e0b",
|
|
194
|
+
"warning-foreground": "#..."
|
|
195
|
+
},
|
|
196
|
+
"radii": {
|
|
197
|
+
"none": "0",
|
|
198
|
+
"sm": "...",
|
|
199
|
+
"md": "...",
|
|
200
|
+
"lg": "...",
|
|
201
|
+
"xl": "...",
|
|
202
|
+
"2xl": "...",
|
|
203
|
+
"full": "9999px",
|
|
204
|
+
"default": "..."
|
|
205
|
+
},
|
|
206
|
+
"shadows": {
|
|
207
|
+
"none": "none",
|
|
208
|
+
"sm": "...",
|
|
209
|
+
"md": "...",
|
|
210
|
+
"lg": "...",
|
|
211
|
+
"xl": "...",
|
|
212
|
+
"glow": "..."
|
|
47
213
|
},
|
|
48
|
-
"radius": "rounded-lg",
|
|
49
|
-
"shadow": "shadow-sm",
|
|
50
214
|
"spacing": {
|
|
51
|
-
"tight": "
|
|
52
|
-
"normal": "
|
|
53
|
-
"loose": "
|
|
215
|
+
"tight": "1",
|
|
216
|
+
"normal": "2",
|
|
217
|
+
"loose": "4"
|
|
54
218
|
},
|
|
55
219
|
"typography": {
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
220
|
+
"families": {
|
|
221
|
+
"heading": { "name": "...", "stack": "...", "variable": "--font-heading" },
|
|
222
|
+
"body": { "name": "...", "stack": "...", "variable": "--font-body" },
|
|
223
|
+
"code": { "name": "JetBrains Mono", "stack": "'JetBrains Mono', ui-monospace, monospace", "variable": "--font-code" }
|
|
224
|
+
},
|
|
225
|
+
"scale": {
|
|
226
|
+
"xs": { "size": "0.75rem", "lineHeight": "1rem", "letterSpacing": "0.025em" },
|
|
227
|
+
"sm": { "size": "0.875rem", "lineHeight": "1.25rem", "letterSpacing": "0.015em" },
|
|
228
|
+
"base": { "size": "1rem", "lineHeight": "1.5rem", "letterSpacing": "0" },
|
|
229
|
+
"lg": { "size": "1.125rem", "lineHeight": "1.75rem", "letterSpacing": "-0.01em" },
|
|
230
|
+
"xl": { "size": "1.25rem", "lineHeight": "1.75rem", "letterSpacing": "-0.015em" },
|
|
231
|
+
"2xl": { "size": "1.5rem", "lineHeight": "2rem", "letterSpacing": "-0.02em" },
|
|
232
|
+
"3xl": { "size": "1.875rem", "lineHeight": "2.25rem", "letterSpacing": "-0.025em" },
|
|
233
|
+
"4xl": { "size": "2.25rem", "lineHeight": "2.5rem", "letterSpacing": "-0.03em" },
|
|
234
|
+
"5xl": { "size": "3rem", "lineHeight": "1", "letterSpacing": "-0.035em" },
|
|
235
|
+
"6xl": { "size": "3.75rem", "lineHeight": "1", "letterSpacing": "-0.04em" }
|
|
236
|
+
},
|
|
237
|
+
"weights": { "normal": "400", "medium": "500", "semibold": "600", "bold": "700" },
|
|
238
|
+
"tracking": { "tighter": "-0.05em", "tight": "-0.025em", "normal": "0", "wide": "0.025em", "wider": "0.05em", "widest": "0.1em" },
|
|
239
|
+
"leading": { "none": "1", "tight": "1.25", "snug": "1.375", "normal": "1.5", "relaxed": "1.625", "loose": "2" },
|
|
240
|
+
"googleFonts": { "url": "...", "preconnect": true },
|
|
241
|
+
"classes": {
|
|
242
|
+
"h1": "font-heading text-4xl font-bold tracking-tight",
|
|
243
|
+
"h2": "font-heading text-3xl font-semibold tracking-tight",
|
|
244
|
+
"h3": "font-heading text-2xl font-semibold",
|
|
245
|
+
"h4": "font-heading text-xl font-semibold",
|
|
246
|
+
"h5": "font-heading text-lg font-medium",
|
|
247
|
+
"h6": "font-heading text-base font-medium tracking-wide",
|
|
248
|
+
"body": "font-body text-base leading-relaxed",
|
|
249
|
+
"caption": "font-body text-xs text-muted-foreground tracking-wide",
|
|
250
|
+
"label": "font-body text-sm font-medium tracking-wide",
|
|
251
|
+
"code": "font-mono text-sm"
|
|
252
|
+
},
|
|
253
|
+
"accessibility": {
|
|
254
|
+
"minBodySize": "16px",
|
|
255
|
+
"minLineHeight": "1.5",
|
|
256
|
+
"preferredLineLength": "65ch"
|
|
257
|
+
}
|
|
59
258
|
}
|
|
60
|
-
},
|
|
61
|
-
"personality": {
|
|
62
|
-
"corners": "rounded",
|
|
63
|
-
"depth": "subtle-shadows",
|
|
64
|
-
"density": "comfortable"
|
|
65
259
|
}
|
|
66
260
|
}
|
|
67
261
|
```
|
|
68
262
|
|
|
69
|
-
|
|
263
|
+
6. **Generate `.claude/component-recipes/`** directory with recipes:
|
|
70
264
|
- button.md, card.md, input.md, modal.md, select.md
|
|
71
265
|
- checkbox.md, radio.md, badge.md, alert.md, avatar.md
|
|
72
266
|
- tooltip.md, table.md, tabs.md, accordion.md, dialog.md, toast.md
|
|
73
267
|
|
|
74
|
-
Each recipe
|
|
268
|
+
Each recipe uses semantic tokens (bg-primary, text-foreground, border-border, etc.)
|
|
269
|
+
|
|
270
|
+
7. **Validate the config**:
|
|
271
|
+
```bash
|
|
272
|
+
npx validate-config
|
|
273
|
+
```
|
|
274
|
+
If validation fails, fix the issues and re-run until it passes.
|
|
75
275
|
|
|
76
|
-
|
|
276
|
+
8. **Ask about component-harness**: "Want me to scaffold the visual component-harness for previewing?"
|
|
77
277
|
|
|
78
278
|
## Recipe Template
|
|
79
279
|
|
|
80
|
-
When generating each recipe, follow this structure:
|
|
81
280
|
```markdown
|
|
82
281
|
# [Component] Component Recipe
|
|
83
282
|
|
|
@@ -88,24 +287,154 @@ When generating each recipe, follow this structure:
|
|
|
88
287
|
- Required states (loading, disabled, etc.)
|
|
89
288
|
|
|
90
289
|
## Tailwind Classes
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
290
|
+
### Base
|
|
291
|
+
[classes using semantic tokens: bg-background, text-foreground, border-border]
|
|
292
|
+
|
|
293
|
+
### Variants
|
|
294
|
+
[variant-specific classes using semantic tokens]
|
|
295
|
+
|
|
296
|
+
### Sizes
|
|
297
|
+
[size-specific classes]
|
|
298
|
+
|
|
299
|
+
### States
|
|
300
|
+
[hover, focus, disabled using semantic tokens]
|
|
95
301
|
|
|
96
302
|
## Props Interface
|
|
97
|
-
|
|
98
|
-
- size: [list of sizes]
|
|
99
|
-
- [other props]
|
|
303
|
+
[TypeScript interface]
|
|
100
304
|
|
|
101
305
|
## Do
|
|
102
|
-
-
|
|
306
|
+
- Use semantic color tokens (bg-primary, not bg-blue-500)
|
|
307
|
+
- Include focus-visible states for accessibility
|
|
308
|
+
- Use cn() utility for class merging
|
|
103
309
|
|
|
104
310
|
## Don't
|
|
105
|
-
-
|
|
311
|
+
- Hardcode colors (#hex or rgb values)
|
|
312
|
+
- Skip hover/focus states
|
|
313
|
+
- Forget cursor-pointer on interactive elements
|
|
106
314
|
|
|
107
315
|
## Example
|
|
108
|
-
[code example]
|
|
316
|
+
[Complete code example using semantic tokens]
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## CSS Variables
|
|
320
|
+
|
|
321
|
+
The harness maps design tokens to CSS variables:
|
|
322
|
+
|
|
323
|
+
```css
|
|
324
|
+
:root {
|
|
325
|
+
/* Border Radius Scale */
|
|
326
|
+
--radius-none: 0;
|
|
327
|
+
--radius-sm: 0.125rem;
|
|
328
|
+
--radius-md: 0.375rem;
|
|
329
|
+
--radius-lg: 0.5rem;
|
|
330
|
+
--radius-xl: 0.75rem;
|
|
331
|
+
--radius-2xl: 1rem;
|
|
332
|
+
--radius-full: 9999px;
|
|
333
|
+
--radius: var(--radius-md); /* default radius */
|
|
334
|
+
|
|
335
|
+
/* Shadow/Elevation Scale */
|
|
336
|
+
--shadow-none: none;
|
|
337
|
+
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
338
|
+
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
|
|
339
|
+
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
|
|
340
|
+
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1);
|
|
341
|
+
--shadow-glow: 0 0 15px rgb(0 0 0 / 0.1);
|
|
342
|
+
|
|
343
|
+
/* Font Families */
|
|
344
|
+
--font-heading: "Plus Jakarta Sans", ui-sans-serif, system-ui, sans-serif;
|
|
345
|
+
--font-body: "Inter", ui-sans-serif, system-ui, sans-serif;
|
|
346
|
+
--font-code: "JetBrains Mono", ui-monospace, monospace;
|
|
347
|
+
|
|
348
|
+
/* Type Scale */
|
|
349
|
+
--text-xs: 0.75rem;
|
|
350
|
+
--text-sm: 0.875rem;
|
|
351
|
+
--text-base: 1rem;
|
|
352
|
+
--text-lg: 1.125rem;
|
|
353
|
+
--text-xl: 1.25rem;
|
|
354
|
+
--text-2xl: 1.5rem;
|
|
355
|
+
--text-3xl: 1.875rem;
|
|
356
|
+
--text-4xl: 2.25rem;
|
|
357
|
+
--text-5xl: 3rem;
|
|
358
|
+
--text-6xl: 3.75rem;
|
|
359
|
+
|
|
360
|
+
/* Semantic Colors (from tokens.colors) */
|
|
361
|
+
--color-background: #...;
|
|
362
|
+
--color-foreground: #...;
|
|
363
|
+
--color-primary: #...;
|
|
364
|
+
--color-primary-foreground: #...;
|
|
365
|
+
/* etc. */
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## Tailwind Config Extension
|
|
370
|
+
|
|
371
|
+
Add design tokens to `tailwind.config.js`:
|
|
372
|
+
|
|
373
|
+
```js
|
|
374
|
+
module.exports = {
|
|
375
|
+
theme: {
|
|
376
|
+
extend: {
|
|
377
|
+
borderRadius: {
|
|
378
|
+
none: 'var(--radius-none)',
|
|
379
|
+
sm: 'var(--radius-sm)',
|
|
380
|
+
DEFAULT: 'var(--radius)',
|
|
381
|
+
md: 'var(--radius-md)',
|
|
382
|
+
lg: 'var(--radius-lg)',
|
|
383
|
+
xl: 'var(--radius-xl)',
|
|
384
|
+
'2xl': 'var(--radius-2xl)',
|
|
385
|
+
full: 'var(--radius-full)',
|
|
386
|
+
},
|
|
387
|
+
boxShadow: {
|
|
388
|
+
none: 'var(--shadow-none)',
|
|
389
|
+
sm: 'var(--shadow-sm)',
|
|
390
|
+
DEFAULT: 'var(--shadow-md)',
|
|
391
|
+
md: 'var(--shadow-md)',
|
|
392
|
+
lg: 'var(--shadow-lg)',
|
|
393
|
+
xl: 'var(--shadow-xl)',
|
|
394
|
+
glow: 'var(--shadow-glow)',
|
|
395
|
+
},
|
|
396
|
+
colors: {
|
|
397
|
+
background: 'var(--color-background)',
|
|
398
|
+
foreground: 'var(--color-foreground)',
|
|
399
|
+
primary: {
|
|
400
|
+
DEFAULT: 'var(--color-primary)',
|
|
401
|
+
foreground: 'var(--color-primary-foreground)',
|
|
402
|
+
},
|
|
403
|
+
secondary: {
|
|
404
|
+
DEFAULT: 'var(--color-secondary)',
|
|
405
|
+
foreground: 'var(--color-secondary-foreground)',
|
|
406
|
+
},
|
|
407
|
+
muted: {
|
|
408
|
+
DEFAULT: 'var(--color-muted)',
|
|
409
|
+
foreground: 'var(--color-muted-foreground)',
|
|
410
|
+
},
|
|
411
|
+
accent: {
|
|
412
|
+
DEFAULT: 'var(--color-accent)',
|
|
413
|
+
foreground: 'var(--color-accent-foreground)',
|
|
414
|
+
},
|
|
415
|
+
destructive: {
|
|
416
|
+
DEFAULT: 'var(--color-destructive)',
|
|
417
|
+
foreground: 'var(--color-destructive-foreground)',
|
|
418
|
+
},
|
|
419
|
+
success: {
|
|
420
|
+
DEFAULT: 'var(--color-success)',
|
|
421
|
+
foreground: 'var(--color-success-foreground)',
|
|
422
|
+
},
|
|
423
|
+
warning: {
|
|
424
|
+
DEFAULT: 'var(--color-warning)',
|
|
425
|
+
foreground: 'var(--color-warning-foreground)',
|
|
426
|
+
},
|
|
427
|
+
border: 'var(--color-border)',
|
|
428
|
+
surface: 'var(--color-surface)',
|
|
429
|
+
},
|
|
430
|
+
fontFamily: {
|
|
431
|
+
heading: ['var(--font-heading)'],
|
|
432
|
+
body: ['var(--font-body)'],
|
|
433
|
+
mono: ['var(--font-code)'],
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
}
|
|
109
438
|
```
|
|
110
439
|
|
|
111
440
|
$ARGUMENTS
|
|
@@ -17,7 +17,7 @@ border-b border-border
|
|
|
17
17
|
```
|
|
18
18
|
flex flex-1 items-center justify-between py-4 text-sm font-medium
|
|
19
19
|
transition-all hover:underline
|
|
20
|
-
[&[data-
|
|
20
|
+
[&[data-open]>svg]:rotate-180
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
### Chevron Icon
|
|
@@ -28,8 +28,8 @@ h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200
|
|
|
28
28
|
### Content
|
|
29
29
|
```
|
|
30
30
|
overflow-hidden text-sm
|
|
31
|
-
data-
|
|
32
|
-
data-
|
|
31
|
+
data-closed:animate-accordion-up
|
|
32
|
+
data-open:animate-accordion-down
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
### Content Inner
|
|
@@ -42,10 +42,10 @@ pb-4 pt-0
|
|
|
42
42
|
keyframes: {
|
|
43
43
|
'accordion-down': {
|
|
44
44
|
from: { height: '0' },
|
|
45
|
-
to: { height: 'var(--
|
|
45
|
+
to: { height: 'var(--accordion-panel-height)' },
|
|
46
46
|
},
|
|
47
47
|
'accordion-up': {
|
|
48
|
-
from: { height: 'var(--
|
|
48
|
+
from: { height: 'var(--accordion-panel-height)' },
|
|
49
49
|
to: { height: '0' },
|
|
50
50
|
},
|
|
51
51
|
},
|
|
@@ -84,7 +84,7 @@ interface AccordionContentProps {
|
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
## Do
|
|
87
|
-
- Use
|
|
87
|
+
- Use Base UI Accordion for accessibility
|
|
88
88
|
- Animate height changes smoothly
|
|
89
89
|
- Rotate chevron on open
|
|
90
90
|
- Support both single and multiple modes
|
|
@@ -97,7 +97,7 @@ interface AccordionContentProps {
|
|
|
97
97
|
|
|
98
98
|
## Example
|
|
99
99
|
```tsx
|
|
100
|
-
import
|
|
100
|
+
import { Accordion as AccordionPrimitive } from '@base-ui/react/accordion'
|
|
101
101
|
import { ChevronDown } from 'lucide-react'
|
|
102
102
|
import { cn } from '@/lib/utils'
|
|
103
103
|
|
|
@@ -115,7 +115,7 @@ const AccordionTrigger = ({ className, children, ...props }) => (
|
|
|
115
115
|
<AccordionPrimitive.Trigger
|
|
116
116
|
className={cn(
|
|
117
117
|
'flex flex-1 items-center justify-between py-4 text-sm font-medium',
|
|
118
|
-
'transition-all hover:underline [&[data-
|
|
118
|
+
'transition-all hover:underline [&[data-open]>svg]:rotate-180',
|
|
119
119
|
className
|
|
120
120
|
)}
|
|
121
121
|
{...props}
|
|
@@ -128,7 +128,7 @@ const AccordionTrigger = ({ className, children, ...props }) => (
|
|
|
128
128
|
|
|
129
129
|
const AccordionContent = ({ className, children, ...props }) => (
|
|
130
130
|
<AccordionPrimitive.Content
|
|
131
|
-
className="overflow-hidden text-sm data-
|
|
131
|
+
className="overflow-hidden text-sm data-closed:animate-accordion-up data-open:animate-accordion-down"
|
|
132
132
|
{...props}
|
|
133
133
|
>
|
|
134
134
|
<div className={cn('pb-4 pt-0', className)}>{children}</div>
|