@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.
Files changed (32) hide show
  1. package/.claude/CLAUDE.md +103 -58
  2. package/.claude/agents/02-uxui-frontend.md +5 -6
  3. package/.claude/agents/07-ux-tester.md +407 -0
  4. package/.claude/commands/cdev.md +113 -8
  5. package/.claude/commands/csetup.md +86 -28
  6. package/.claude/commands/designsetup.md +171 -48
  7. package/.claude/commands/extract.md +382 -668
  8. package/.claude/commands/pageplan.md +43 -27
  9. package/.claude/contexts/design/box-thinking.md +4 -4
  10. package/.claude/contexts/design/color-theory.md +5 -5
  11. package/.claude/contexts/design/index.md +9 -9
  12. package/.claude/contexts/design/spacing.md +4 -4
  13. package/.claude/contexts/patterns/agent-discovery.md +1 -1
  14. package/.claude/contexts/patterns/animation-patterns.md +3 -3
  15. package/.claude/contexts/patterns/ui-component-consistency.md +3 -3
  16. package/.claude/lib/README.md +1 -1
  17. package/.claude/lib/agent-executor.md +223 -0
  18. package/.claude/lib/context-loading-protocol.md +5 -6
  19. package/.claude/lib/detailed-guides/agent-system.md +4 -4
  20. package/.claude/lib/detailed-guides/best-practices-system.md +5 -4
  21. package/.claude/lib/detailed-guides/context-optimization.md +21 -22
  22. package/.claude/lib/detailed-guides/design-system.md +6 -5
  23. package/.claude/lib/detailed-guides/page-planning.md +6 -6
  24. package/.claude/lib/document-loader.md +32 -47
  25. package/.claude/lib/task-analyzer.md +194 -1
  26. package/.claude/templates/PROJECT_STATUS.template.yml +1 -1
  27. package/.claude/templates/STYLE_GUIDE.template.md +7 -7
  28. package/.claude/templates/design-context-template.md +22 -29
  29. package/.claude/templates/page-plan-example.md +9 -9
  30. package/.claude/templates/phases-sections/ux-testing.md +164 -0
  31. package/README.md +3 -1
  32. package/package.json +1 -1
@@ -1,91 +1,97 @@
1
- # /extract - Extract Design Inspiration from Website(s)
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 one or more websites and save as JSON files that `/designsetup` will use.
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> [URL2] [URL3] ...
12
+ /extract <URL>
13
13
 
14
14
  Arguments:
15
- URL Required. One or more website URLs to extract from
15
+ URL Required. Website URL to extract from
16
16
 
17
17
  Examples:
