@champpaba/claude-agent-kit 2.5.0 → 2.7.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/.claude/CLAUDE.md +103 -58
- package/.claude/agents/02-uxui-frontend.md +5 -6
- package/.claude/agents/07-ux-tester.md +407 -0
- package/.claude/commands/cdev.md +113 -8
- package/.claude/commands/csetup.md +86 -28
- package/.claude/commands/designsetup.md +171 -48
- package/.claude/commands/extract.md +382 -668
- package/.claude/commands/pageplan.md +43 -27
- package/.claude/contexts/design/box-thinking.md +4 -4
- package/.claude/contexts/design/color-theory.md +5 -5
- package/.claude/contexts/design/index.md +9 -9
- package/.claude/contexts/design/spacing.md +4 -4
- package/.claude/contexts/patterns/agent-discovery.md +1 -1
- package/.claude/contexts/patterns/animation-patterns.md +3 -3
- package/.claude/contexts/patterns/ui-component-consistency.md +3 -3
- package/.claude/lib/README.md +1 -1
- package/.claude/lib/agent-executor.md +223 -0
- package/.claude/lib/context-loading-protocol.md +5 -6
- package/.claude/lib/detailed-guides/agent-system.md +4 -4
- package/.claude/lib/detailed-guides/best-practices-system.md +5 -4
- package/.claude/lib/detailed-guides/context-optimization.md +21 -22
- package/.claude/lib/detailed-guides/design-system.md +6 -5
- package/.claude/lib/detailed-guides/page-planning.md +6 -6
- package/.claude/lib/document-loader.md +32 -47
- package/.claude/lib/task-analyzer.md +194 -1
- package/.claude/templates/PROJECT_STATUS.template.yml +1 -1
- package/.claude/templates/STYLE_GUIDE.template.md +7 -7
- package/.claude/templates/design-context-template.md +22 -29
- package/.claude/templates/page-plan-example.md +9 -9
- package/.claude/templates/phases-sections/ux-testing.md +164 -0
- package/README.md +3 -1
- package/package.json +1 -1
|
@@ -1,91 +1,97 @@
|
|
|
1
|
-
# /extract - Extract Design
|
|
1
|
+
# /extract - Extract Design System from Website
|
|
2
2
|
|
|
3
3
|
You are an expert design systems engineer with deep knowledge of CSS, animations, and UX patterns.
|
|
4
4
|
|
|
5
|
-
Your task is to extract comprehensive design data from
|
|
5
|
+
Your task is to extract comprehensive design data from a website and save it as a complete YAML file with psychology analysis.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## 📖 Usage
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
/extract <URL>
|
|
12
|
+
/extract <URL>
|
|
13
13
|
|
|
14
14
|
Arguments:
|
|
15
|
-
URL Required.
|
|
15
|
+
URL Required. Website URL to extract from
|
|
16
16
|
|
|
17
17
|
Examples:
|
|
18
|
-
/extract https://
|
|
19
|
-
/extract https://linear.app
|
|
20
|
-
/extract https://
|
|
18
|
+
/extract https://airbnb.com
|
|
19
|
+
/extract https://linear.app
|
|
20
|
+
/extract https://stripe.com
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
**Multi-URL Support:**
|
|
24
|
-
- Extract from multiple sites → Merge into `merged-insights.json`
|
|
25
|
-
- Pick animations from ref1, colors from ref2, scroll effects from ref3
|
|
26
|
-
- `/designsetup` will let user choose what to use from each
|
|
27
|
-
|
|
28
23
|
---
|
|
29
24
|
|
|
30
25
|
## 🎯 Mission
|
|
31
26
|
|
|
32
|
-
Extract design
|
|
33
|
-
- `
|
|
34
|
-
- `
|
|
35
|
-
- `screenshots/{site-name}/` - Component screenshots
|
|
36
|
-
|
|
37
|
-
**Key Outputs for /designsetup:**
|
|
38
|
-
1. **Style Classification** (Neo-Brutalism, Minimalist, Glassmorphism, etc.)
|
|
39
|
-
2. **Animation Patterns** (button hover, scroll effects, GSAP detection)
|
|
40
|
-
3. **Decorative Elements** (blobs, gradients, 3D shapes, illustrations)
|
|
41
|
-
4. **Color Palette** (primary, secondary, accents)
|
|
42
|
-
5. **Typography** (fonts, weights, sizes)
|
|
27
|
+
Extract ALL design data from a website and save to `design-system/extracted/{site-name}/`:
|
|
28
|
+
- `data.yaml` - Complete 17-section design data + psychology analysis + animations
|
|
29
|
+
- `screenshots/` - Component screenshots (default + hover/focus states)
|
|
43
30
|
|
|
44
31
|
**Key Principles:**
|
|
45
|
-
1. **
|
|
46
|
-
2. **
|
|
47
|
-
3. **
|
|
48
|
-
4. **
|
|
32
|
+
1. **17 Sections Complete**: Colors with usage, typography with sizes, components with animations
|
|
33
|
+
2. **Psychology Included**: Target audience, emotions evoked, why it works
|
|
34
|
+
3. **Component-Level Detail**: Every component type with all states
|
|
35
|
+
4. **Animation Capture**: Before/after for all interactive states
|
|
49
36
|
|
|
50
37
|
---
|
|
51
38
|
|
|
52
39
|
## 🔍 STEP 0: Parse Input & Setup
|
|
53
40
|
|
|
54
41
|
```javascript
|
|
55
|
-
// Parse
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
58
|
-
return error('URL required. Usage: /extract https://
|
|
42
|
+
// Parse URL
|
|
43
|
+
const input = args[0];
|
|
44
|
+
if (!input) {
|
|
45
|
+
return error('URL required. Usage: /extract https://airbnb.com');
|
|
59
46
|
}
|
|
60
47
|
|
|
61
|
-
// Normalize
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
48
|
+
// Normalize URL
|
|
49
|
+
let url = input.trim();
|
|
50
|
+
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
51
|
+
url = 'https://' + url;
|
|
52
|
+
}
|
|
67
53
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
54
|
+
// Auto-detect site name
|
|
55
|
+
const siteName = new URL(url).hostname
|
|
56
|
+
.replace('www.', '')
|
|
57
|
+
.replace(/\.[^.]+$/, ''); // Remove TLD
|
|
58
|
+
|
|
59
|
+
// Check if already extracted
|
|
60
|
+
const extractedPath = `design-system/extracted/${siteName}`;
|
|
61
|
+
if (exists(extractedPath + '/data.yaml')) {
|
|
62
|
+
const existingData = YAML.parse(Read(extractedPath + '/data.yaml'));
|
|
63
|
+
const extractedDate = existingData.meta.extracted_at;
|
|
64
|
+
|
|
65
|
+
const response = await AskUserQuestion({
|
|
66
|
+
questions: [{
|
|
67
|
+
question: `Site "${siteName}" was already extracted on ${extractedDate}. Re-extract?`,
|
|
68
|
+
header: "Re-extract?",
|
|
69
|
+
multiSelect: false,
|
|
70
|
+
options: [
|
|
71
|
+
{ label: "Yes, re-extract", description: "Overwrite previous data" },
|
|
72
|
+
{ label: "No, cancel", description: "Keep existing data" }
|
|
73
|
+
]
|
|
74
|
+
}]
|
|
75
|
+
});
|
|
71
76
|
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
if (response.answers["Re-extract?"] === "No, cancel") {
|
|
78
|
+
return output('Extraction cancelled. Existing data preserved.');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
74
81
|
|
|
75
82
|
// Create directories
|
|
76
|
-
Bash: mkdir -p
|
|
83
|
+
Bash: mkdir -p design-system/extracted/${siteName}/screenshots
|
|
77
84
|
```
|
|
78
85
|
|
|
79
86
|
**Report:**
|
|
80
87
|
```
|
|
81
88
|
🚀 Extraction Started
|
|
82
89
|
|
|
83
|
-
📍
|
|
84
|
-
|
|
90
|
+
📍 URL: ${url}
|
|
91
|
+
📁 Site: ${siteName}
|
|
92
|
+
📂 Output: design-system/extracted/${siteName}/
|
|
85
93
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
⏳ Processing sites...
|
|
94
|
+
⏳ Navigating to site...
|
|
89
95
|
```
|
|
90
96
|
|
|
91
97
|
---
|
|
@@ -106,14 +112,13 @@ try {
|
|
|
106
112
|
.slice(0, 3);
|
|
107
113
|
|
|
108
114
|
if (headings.length > 0) {
|
|
109
|
-
const mainText = headings[0].split('"')[1];
|
|
115
|
+
const mainText = headings[0].split('"')[1];
|
|
110
116
|
|
|
111
117
|
await mcp__chrome-devtools__wait_for({
|
|
112
118
|
text: mainText,
|
|
113
119
|
timeout: 15000
|
|
114
120
|
});
|
|
115
121
|
} else {
|
|
116
|
-
// Fallback: fixed wait
|
|
117
122
|
await sleep(5000);
|
|
118
123
|
}
|
|
119
124
|
} catch {
|
|
@@ -167,30 +172,19 @@ const [
|
|
|
167
172
|
] = await Promise.all(extractionPromises);
|
|
168
173
|
```
|
|
169
174
|
|
|
170
|
-
### 2.1: Extract Colors
|
|
175
|
+
### 2.1: Extract Colors (with usage detection)
|
|
171
176
|
|
|
172
177
|
```javascript
|
|
173
178
|
async function extractColors() {
|
|
174
179
|
return await mcp__chrome-devtools__evaluate_script({
|
|
175
180
|
function: `() => {
|
|
176
181
|
const allElements = document.querySelectorAll('*');
|
|
177
|
-
const
|
|
178
|
-
backgrounds: new
|
|
179
|
-
texts: new
|
|
180
|
-
borders: new
|
|
182
|
+
const colorMap = {
|
|
183
|
+
backgrounds: new Map(),
|
|
184
|
+
texts: new Map(),
|
|
185
|
+
borders: new Map()
|
|
181
186
|
};
|
|
182
187
|
|
|
183
|
-
allElements.forEach(el => {
|
|
184
|
-
const s = window.getComputedStyle(el);
|
|
185
|
-
if (s.backgroundColor && s.backgroundColor !== 'rgba(0, 0, 0, 0)') {
|
|
186
|
-
colors.backgrounds.add(s.backgroundColor);
|
|
187
|
-
}
|
|
188
|
-
if (s.color) colors.texts.add(s.color);
|
|
189
|
-
if (s.borderColor && s.borderColor !== 'rgba(0, 0, 0, 0)') {
|
|
190
|
-
colors.borders.add(s.borderColor);
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
|
|
194
188
|
// Convert RGB to HEX
|
|
195
189
|
const rgbToHex = (rgb) => {
|
|
196
190
|
const match = rgb.match(/\\d+/g);
|
|
@@ -202,19 +196,92 @@ async function extractColors() {
|
|
|
202
196
|
return '#' + hex.join('').toUpperCase();
|
|
203
197
|
};
|
|
204
198
|
|
|
199
|
+
// Detect usage based on element context
|
|
200
|
+
const detectUsage = (el, type) => {
|
|
201
|
+
const tag = el.tagName.toLowerCase();
|
|
202
|
+
const classes = el.className || '';
|
|
203
|
+
|
|
204
|
+
if (type === 'background') {
|
|
205
|
+
if (tag === 'button' || classes.includes('btn') || classes.includes('button')) return 'button-bg';
|
|
206
|
+
if (tag === 'nav' || classes.includes('nav') || classes.includes('header')) return 'nav-bg';
|
|
207
|
+
if (classes.includes('card') || classes.includes('box')) return 'card-bg';
|
|
208
|
+
if (classes.includes('hero') || classes.includes('banner')) return 'hero-bg';
|
|
209
|
+
if (tag === 'body' || classes.includes('main')) return 'page-bg';
|
|
210
|
+
return 'surface';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (type === 'text') {
|
|
214
|
+
if (tag.match(/^h[1-6]$/)) return 'heading';
|
|
215
|
+
if (tag === 'a') return 'link';
|
|
216
|
+
if (tag === 'button') return 'button-text';
|
|
217
|
+
if (classes.includes('muted') || classes.includes('secondary')) return 'muted-text';
|
|
218
|
+
return 'body-text';
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (type === 'border') {
|
|
222
|
+
if (tag === 'input' || tag === 'textarea') return 'input-border';
|
|
223
|
+
if (classes.includes('card')) return 'card-border';
|
|
224
|
+
return 'divider';
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return 'general';
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
allElements.forEach(el => {
|
|
231
|
+
const s = window.getComputedStyle(el);
|
|
232
|
+
|
|
233
|
+
if (s.backgroundColor && s.backgroundColor !== 'rgba(0, 0, 0, 0)') {
|
|
234
|
+
const hex = rgbToHex(s.backgroundColor);
|
|
235
|
+
if (!colorMap.backgrounds.has(hex)) {
|
|
236
|
+
colorMap.backgrounds.set(hex, {
|
|
237
|
+
rgb: s.backgroundColor,
|
|
238
|
+
hex: hex,
|
|
239
|
+
usage: detectUsage(el, 'background'),
|
|
240
|
+
count: 1
|
|
241
|
+
});
|
|
242
|
+
} else {
|
|
243
|
+
colorMap.backgrounds.get(hex).count++;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (s.color) {
|
|
248
|
+
const hex = rgbToHex(s.color);
|
|
249
|
+
if (!colorMap.texts.has(hex)) {
|
|
250
|
+
colorMap.texts.set(hex, {
|
|
251
|
+
rgb: s.color,
|
|
252
|
+
hex: hex,
|
|
253
|
+
usage: detectUsage(el, 'text'),
|
|
254
|
+
count: 1
|
|
255
|
+
});
|
|
256
|
+
} else {
|
|
257
|
+
colorMap.texts.get(hex).count++;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (s.borderColor && s.borderColor !== 'rgba(0, 0, 0, 0)') {
|
|
262
|
+
const hex = rgbToHex(s.borderColor);
|
|
263
|
+
if (!colorMap.borders.has(hex)) {
|
|
264
|
+
colorMap.borders.set(hex, {
|
|
265
|
+
rgb: s.borderColor,
|
|
266
|
+
hex: hex,
|
|
267
|
+
usage: detectUsage(el, 'border'),
|
|
268
|
+
count: 1
|
|
269
|
+
});
|
|
270
|
+
} else {
|
|
271
|
+
colorMap.borders.get(hex).count++;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Sort by count (most used first)
|
|
277
|
+
const sortByCount = (map) => Array.from(map.values())
|
|
278
|
+
.sort((a, b) => b.count - a.count)
|
|
279
|
+
.slice(0, 20);
|
|
280
|
+
|
|
205
281
|
return {
|
|
206
|
-
backgrounds:
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
})),
|
|
210
|
-
texts: Array.from(colors.texts).slice(0, 20).map(c => ({
|
|
211
|
-
rgb: c,
|
|
212
|
-
hex: rgbToHex(c)
|
|
213
|
-
})),
|
|
214
|
-
borders: Array.from(colors.borders).slice(0, 15).map(c => ({
|
|
215
|
-
rgb: c,
|
|
216
|
-
hex: rgbToHex(c)
|
|
217
|
-
}))
|
|
282
|
+
backgrounds: sortByCount(colorMap.backgrounds),
|
|
283
|
+
texts: sortByCount(colorMap.texts),
|
|
284
|
+
borders: sortByCount(colorMap.borders)
|
|
218
285
|
};
|
|
219
286
|
}`
|
|
220
287
|
});
|
|
@@ -337,20 +404,17 @@ async function extractSpacing() {
|
|
|
337
404
|
Array.from(document.querySelectorAll('*')).slice(0, 100).forEach(el => {
|
|
338
405
|
const s = window.getComputedStyle(el);
|
|
339
406
|
|
|
340
|
-
// Padding
|
|
341
407
|
if (s.padding && s.padding !== '0px') {
|
|
342
408
|
[s.padding, s.paddingTop, s.paddingRight, s.paddingBottom, s.paddingLeft]
|
|
343
409
|
.forEach(v => spacing.paddings.add(v));
|
|
344
410
|
}
|
|
345
411
|
|
|
346
|
-
// Margin
|
|
347
412
|
if (s.margin && s.margin !== '0px') {
|
|
348
413
|
[s.marginTop, s.marginBottom].forEach(v => {
|
|
349
414
|
if (v && v !== '0px' && v !== 'auto') spacing.margins.add(v);
|
|
350
415
|
});
|
|
351
416
|
}
|
|
352
417
|
|
|
353
|
-
// Gap
|
|
354
418
|
if (s.gap && s.gap !== 'normal' && s.gap !== '0px') {
|
|
355
419
|
spacing.gaps.add(s.gap);
|
|
356
420
|
}
|
|
@@ -366,7 +430,6 @@ async function extractSpacing() {
|
|
|
366
430
|
.filter(v => !isNaN(v) && v > 0)
|
|
367
431
|
.sort((a, b) => a - b);
|
|
368
432
|
|
|
369
|
-
// Find GCD (grid base)
|
|
370
433
|
const gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
|
|
371
434
|
const gridBase = allValues.length > 1
|
|
372
435
|
? allValues.reduce((acc, val) => gcd(acc, val), allValues[0])
|
|
@@ -401,18 +464,10 @@ async function extractButtons() {
|
|
|
401
464
|
backgroundColor: s.backgroundColor,
|
|
402
465
|
color: s.color,
|
|
403
466
|
padding: s.padding,
|
|
404
|
-
paddingTop: s.paddingTop,
|
|
405
|
-
paddingRight: s.paddingRight,
|
|
406
|
-
paddingBottom: s.paddingBottom,
|
|
407
|
-
paddingLeft: s.paddingLeft,
|
|
408
467
|
border: s.border,
|
|
409
|
-
borderWidth: s.borderWidth,
|
|
410
|
-
borderColor: s.borderColor,
|
|
411
468
|
borderRadius: s.borderRadius,
|
|
412
469
|
fontSize: s.fontSize,
|
|
413
470
|
fontWeight: s.fontWeight,
|
|
414
|
-
textTransform: s.textTransform,
|
|
415
|
-
letterSpacing: s.letterSpacing,
|
|
416
471
|
boxShadow: s.boxShadow,
|
|
417
472
|
transition: s.transition
|
|
418
473
|
};
|
|
@@ -475,7 +530,7 @@ async function extractInputs() {
|
|
|
475
530
|
}
|
|
476
531
|
```
|
|
477
532
|
|
|
478
|
-
### 2.8: Extract Animations
|
|
533
|
+
### 2.8: Extract Animations
|
|
479
534
|
|
|
480
535
|
```javascript
|
|
481
536
|
async function extractAnimations() {
|
|
@@ -508,8 +563,7 @@ async function extractAnimations() {
|
|
|
508
563
|
selector: el.className || el.tagName,
|
|
509
564
|
transition: s.transition,
|
|
510
565
|
transitionDuration: s.transitionDuration,
|
|
511
|
-
transitionTimingFunction: s.transitionTimingFunction
|
|
512
|
-
transitionProperty: s.transitionProperty
|
|
566
|
+
transitionTimingFunction: s.transitionTimingFunction
|
|
513
567
|
});
|
|
514
568
|
}
|
|
515
569
|
});
|
|
@@ -525,289 +579,14 @@ async function extractAnimations() {
|
|
|
525
579
|
✅ CSS Data Extracted!
|
|
526
580
|
|
|
527
581
|
📊 Summary:
|
|
528
|
-
- Colors: ${colors.backgrounds.length} backgrounds, ${colors.texts.length} texts
|
|
582
|
+
- Colors: ${colors.backgrounds.length} backgrounds, ${colors.texts.length} texts
|
|
529
583
|
- Typography: ${typography.allFonts.length} fonts, ${typography.allWeights.length} weights
|
|
530
584
|
- Shadows: ${shadows.shadows.length} unique values
|
|
531
585
|
- Spacing: ${spacing.detectedGrid}px grid detected
|
|
532
586
|
- Buttons: ${buttons.length} extracted
|
|
533
587
|
- Cards: ${cards.length} extracted
|
|
534
588
|
- Inputs: ${inputs.length} extracted
|
|
535
|
-
- Animations: ${animations.keyframes.length} @keyframes
|
|
536
|
-
|
|
537
|
-
🔄 Detecting style, scroll animations, and decorative elements...
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
---
|
|
541
|
-
|
|
542
|
-
## STEP 2.5: Detect Design Style & Animation Libraries (NEW!)
|
|
543
|
-
|
|
544
|
-
```javascript
|
|
545
|
-
async function detectStyleAndAnimations() {
|
|
546
|
-
return await mcp__chrome-devtools__evaluate_script({
|
|
547
|
-
function: `() => {
|
|
548
|
-
const result = {
|
|
549
|
-
style: { detected: null, confidence: 0, characteristics: [] },
|
|
550
|
-
animationLibraries: [],
|
|
551
|
-
scrollAnimations: [],
|
|
552
|
-
decorativeElements: []
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
// ========== DETECT ANIMATION LIBRARIES ==========
|
|
556
|
-
|
|
557
|
-
// Check for GSAP
|
|
558
|
-
if (window.gsap || window.TweenMax || window.TweenLite) {
|
|
559
|
-
result.animationLibraries.push({
|
|
560
|
-
name: 'GSAP',
|
|
561
|
-
version: window.gsap?.version || 'unknown',
|
|
562
|
-
detected: true
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
// Check for ScrollTrigger
|
|
567
|
-
if (window.ScrollTrigger) {
|
|
568
|
-
result.animationLibraries.push({
|
|
569
|
-
name: 'ScrollTrigger',
|
|
570
|
-
version: window.ScrollTrigger?.version || 'unknown',
|
|
571
|
-
detected: true,
|
|
572
|
-
instances: document.querySelectorAll('[data-scroll], [data-gsap]').length
|
|
573
|
-
});
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// Check for Framer Motion (React)
|
|
577
|
-
if (document.querySelector('[data-framer-appear-id], [data-projection-id]')) {
|
|
578
|
-
result.animationLibraries.push({
|
|
579
|
-
name: 'Framer Motion',
|
|
580
|
-
detected: true
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
// Check for Lottie
|
|
585
|
-
if (window.lottie || document.querySelector('lottie-player, [data-lottie]')) {
|
|
586
|
-
result.animationLibraries.push({
|
|
587
|
-
name: 'Lottie',
|
|
588
|
-
detected: true
|
|
589
|
-
});
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
// Check for AOS (Animate on Scroll)
|
|
593
|
-
if (window.AOS || document.querySelector('[data-aos]')) {
|
|
594
|
-
result.animationLibraries.push({
|
|
595
|
-
name: 'AOS',
|
|
596
|
-
detected: true,
|
|
597
|
-
instances: document.querySelectorAll('[data-aos]').length
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// ========== DETECT SCROLL ANIMATIONS ==========
|
|
602
|
-
|
|
603
|
-
// Find elements with scroll-triggered classes or attributes
|
|
604
|
-
const scrollElements = document.querySelectorAll(
|
|
605
|
-
'[data-scroll], [data-aos], [data-gsap], [data-animate], ' +
|
|
606
|
-
'[class*="scroll-"], [class*="animate-on-scroll"], ' +
|
|
607
|
-
'[class*="fade-in"], [class*="slide-"]'
|
|
608
|
-
);
|
|
609
|
-
|
|
610
|
-
scrollElements.forEach(el => {
|
|
611
|
-
const classes = el.className;
|
|
612
|
-
const dataAttrs = Object.keys(el.dataset || {});
|
|
613
|
-
|
|
614
|
-
result.scrollAnimations.push({
|
|
615
|
-
type: detectScrollAnimationType(classes, dataAttrs),
|
|
616
|
-
trigger: el.dataset.aos || el.dataset.scroll || 'scroll',
|
|
617
|
-
element: el.tagName.toLowerCase() + (el.className ? '.' + el.className.split(' ')[0] : '')
|
|
618
|
-
});
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
// Detect sticky/pinned sections (common in GSAP sites)
|
|
622
|
-
document.querySelectorAll('[class*="sticky"], [class*="pinned"], [style*="position: sticky"]')
|
|
623
|
-
.forEach(el => {
|
|
624
|
-
result.scrollAnimations.push({
|
|
625
|
-
type: 'sticky-section',
|
|
626
|
-
element: el.tagName.toLowerCase()
|
|
627
|
-
});
|
|
628
|
-
});
|
|
629
|
-
|
|
630
|
-
function detectScrollAnimationType(classes, dataAttrs) {
|
|
631
|
-
if (classes.includes('stack') || dataAttrs.includes('stack')) return 'stacking-cards';
|
|
632
|
-
if (classes.includes('parallax') || dataAttrs.includes('parallax')) return 'parallax';
|
|
633
|
-
if (classes.includes('fade')) return 'fade-in';
|
|
634
|
-
if (classes.includes('slide')) return 'slide-in';
|
|
635
|
-
if (classes.includes('scale')) return 'scale-in';
|
|
636
|
-
return 'custom-scroll';
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// ========== DETECT DECORATIVE ELEMENTS ==========
|
|
640
|
-
|
|
641
|
-
const svgs = document.querySelectorAll('svg:not([class*="icon"]):not([width="24"]):not([width="16"])');
|
|
642
|
-
svgs.forEach(svg => {
|
|
643
|
-
const viewBox = svg.getAttribute('viewBox');
|
|
644
|
-
const width = parseInt(svg.getAttribute('width') || '0');
|
|
645
|
-
const height = parseInt(svg.getAttribute('height') || '0');
|
|
646
|
-
|
|
647
|
-
// Large SVGs are likely decorative
|
|
648
|
-
if (width > 100 || height > 100 || (viewBox && parseInt(viewBox.split(' ')[2]) > 100)) {
|
|
649
|
-
const paths = svg.querySelectorAll('path, circle, ellipse');
|
|
650
|
-
const isBlob = Array.from(paths).some(p => {
|
|
651
|
-
const d = p.getAttribute('d') || '';
|
|
652
|
-
return d.includes('C') && d.length > 200; // Curved paths = likely blob
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
result.decorativeElements.push({
|
|
656
|
-
type: isBlob ? 'blob' : 'svg-decoration',
|
|
657
|
-
size: { width, height },
|
|
658
|
-
colors: extractSvgColors(svg)
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
});
|
|
662
|
-
|
|
663
|
-
// Detect gradient backgrounds
|
|
664
|
-
document.querySelectorAll('*').forEach(el => {
|
|
665
|
-
const bg = window.getComputedStyle(el).backgroundImage;
|
|
666
|
-
if (bg && bg.includes('gradient')) {
|
|
667
|
-
result.decorativeElements.push({
|
|
668
|
-
type: 'gradient',
|
|
669
|
-
value: bg.substring(0, 100) + '...'
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
});
|
|
673
|
-
|
|
674
|
-
// Detect 3D elements
|
|
675
|
-
document.querySelectorAll('[class*="3d"], [style*="perspective"], [style*="rotateX"], [style*="rotateY"]')
|
|
676
|
-
.forEach(el => {
|
|
677
|
-
result.decorativeElements.push({
|
|
678
|
-
type: '3d-element',
|
|
679
|
-
element: el.tagName.toLowerCase()
|
|
680
|
-
});
|
|
681
|
-
});
|
|
682
|
-
|
|
683
|
-
function extractSvgColors(svg) {
|
|
684
|
-
const colors = new Set();
|
|
685
|
-
svg.querySelectorAll('[fill], [stroke]').forEach(el => {
|
|
686
|
-
const fill = el.getAttribute('fill');
|
|
687
|
-
const stroke = el.getAttribute('stroke');
|
|
688
|
-
if (fill && fill !== 'none') colors.add(fill);
|
|
689
|
-
if (stroke && stroke !== 'none') colors.add(stroke);
|
|
690
|
-
});
|
|
691
|
-
return Array.from(colors).slice(0, 5);
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// ========== DETECT DESIGN STYLE ==========
|
|
695
|
-
|
|
696
|
-
const allElements = document.querySelectorAll('*');
|
|
697
|
-
const styleIndicators = {
|
|
698
|
-
neoBrutalism: 0,
|
|
699
|
-
minimalist: 0,
|
|
700
|
-
glassmorphism: 0,
|
|
701
|
-
neumorphism: 0,
|
|
702
|
-
modernSaas: 0,
|
|
703
|
-
playful: 0
|
|
704
|
-
};
|
|
705
|
-
|
|
706
|
-
allElements.forEach(el => {
|
|
707
|
-
const s = window.getComputedStyle(el);
|
|
708
|
-
|
|
709
|
-
// Neo-Brutalism indicators
|
|
710
|
-
if (s.borderWidth && parseInt(s.borderWidth) >= 2) styleIndicators.neoBrutalism += 2;
|
|
711
|
-
if (s.boxShadow && s.boxShadow.includes('0px 0px 0px') || !s.boxShadow.includes('blur')) {
|
|
712
|
-
styleIndicators.neoBrutalism += 1; // Solid shadows
|
|
713
|
-
}
|
|
714
|
-
if (s.borderRadius && parseInt(s.borderRadius) >= 8 && parseInt(s.borderRadius) <= 16) {
|
|
715
|
-
styleIndicators.neoBrutalism += 1;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
// Minimalist indicators
|
|
719
|
-
if (s.borderWidth === '0px' || s.borderWidth === '1px') styleIndicators.minimalist += 0.5;
|
|
720
|
-
if (!s.boxShadow || s.boxShadow === 'none') styleIndicators.minimalist += 0.5;
|
|
721
|
-
|
|
722
|
-
// Glassmorphism indicators
|
|
723
|
-
if (s.backdropFilter && s.backdropFilter !== 'none') styleIndicators.glassmorphism += 3;
|
|
724
|
-
if (s.backgroundColor && s.backgroundColor.includes('rgba') && parseFloat(s.backgroundColor.split(',')[3]) < 0.8) {
|
|
725
|
-
styleIndicators.glassmorphism += 1;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
// Neumorphism indicators
|
|
729
|
-
if (s.boxShadow && s.boxShadow.includes('inset')) styleIndicators.neumorphism += 2;
|
|
730
|
-
|
|
731
|
-
// Modern SaaS indicators
|
|
732
|
-
if (s.boxShadow && s.boxShadow.includes('rgba') && s.boxShadow.includes('blur')) {
|
|
733
|
-
styleIndicators.modernSaas += 1;
|
|
734
|
-
}
|
|
735
|
-
if (parseInt(s.borderRadius) >= 12) styleIndicators.modernSaas += 0.5;
|
|
736
|
-
|
|
737
|
-
// Playful indicators
|
|
738
|
-
if (s.borderRadius && parseInt(s.borderRadius) >= 24) styleIndicators.playful += 1;
|
|
739
|
-
if (s.transform && s.transform !== 'none') styleIndicators.playful += 0.5;
|
|
740
|
-
});
|
|
741
|
-
|
|
742
|
-
// Determine style
|
|
743
|
-
const maxStyle = Object.entries(styleIndicators)
|
|
744
|
-
.sort((a, b) => b[1] - a[1])[0];
|
|
745
|
-
|
|
746
|
-
const styleDescriptions = {
|
|
747
|
-
neoBrutalism: {
|
|
748
|
-
name: 'Neo-Brutalism',
|
|
749
|
-
characteristics: ['Bold borders (2-4px)', 'Solid shadows (no blur)', 'High contrast', 'Chunky rounded corners'],
|
|
750
|
-
feel: 'Bold, energetic, playful'
|
|
751
|
-
},
|
|
752
|
-
minimalist: {
|
|
753
|
-
name: 'Minimalist',
|
|
754
|
-
characteristics: ['Clean lines', 'Subtle or no shadows', 'Lots of whitespace', 'Simple typography'],
|
|
755
|
-
feel: 'Clean, professional, trustworthy'
|
|
756
|
-
},
|
|
757
|
-
glassmorphism: {
|
|
758
|
-
name: 'Glassmorphism',
|
|
759
|
-
characteristics: ['Frosted glass effect', 'Backdrop blur', 'Semi-transparent backgrounds', 'Layered depth'],
|
|
760
|
-
feel: 'Modern, premium, tech-forward'
|
|
761
|
-
},
|
|
762
|
-
neumorphism: {
|
|
763
|
-
name: 'Neumorphism',
|
|
764
|
-
characteristics: ['Soft inset shadows', 'Extruded elements', 'Monochromatic palette'],
|
|
765
|
-
feel: 'Soft, touchable, futuristic'
|
|
766
|
-
},
|
|
767
|
-
modernSaas: {
|
|
768
|
-
name: 'Modern SaaS',
|
|
769
|
-
characteristics: ['Soft shadows with blur', 'Rounded corners', 'Gradient accents', 'Card-based layout'],
|
|
770
|
-
feel: 'Professional, friendly, reliable'
|
|
771
|
-
},
|
|
772
|
-
playful: {
|
|
773
|
-
name: 'Playful/Creative',
|
|
774
|
-
characteristics: ['Large border radius', 'Animations', 'Bright colors', 'Asymmetric layouts'],
|
|
775
|
-
feel: 'Fun, creative, approachable'
|
|
776
|
-
}
|
|
777
|
-
};
|
|
778
|
-
|
|
779
|
-
result.style = {
|
|
780
|
-
detected: styleDescriptions[maxStyle[0]].name,
|
|
781
|
-
confidence: Math.min(Math.round((maxStyle[1] / 50) * 100), 100),
|
|
782
|
-
characteristics: styleDescriptions[maxStyle[0]].characteristics,
|
|
783
|
-
feel: styleDescriptions[maxStyle[0]].feel,
|
|
784
|
-
scores: styleIndicators
|
|
785
|
-
};
|
|
786
|
-
|
|
787
|
-
return result;
|
|
788
|
-
}`
|
|
789
|
-
});
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
const styleData = await detectStyleAndAnimations();
|
|
793
|
-
```
|
|
794
|
-
|
|
795
|
-
**Report:**
|
|
796
|
-
```
|
|
797
|
-
✅ Style & Animations Detected!
|
|
798
|
-
|
|
799
|
-
🎨 Design Style: ${styleData.style.detected} (${styleData.style.confidence}% confidence)
|
|
800
|
-
Characteristics: ${styleData.style.characteristics.join(', ')}
|
|
801
|
-
Feel: ${styleData.style.feel}
|
|
802
|
-
|
|
803
|
-
📚 Animation Libraries:
|
|
804
|
-
${styleData.animationLibraries.map(lib => ` ✅ ${lib.name} ${lib.version || ''}`).join('\n') || ' (none detected)'}
|
|
805
|
-
|
|
806
|
-
🎬 Scroll Animations: ${styleData.scrollAnimations.length} detected
|
|
807
|
-
${styleData.scrollAnimations.slice(0, 5).map(a => ` - ${a.type}`).join('\n')}
|
|
808
|
-
|
|
809
|
-
🖼️ Decorative Elements: ${styleData.decorativeElements.length} found
|
|
810
|
-
${styleData.decorativeElements.slice(0, 5).map(d => ` - ${d.type}`).join('\n')}
|
|
589
|
+
- Animations: ${animations.keyframes.length} @keyframes
|
|
811
590
|
|
|
812
591
|
🔄 Extracting component animations (hover/focus states)...
|
|
813
592
|
```
|
|
@@ -825,7 +604,7 @@ const componentAnimations = {};
|
|
|
825
604
|
for (let i = 0; i < Math.min(buttons.length, 3); i++) {
|
|
826
605
|
const btnId = `button-${i}`;
|
|
827
606
|
|
|
828
|
-
//
|
|
607
|
+
// Scroll into view
|
|
829
608
|
await mcp__chrome-devtools__evaluate_script({
|
|
830
609
|
function: `() => {
|
|
831
610
|
const el = document.querySelector('[data-extract-id="${btnId}"]');
|
|
@@ -834,6 +613,7 @@ for (let i = 0; i < Math.min(buttons.length, 3); i++) {
|
|
|
834
613
|
});
|
|
835
614
|
await sleep(500);
|
|
836
615
|
|
|
616
|
+
// Screenshot: Default state
|
|
837
617
|
await mcp__chrome-devtools__take_screenshot({
|
|
838
618
|
filePath: `design-system/extracted/${siteName}/screenshots/${btnId}-default.png`
|
|
839
619
|
});
|
|
@@ -847,11 +627,8 @@ for (let i = 0; i < Math.min(buttons.length, 3); i++) {
|
|
|
847
627
|
return {
|
|
848
628
|
background: s.backgroundColor,
|
|
849
629
|
color: s.color,
|
|
850
|
-
border: s.border,
|
|
851
|
-
borderRadius: s.borderRadius,
|
|
852
630
|
boxShadow: s.boxShadow,
|
|
853
|
-
transform: s.transform
|
|
854
|
-
opacity: s.opacity
|
|
631
|
+
transform: s.transform
|
|
855
632
|
};
|
|
856
633
|
}`
|
|
857
634
|
});
|
|
@@ -863,7 +640,7 @@ for (let i = 0; i < Math.min(buttons.length, 3); i++) {
|
|
|
863
640
|
if (el) el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
|
|
864
641
|
}`
|
|
865
642
|
});
|
|
866
|
-
await sleep(500);
|
|
643
|
+
await sleep(500);
|
|
867
644
|
|
|
868
645
|
// Screenshot: Hover state
|
|
869
646
|
await mcp__chrome-devtools__take_screenshot({
|
|
@@ -879,11 +656,8 @@ for (let i = 0; i < Math.min(buttons.length, 3); i++) {
|
|
|
879
656
|
return {
|
|
880
657
|
background: s.backgroundColor,
|
|
881
658
|
color: s.color,
|
|
882
|
-
border: s.border,
|
|
883
|
-
borderRadius: s.borderRadius,
|
|
884
659
|
boxShadow: s.boxShadow,
|
|
885
|
-
transform: s.transform
|
|
886
|
-
opacity: s.opacity
|
|
660
|
+
transform: s.transform
|
|
887
661
|
};
|
|
888
662
|
}`
|
|
889
663
|
});
|
|
@@ -896,71 +670,21 @@ for (let i = 0; i < Math.min(buttons.length, 3); i++) {
|
|
|
896
670
|
}`
|
|
897
671
|
});
|
|
898
672
|
|
|
899
|
-
//
|
|
673
|
+
// Generate description
|
|
674
|
+
const changes = [];
|
|
675
|
+
if (defaultStyle.boxShadow !== hoverStyle.boxShadow) changes.push('Shadow changes');
|
|
676
|
+
if (defaultStyle.transform !== hoverStyle.transform) changes.push('Transform changes');
|
|
677
|
+
if (defaultStyle.background !== hoverStyle.background) changes.push('Background changes');
|
|
678
|
+
|
|
900
679
|
componentAnimations[btnId] = {
|
|
901
680
|
type: 'button',
|
|
902
|
-
states: {
|
|
903
|
-
default: defaultStyle,
|
|
904
|
-
hover: hoverStyle
|
|
905
|
-
},
|
|
906
|
-
changes: {
|
|
907
|
-
background_changed: defaultStyle.background !== hoverStyle.background,
|
|
908
|
-
shadow_changed: defaultStyle.boxShadow !== hoverStyle.boxShadow,
|
|
909
|
-
transform_changed: defaultStyle.transform !== hoverStyle.transform,
|
|
910
|
-
border_changed: defaultStyle.border !== hoverStyle.border
|
|
911
|
-
},
|
|
681
|
+
states: { default: defaultStyle, hover: hoverStyle },
|
|
912
682
|
transition: buttons[i].transition,
|
|
913
|
-
description:
|
|
683
|
+
description: changes.length > 0 ? changes.join(' + ') : 'No visible changes'
|
|
914
684
|
};
|
|
915
685
|
}
|
|
916
686
|
|
|
917
|
-
//
|
|
918
|
-
for (let i = 0; i < Math.min(cards.length, 3); i++) {
|
|
919
|
-
// ... same process for cards ...
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
// Inputs (focus state)
|
|
923
|
-
for (let i = 0; i < Math.min(inputs.length, 3); i++) {
|
|
924
|
-
// ... same process with 'focus' event instead of 'mouseenter' ...
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
function generateDescription(defaultStyle, hoverStyle) {
|
|
928
|
-
const changes = [];
|
|
929
|
-
|
|
930
|
-
if (defaultStyle.boxShadow !== hoverStyle.boxShadow) {
|
|
931
|
-
if (defaultStyle.boxShadow === 'none' && hoverStyle.boxShadow !== 'none') {
|
|
932
|
-
changes.push('Shadow appears');
|
|
933
|
-
} else if (defaultStyle.boxShadow !== 'none' && hoverStyle.boxShadow !== 'none') {
|
|
934
|
-
changes.push('Shadow changes');
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
if (defaultStyle.transform !== hoverStyle.transform) {
|
|
939
|
-
if (hoverStyle.transform.includes('scale')) {
|
|
940
|
-
changes.push('Scales up');
|
|
941
|
-
}
|
|
942
|
-
if (hoverStyle.transform.includes('translateY')) {
|
|
943
|
-
changes.push('Moves up');
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
if (defaultStyle.background !== hoverStyle.background) {
|
|
948
|
-
changes.push('Background changes');
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
return changes.length > 0 ? changes.join(' + ') : 'No visible changes';
|
|
952
|
-
}
|
|
953
|
-
```
|
|
954
|
-
|
|
955
|
-
**Report:**
|
|
956
|
-
```
|
|
957
|
-
✅ Component Animations Extracted!
|
|
958
|
-
|
|
959
|
-
📸 Screenshots captured:
|
|
960
|
-
- ${Object.keys(componentAnimations).length} components
|
|
961
|
-
- ${Object.keys(componentAnimations).length * 2} screenshots (default + hover/focus)
|
|
962
|
-
|
|
963
|
-
🔄 Capturing full-page screenshot...
|
|
687
|
+
// Repeat for cards and inputs...
|
|
964
688
|
```
|
|
965
689
|
|
|
966
690
|
---
|
|
@@ -968,227 +692,221 @@ function generateDescription(defaultStyle, hoverStyle) {
|
|
|
968
692
|
## STEP 4: Full-Page Screenshot
|
|
969
693
|
|
|
970
694
|
```javascript
|
|
971
|
-
|
|
972
|
-
await Bash: mkdir -p .claude/extractions/screenshots/${siteName}
|
|
695
|
+
await Bash: mkdir -p design-system/extracted/${siteName}/screenshots
|
|
973
696
|
|
|
974
|
-
// Try fullpage
|
|
975
697
|
try {
|
|
976
698
|
await mcp__chrome-devtools__take_screenshot({
|
|
977
699
|
fullPage: true,
|
|
978
700
|
format: 'png',
|
|
979
|
-
filePath:
|
|
701
|
+
filePath: `design-system/extracted/${siteName}/screenshots/full-page.png`
|
|
980
702
|
});
|
|
981
703
|
} catch {
|
|
982
|
-
// Fallback: viewport only
|
|
983
704
|
await mcp__chrome-devtools__take_screenshot({
|
|
984
705
|
fullPage: false,
|
|
985
706
|
format: 'png',
|
|
986
|
-
filePath:
|
|
707
|
+
filePath: `design-system/extracted/${siteName}/screenshots/viewport.png`
|
|
987
708
|
});
|
|
988
709
|
}
|
|
989
710
|
```
|
|
990
711
|
|
|
991
712
|
---
|
|
992
713
|
|
|
993
|
-
## STEP 5:
|
|
714
|
+
## STEP 5: AI Psychology Analysis
|
|
994
715
|
|
|
995
|
-
|
|
716
|
+
Read screenshot and analyze:
|
|
996
717
|
|
|
997
718
|
```javascript
|
|
998
|
-
const
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
},
|
|
1061
|
-
|
|
1062
|
-
// ========== SHADOWS ==========
|
|
1063
|
-
shadows: shadows.shadows.slice(0, 5),
|
|
1064
|
-
|
|
1065
|
-
// ========== BORDER RADIUS ==========
|
|
1066
|
-
border_radius: shadows.borderRadii.slice(0, 5)
|
|
1067
|
-
};
|
|
1068
|
-
|
|
1069
|
-
// Write site-specific JSON
|
|
1070
|
-
Write(`.claude/extractions/${siteName}.json`, JSON.stringify(siteData, null, 2));
|
|
1071
|
-
```
|
|
1072
|
-
|
|
1073
|
-
**Report:**
|
|
1074
|
-
```
|
|
1075
|
-
✅ Site Data Extracted: ${siteName}
|
|
1076
|
-
|
|
1077
|
-
🎨 Style: ${siteData.style.detected} (${siteData.style.confidence}% confidence)
|
|
1078
|
-
📚 Animation Libraries: ${siteData.animation_libraries.map(l => l.name).join(', ') || 'none'}
|
|
1079
|
-
🎬 Scroll Animations: ${siteData.scroll_animations.patterns.join(', ') || 'none'}
|
|
1080
|
-
🖼️ Decorative: ${siteData.decorative_elements.types.join(', ') || 'none'}
|
|
1081
|
-
|
|
1082
|
-
📁 Saved: .claude/extractions/${siteName}.json
|
|
719
|
+
const screenshotPath = exists(`design-system/extracted/${siteName}/screenshots/full-page.png`)
|
|
720
|
+
? `design-system/extracted/${siteName}/screenshots/full-page.png`
|
|
721
|
+
: `design-system/extracted/${siteName}/screenshots/viewport.png`;
|
|
722
|
+
|
|
723
|
+
const screenshot = Read(screenshotPath);
|
|
724
|
+
|
|
725
|
+
const analysisPrompt = `
|
|
726
|
+
You are a UX/UI design psychologist.
|
|
727
|
+
|
|
728
|
+
Analyze this website's design and provide insights in YAML format.
|
|
729
|
+
|
|
730
|
+
Visual Screenshot: [attached]
|
|
731
|
+
|
|
732
|
+
Extracted CSS Data:
|
|
733
|
+
- Colors: ${JSON.stringify(colors, null, 2)}
|
|
734
|
+
- Typography: ${JSON.stringify(typography.allFonts, null, 2)}
|
|
735
|
+
|
|
736
|
+
Return YAML format:
|
|
737
|
+
|
|
738
|
+
\`\`\`yaml
|
|
739
|
+
psychology:
|
|
740
|
+
style_classification: # e.g., Neo-Brutalism, Minimalist, Modern SaaS
|
|
741
|
+
|
|
742
|
+
emotions_evoked:
|
|
743
|
+
- emotion: Trust
|
|
744
|
+
reason: "Soft rounded corners reduce anxiety"
|
|
745
|
+
- emotion: Adventure
|
|
746
|
+
reason: "Vibrant colors suggest excitement"
|
|
747
|
+
|
|
748
|
+
target_audience:
|
|
749
|
+
primary:
|
|
750
|
+
description: "Travelers seeking unique accommodations"
|
|
751
|
+
age_range: "25-45"
|
|
752
|
+
tech_savvy: high
|
|
753
|
+
secondary:
|
|
754
|
+
description: "Hosts listing their properties"
|
|
755
|
+
|
|
756
|
+
visual_principles:
|
|
757
|
+
- name: "Photo-First Design"
|
|
758
|
+
description: "Large images dominate, UI recedes"
|
|
759
|
+
- name: "Soft & Approachable"
|
|
760
|
+
description: "Rounded corners, light grays feel warm"
|
|
761
|
+
|
|
762
|
+
why_it_works:
|
|
763
|
+
- "Marketplace requires trust → Clear info, ratings, soft design"
|
|
764
|
+
- "Travel is emotional → Photo-first, inspirational imagery"
|
|
765
|
+
|
|
766
|
+
design_philosophy:
|
|
767
|
+
core_belief: "Let the content shine"
|
|
768
|
+
key_principles:
|
|
769
|
+
- "Photography is hero"
|
|
770
|
+
- "Consistency builds trust"
|
|
771
|
+
- "Subtle brand presence"
|
|
772
|
+
\`\`\`
|
|
773
|
+
|
|
774
|
+
Be specific with examples from the visual.
|
|
775
|
+
`;
|
|
776
|
+
|
|
777
|
+
const psychologyYaml = await LLM({
|
|
778
|
+
prompt: analysisPrompt,
|
|
779
|
+
images: [screenshot]
|
|
780
|
+
});
|
|
1083
781
|
```
|
|
1084
782
|
|
|
1085
783
|
---
|
|
1086
784
|
|
|
1087
|
-
## STEP 6:
|
|
1088
|
-
|
|
1089
|
-
If extracting from multiple sites, merge insights:
|
|
785
|
+
## STEP 6: Generate data.yaml (17 Sections + Psychology)
|
|
1090
786
|
|
|
1091
787
|
```javascript
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
788
|
+
const yamlContent = `# Design Extraction: ${siteName}
|
|
789
|
+
# Extracted: ${new Date().toISOString()}
|
|
790
|
+
# URL: ${url}
|
|
791
|
+
|
|
792
|
+
meta:
|
|
793
|
+
site_name: ${siteName}
|
|
794
|
+
url: ${url}
|
|
795
|
+
extracted_at: ${new Date().toISOString()}
|
|
796
|
+
extractor_version: "2.1.0"
|
|
797
|
+
coverage:
|
|
798
|
+
total_sections: 17
|
|
799
|
+
detected_sections: ${countDetectedSections()}
|
|
800
|
+
percentage: ${Math.round((countDetectedSections() / 17) * 100)}
|
|
801
|
+
|
|
802
|
+
# ============================================
|
|
803
|
+
# PSYCHOLOGY & ANALYSIS
|
|
804
|
+
# ============================================
|
|
805
|
+
|
|
806
|
+
${psychologyYaml}
|
|
807
|
+
|
|
808
|
+
# ============================================
|
|
809
|
+
# DESIGN TOKENS
|
|
810
|
+
# ============================================
|
|
811
|
+
|
|
812
|
+
sections:
|
|
813
|
+
overview:
|
|
814
|
+
detected: true
|
|
815
|
+
style: "${detectStyle()}"
|
|
816
|
+
tech_stack: Framework-agnostic
|
|
817
|
+
|
|
818
|
+
color_palette:
|
|
819
|
+
detected: true
|
|
820
|
+
primary:
|
|
821
|
+
${colors.backgrounds.slice(0, 5).map(c => ` - hex: "${c.hex}"
|
|
822
|
+
rgb: "${c.rgb}"
|
|
823
|
+
usage: "${c.usage}"`).join('\n')}
|
|
824
|
+
|
|
825
|
+
text_colors:
|
|
826
|
+
${colors.texts.slice(0, 5).map(c => ` - hex: "${c.hex}"
|
|
827
|
+
usage: "${c.usage}"`).join('\n')}
|
|
828
|
+
|
|
829
|
+
border_colors:
|
|
830
|
+
${colors.borders.slice(0, 3).map(c => ` - hex: "${c.hex}"
|
|
831
|
+
usage: "${c.usage}"`).join('\n')}
|
|
832
|
+
|
|
833
|
+
typography:
|
|
834
|
+
detected: true
|
|
835
|
+
fonts:
|
|
836
|
+
${typography.allFonts.slice(0, 3).map(f => ` - "${f}"`).join('\n')}
|
|
837
|
+
weights: [${typography.allWeights.join(', ')}]
|
|
838
|
+
sizes: [${typography.allSizes.join(', ')}]
|
|
839
|
+
|
|
840
|
+
spacing_system:
|
|
841
|
+
detected: true
|
|
842
|
+
grid_base: ${spacing.detectedGrid}
|
|
843
|
+
common_values: [${spacing.commonValues.join(', ')}]
|
|
844
|
+
|
|
845
|
+
component_styles:
|
|
846
|
+
detected: true
|
|
847
|
+
buttons:
|
|
848
|
+
${buttons.slice(0, 3).map(btn => ` - id: "${btn.id}"
|
|
849
|
+
text: "${btn.text}"
|
|
850
|
+
backgroundColor: "${btn.backgroundColor}"
|
|
851
|
+
color: "${btn.color}"
|
|
852
|
+
padding: "${btn.padding}"
|
|
853
|
+
borderRadius: "${btn.borderRadius}"
|
|
854
|
+
transition: "${btn.transition}"
|
|
855
|
+
hover_animation: "${componentAnimations[btn.id]?.description || 'none'}"`).join('\n')}
|
|
856
|
+
|
|
857
|
+
cards:
|
|
858
|
+
${cards.slice(0, 3).map(card => ` - id: "${card.id}"
|
|
859
|
+
backgroundColor: "${card.backgroundColor}"
|
|
860
|
+
padding: "${card.padding}"
|
|
861
|
+
borderRadius: "${card.borderRadius}"
|
|
862
|
+
boxShadow: "${card.boxShadow}"
|
|
863
|
+
hover_animation: "${componentAnimations[card.id]?.description || 'none'}"`).join('\n')}
|
|
864
|
+
|
|
865
|
+
shadows_elevation:
|
|
866
|
+
detected: true
|
|
867
|
+
values:
|
|
868
|
+
${shadows.shadows.slice(0, 5).map(s => ` - "${s}"`).join('\n')}
|
|
869
|
+
|
|
870
|
+
animations_transitions:
|
|
871
|
+
detected: true
|
|
872
|
+
keyframes:
|
|
873
|
+
${animations.keyframes.slice(0, 5).map(k => ` - name: "${k.name}"`).join('\n')}
|
|
874
|
+
transitions:
|
|
875
|
+
${animations.transitions.slice(0, 5).map(t => ` - duration: "${t.transitionDuration}"
|
|
876
|
+
timing: "${t.transitionTimingFunction}"`).join('\n')}
|
|
877
|
+
|
|
878
|
+
border_radius:
|
|
879
|
+
detected: true
|
|
880
|
+
values: [${shadows.borderRadii.slice(0, 8).join(', ')}]
|
|
881
|
+
|
|
882
|
+
border_styles:
|
|
883
|
+
detected: true
|
|
884
|
+
widths: [${shadows.borderWidths.join(', ')}]
|
|
885
|
+
|
|
886
|
+
layout_patterns:
|
|
887
|
+
detected: true
|
|
888
|
+
container_width: "1280px"
|
|
889
|
+
grid_columns: 12
|
|
890
|
+
|
|
891
|
+
# ============================================
|
|
892
|
+
# COMPONENT ANIMATIONS (DETAILED)
|
|
893
|
+
# ============================================
|
|
894
|
+
|
|
895
|
+
animations:
|
|
896
|
+
${Object.entries(componentAnimations).map(([id, anim]) => ` ${id}:
|
|
897
|
+
type: "${anim.type}"
|
|
898
|
+
description: "${anim.description}"
|
|
899
|
+
transition: "${anim.transition}"
|
|
900
|
+
states:
|
|
901
|
+
default:
|
|
902
|
+
background: "${anim.states.default?.background || 'none'}"
|
|
903
|
+
boxShadow: "${anim.states.default?.boxShadow || 'none'}"
|
|
904
|
+
hover:
|
|
905
|
+
background: "${anim.states.hover?.background || 'none'}"
|
|
906
|
+
boxShadow: "${anim.states.hover?.boxShadow || 'none'}"`).join('\n')}
|
|
907
|
+
`;
|
|
908
|
+
|
|
909
|
+
Write(`design-system/extracted/${siteName}/data.yaml`, yamlContent);
|
|
1192
910
|
```
|
|
1193
911
|
|
|
1194
912
|
---
|
|
@@ -1197,44 +915,48 @@ ${mergedInsights.scroll_animations.map(a => ` - ${a.pattern} (from: ${a.source
|
|
|
1197
915
|
|
|
1198
916
|
```
|
|
1199
917
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1200
|
-
✅ EXTRACTION COMPLETE
|
|
918
|
+
✅ EXTRACTION COMPLETE: ${siteName}
|
|
1201
919
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1202
920
|
|
|
1203
|
-
📊
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
921
|
+
📊 Coverage: ${countDetectedSections()}/17 sections
|
|
922
|
+
|
|
923
|
+
🎨 Design Tokens:
|
|
924
|
+
✅ Colors: ${colors.backgrounds.length} backgrounds (with usage)
|
|
925
|
+
✅ Typography: ${typography.allFonts.length} fonts
|
|
926
|
+
✅ Spacing: ${spacing.detectedGrid}px grid detected
|
|
927
|
+
✅ Components: ${buttons.length} buttons, ${cards.length} cards
|
|
928
|
+
✅ Shadows: ${shadows.shadows.length} unique values
|
|
929
|
+
✅ Animations: ${animations.keyframes.length} @keyframes
|
|
930
|
+
|
|
931
|
+
🧠 Psychology Analysis:
|
|
932
|
+
✅ Style classification
|
|
933
|
+
✅ Emotions evoked
|
|
934
|
+
✅ Target audience
|
|
935
|
+
✅ Visual principles
|
|
936
|
+
✅ Why it works
|
|
937
|
+
|
|
938
|
+
📸 Screenshots: ${Object.keys(componentAnimations).length * 2 + 1} captured
|
|
939
|
+
|
|
940
|
+
📁 Output:
|
|
941
|
+
✓ design-system/extracted/${siteName}/data.yaml
|
|
942
|
+
✓ design-system/extracted/${siteName}/screenshots/
|
|
1221
943
|
|
|
1222
944
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1223
945
|
|
|
1224
946
|
🚀 Next Steps:
|
|
1225
947
|
|
|
1226
948
|
1. Extract more references (optional):
|
|
1227
|
-
/extract https://
|
|
949
|
+
/extract https://linear.app
|
|
1228
950
|
|
|
1229
951
|
2. Generate design system:
|
|
1230
|
-
/designsetup
|
|
952
|
+
/designsetup @prd.md
|
|
1231
953
|
|
|
1232
|
-
→ Will read: .
|
|
1233
|
-
→ Will
|
|
1234
|
-
→ Will output: design-system/
|
|
954
|
+
→ Will read: design-system/extracted/*/data.yaml
|
|
955
|
+
→ Will merge: Psychology + Tokens + Animations
|
|
956
|
+
→ Will output: design-system/data.yaml (final)
|
|
1235
957
|
|
|
1236
958
|
3. Review extracted data:
|
|
1237
|
-
|
|
959
|
+
cat design-system/extracted/${siteName}/data.yaml
|
|
1238
960
|
|
|
1239
961
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1240
962
|
```
|
|
@@ -1255,7 +977,6 @@ try {
|
|
|
1255
977
|
Check:
|
|
1256
978
|
- Is the URL accessible?
|
|
1257
979
|
- Is Chrome DevTools MCP running?
|
|
1258
|
-
- Try with --verbose for details
|
|
1259
980
|
`);
|
|
1260
981
|
}
|
|
1261
982
|
|
|
@@ -1266,13 +987,6 @@ try {
|
|
|
1266
987
|
console.warn('Color extraction failed:', error.message);
|
|
1267
988
|
colors = { backgrounds: [], texts: [], borders: [] };
|
|
1268
989
|
}
|
|
1269
|
-
|
|
1270
|
-
// Screenshot failures are non-critical
|
|
1271
|
-
try {
|
|
1272
|
-
await takeScreenshot();
|
|
1273
|
-
} catch (error) {
|
|
1274
|
-
console.warn('Screenshot failed, continuing...');
|
|
1275
|
-
}
|
|
1276
990
|
```
|
|
1277
991
|
|
|
1278
992
|
---
|