18
- /extract https://motherduck.com
19
- /extract https://linear.app https://stripe.com
20
- /extract https://ref1.com https://ref2.com https://ref3.com
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 inspiration from website(s) and save to `.claude/extractions/`:
33
- - `{site-name}.json` - Design data per site (colors, typography, animations, style)
34
- - `merged-insights.json` - Combined insights from all extracted sites
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. **Style Detection**: Classify design style automatically
46
- 2. **Animation Capture**: Detect GSAP, ScrollTrigger, CSS animations
47
- 3. **Multi-Site Merge**: Combine insights from multiple references
48
- 4. **Lean Output**: JSON format for token efficiency (~500 tokens per site)
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 URLs (support multiple)
56
- const urls = args.filter(arg => arg.startsWith('http') || !arg.startsWith('-'));
57
- if (urls.length === 0) {
58
- return error('URL required. Usage: /extract https://motherduck.com [https://linear.app]');
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 URLs
62
- const sites = urls.map(url => {
63
- let normalizedUrl = url.trim();
64
- if (!normalizedUrl.startsWith('http://') && !normalizedUrl.startsWith('https://')) {
65
- normalizedUrl = 'https://' + normalizedUrl;
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
- const siteName = new URL(normalizedUrl).hostname
69
- .replace('www.', '')
70
- .replace(/\.[^.]+$/, ''); // Remove TLD
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
- return { url: normalizedUrl, siteName };
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 .claude/extractions/screenshots
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
- 📍 Sites to extract: ${sites.length}
84
- ${sites.map((s, i) => ` ${i + 1}. ${s.siteName} (${s.url})`).join('\n')}
90
+ 📍 URL: ${url}
91
+ 📁 Site: ${siteName}
92
+ 📂 Output: design-system/extracted/${siteName}/
85
93
 
86
- 📂 Output: .claude/extractions/
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]; // Extract text
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 colors = {
178
- backgrounds: new Set(),
179
- texts: new Set(),
180
- borders: new Set()
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: Array.from(colors.backgrounds).slice(0, 30).map(c => ({
207
- rgb: c,
208
- hex: rgbToHex(c)
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 (@keyframes + transitions)
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, ${colors.borders.length} borders
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, ${animations.transitions.length} transitions
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
- // Screenshot: Default state
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); // Wait for transition
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
- // Calculate changes
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: generateDescription(defaultStyle, hoverStyle)
683
+ description: changes.length > 0 ? changes.join(' + ') : 'No visible changes'
914
684
  };
915
685
  }
916
686
 
917
- // Cards (same pattern)
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
- // Create temp directory
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: `.claude/extractions/screenshots/${siteName}/full-page.png`
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: `.claude/extractions/screenshots/${siteName}/viewport.png`
707
+ filePath: `design-system/extracted/${siteName}/screenshots/viewport.png`
987
708
  });
988
709
  }
989
710
  ```
990
711
 
991
712
  ---
992
713
 
993
- ## STEP 5: Generate Site JSON (Lean Format)
714
+ ## STEP 5: AI Psychology Analysis
994
715
 
995
- Generate a lean JSON file per site (~500 tokens):
716
+ Read screenshot and analyze:
996
717
 
997
718
  ```javascript
998
- const siteData = {
999
- meta: {
1000
- site_name: siteName,
1001
- url: url,
1002
- extracted_at: new Date().toISOString(),
1003
- extractor_version: '3.0.0'
1004
- },
1005
-
1006
- // ========== STYLE (from STEP 2.5) ==========
1007
- style: {
1008
- detected: styleData.style.detected,
1009
- confidence: styleData.style.confidence,
1010
- characteristics: styleData.style.characteristics,
1011
- feel: styleData.style.feel
1012
- },
1013
-
1014
- // ========== ANIMATION LIBRARIES ==========
1015
- animation_libraries: styleData.animationLibraries,
1016
-
1017
- // ========== SCROLL ANIMATIONS ==========
1018
- scroll_animations: {
1019
- detected: styleData.scrollAnimations.length > 0,
1020
- patterns: [...new Set(styleData.scrollAnimations.map(a => a.type))],
1021
- count: styleData.scrollAnimations.length
1022
- },
1023
-
1024
- // ========== DECORATIVE ELEMENTS ==========
1025
- decorative_elements: {
1026
- types: [...new Set(styleData.decorativeElements.map(d => d.type))],
1027
- has_blobs: styleData.decorativeElements.some(d => d.type === 'blob'),
1028
- has_gradients: styleData.decorativeElements.some(d => d.type === 'gradient'),
1029
- has_3d: styleData.decorativeElements.some(d => d.type === '3d-element')
1030
- },
1031
-
1032
- // ========== COLORS (Simplified) ==========
1033
- colors: {
1034
- primary: colors.backgrounds.slice(0, 3).map(c => c.hex),
1035
- text: colors.texts.slice(0, 3).map(c => c.hex),
1036
- accent: colors.borders.slice(0, 2).map(c => c.hex)
1037
- },
1038
-
1039
- // ========== TYPOGRAPHY (Simplified) ==========
1040
- typography: {
1041
- fonts: typography.allFonts.slice(0, 3),
1042
- weights: typography.allWeights,
1043
- heading_style: typography.h1[0] ? {
1044
- fontSize: typography.h1[0].fontSize,
1045
- fontWeight: typography.h1[0].fontWeight
1046
- } : null
1047
- },
1048
-
1049
- // ========== COMPONENT ANIMATIONS ==========
1050
- component_animations: {
1051
- button_hover: componentAnimations['button-0']?.description || 'none',
1052
- card_hover: componentAnimations['card-0']?.description || 'none',
1053
- input_focus: componentAnimations['input-0']?.description || 'none'
1054
- },
1055
-
1056
- // ========== SPACING ==========
1057
- spacing: {
1058
- grid_base: spacing.detectedGrid,
1059
- common: spacing.commonValues.slice(0, 8)
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: Merge Multiple Sites (If Multi-URL)
1088
-
1089
- If extracting from multiple sites, merge insights:
785
+ ## STEP 6: Generate data.yaml (17 Sections + Psychology)
1090
786
 
1091
787
  ```javascript
1092
- // Only run if multiple sites
1093
- if (sites.length > 1) {
1094
- const allSiteData = sites.map(site => {
1095
- const data = JSON.parse(Read(`.claude/extractions/${site.siteName}.json`));
1096
- return { siteName: site.siteName, ...data };
1097
- });
1098
-
1099
- const mergedInsights = {
1100
- meta: {
1101
- generated_at: new Date().toISOString(),
1102
- sites_count: sites.length,
1103
- sites: sites.map(s => s.siteName)
1104
- },
1105
-
1106
- // ========== STYLES FROM ALL SITES ==========
1107
- styles: allSiteData.map(site => ({
1108
- site: site.siteName,
1109
- style: site.style.detected,
1110
- confidence: site.style.confidence,
1111
- characteristics: site.style.characteristics,
1112
- feel: site.style.feel
1113
- })),
1114
-
1115
- // ========== ANIMATION LIBRARIES (Combined) ==========
1116
- animation_libraries: [...new Set(
1117
- allSiteData.flatMap(site => site.animation_libraries.map(l => l.name))
1118
- )].map(name => {
1119
- const sources = allSiteData
1120
- .filter(site => site.animation_libraries.some(l => l.name === name))
1121
- .map(site => site.siteName);
1122
- return { name, sources };
1123
- }),
1124
-
1125
- // ========== SCROLL ANIMATIONS (Combined) ==========
1126
- scroll_animations: [...new Set(
1127
- allSiteData.flatMap(site => site.scroll_animations.patterns)
1128
- )].map(pattern => {
1129
- const sources = allSiteData
1130
- .filter(site => site.scroll_animations.patterns.includes(pattern))
1131
- .map(site => site.siteName);
1132
- return { pattern, sources };
1133
- }),
1134
-
1135
- // ========== DECORATIVE ELEMENTS (Combined) ==========
1136
- decorative_elements: [...new Set(
1137
- allSiteData.flatMap(site => site.decorative_elements.types)
1138
- )].map(type => {
1139
- const sources = allSiteData
1140
- .filter(site => site.decorative_elements.types.includes(type))
1141
- .map(site => site.siteName);
1142
- return { type, sources };
1143
- }),
1144
-
1145
- // ========== COMPONENT ANIMATIONS (Combined) ==========
1146
- component_animations: {
1147
- button_hover: allSiteData.map(site => ({
1148
- site: site.siteName,
1149
- animation: site.component_animations.button_hover
1150
- })),
1151
- card_hover: allSiteData.map(site => ({
1152
- site: site.siteName,
1153
- animation: site.component_animations.card_hover
1154
- }))
1155
- },
1156
-
1157
- // ========== COLORS (All Options) ==========
1158
- color_palettes: allSiteData.map(site => ({
1159
- site: site.siteName,
1160
- primary: site.colors.primary,
1161
- text: site.colors.text
1162
- })),
1163
-
1164
- // ========== TYPOGRAPHY (All Options) ==========
1165
- typography_options: allSiteData.map(site => ({
1166
- site: site.siteName,
1167
- fonts: site.typography.fonts
1168
- }))
1169
- };
1170
-
1171
- Write('.claude/extractions/merged-insights.json', JSON.stringify(mergedInsights, null, 2));
1172
- }
1173
- ```
1174
-
1175
- **Report (Multi-Site):**
1176
- ```
1177
- Merged Insights Generated!
1178
-
1179
- 📊 Combined from ${sites.length} sites:
1180
- ${sites.map(s => ` - ${s.siteName}`).join('\n')}
1181
-
1182
- 🎨 Styles found:
1183
- ${mergedInsights.styles.map(s => ` - ${s.style} (${s.site})`).join('\n')}
1184
-
1185
- 📚 Animation Libraries:
1186
- ${mergedInsights.animation_libraries.map(l => ` - ${l.name} (from: ${l.sources.join(', ')})`).join('\n')}
1187
-
1188
- 🎬 Scroll Patterns:
1189
- ${mergedInsights.scroll_animations.map(a => ` - ${a.pattern} (from: ${a.sources.join(', ')})`).join('\n')}
1190
-
1191
- 📁 Saved: .claude/extractions/merged-insights.json
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
- 📊 Sites Extracted: ${sites.length}
1204
- ${sites.map(s => `
1205
- ┌─ ${s.siteName} ─────────────────────────────────────────┐
1206
- 🎨 Style: ${siteData.style.detected} (${siteData.style.confidence}%)
1207
- 📚 Libraries: ${siteData.animation_libraries.map(l => l.name).join(', ') || 'none'}
1208
- 🎬 Scroll: ${siteData.scroll_animations.patterns.join(', ') || 'none'}
1209
- 🖼️ Decorative: ${siteData.decorative_elements.types.join(', ') || 'none'}
1210
- 📁 File: .claude/extractions/${s.siteName}.json
1211
- └─────────────────────────────────────────────────────────┘
1212
- `).join('')}
1213
-
1214
- ${sites.length > 1 ? `
1215
- 📋 Merged Insights: .claude/extractions/merged-insights.json
1216
- Use this in /designsetup to pick features from each site
1217
- ` : ''}
1218
-
1219
- 📸 Screenshots: .claude/extractions/screenshots/
1220
- ${sites.map(s => `- ${s.siteName}/full-page.png`).join('\n ')}
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://another-site.com
949
+ /extract https://linear.app
1228
950
 
1229
951
  2. Generate design system:
1230
- /designsetup
952
+ /designsetup @prd.md
1231
953
 
1232
- → Will read: .claude/extractions/*.json
1233
- → Will ask you to pick: style, animations, theme
1234
- → Will output: design-system/tokens.json
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
- Read .claude/extractions/${sites[0].siteName}.json
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
  ---