designlang 2.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <h1 align="center">designlang</h1>
3
- <p align="center">Extract the complete design language from any website in seconds.</p>
3
+ <p align="center">Reverse-engineer any website's complete design system in one command.</p>
4
4
  </p>
5
5
 
6
6
  <p align="center">
@@ -11,11 +11,13 @@
11
11
 
12
12
  ---
13
13
 
14
- **designlang** crawls any website with a headless browser, extracts every computed style from the live DOM, and generates **8 output files** — including an AI-optimized markdown file, visual HTML preview, Tailwind config, Figma variables, React theme, shadcn/ui theme, W3C design tokens, and CSS custom properties.
14
+ <p align="center">
15
+ <img src="designlang.png" alt="designlang in action" width="100%">
16
+ </p>
15
17
 
16
- It also does **WCAG accessibility scoring**, **component screenshot capture**, **multi-page crawling**, **design comparison** between two sites, and **historical tracking** of how a site's design evolves over time.
18
+ **designlang** crawls any website with a headless browser, extracts every computed style from the live DOM, and generates **8 output files** including an AI-optimized markdown file, visual HTML preview, Tailwind config, React theme, shadcn/ui theme, Figma variables, W3C design tokens, and CSS custom properties.
17
19
 
18
- **No other tool does all of this from a single command.**
20
+ But unlike every other tool out there, it also extracts **layout patterns** (grids, flexbox, containers), captures **responsive behavior** across 4 breakpoints, records **interaction states** (hover, focus, active), scores **WCAG accessibility**, and lets you **compare multiple brands** or **sync live sites to local tokens**.
19
21
 
20
22
  ## Quick Start
21
23
 
@@ -23,18 +25,26 @@ It also does **WCAG accessibility scoring**, **component screenshot capture**, *
23
25
  npx designlang https://stripe.com
24
26
  ```
25
27
 
28
+ Get everything at once:
29
+
30
+ ```bash
31
+ npx designlang https://stripe.com --full
32
+ ```
33
+
26
34
  ## What You Get (8 Files)
27
35
 
28
36
  | File | What it is |
29
37
  |------|------------|
30
- | `*-design-language.md` | AI-optimized markdown — the full design system described for LLMs |
31
- | `*-preview.html` | Visual HTML report with swatches, type scale, shadows, a11y score |
32
- | `*-design-tokens.json` | [W3C Design Tokens](https://design-tokens.github.io/community-group/format/) for tooling |
33
- | `*-tailwind.config.js` | Drop-in Tailwind CSS theme extension |
34
- | `*-variables.css` | CSS custom properties ready to import |
38
+ | `*-design-language.md` | AI-optimized markdown — feed it to any LLM to recreate the design |
39
+ | `*-preview.html` | Visual report with swatches, type scale, shadows, a11y score |
40
+ | `*-design-tokens.json` | [W3C Design Tokens](https://design-tokens.github.io/community-group/format/) format |
41
+ | `*-tailwind.config.js` | Drop-in Tailwind CSS theme |
42
+ | `*-variables.css` | CSS custom properties |
35
43
  | `*-figma-variables.json` | Figma Variables import (with dark mode support) |
36
- | `*-theme.js` | React/CSS-in-JS theme object (Chakra, Stitches, Vanilla Extract) |
37
- | `*-shadcn-theme.css` | shadcn/ui globals.css theme variables |
44
+ | `*-theme.js` | React/CSS-in-JS theme (Chakra, Stitches, Vanilla Extract) |
45
+ | `*-shadcn-theme.css` | shadcn/ui globals.css variables |
46
+
47
+ The markdown output has **14 sections**: Color Palette, Typography, Spacing, Border Radii, Box Shadows, CSS Custom Properties, Breakpoints, Transitions & Animations, Component Patterns, Layout System, Responsive Design, Interaction States, Accessibility (WCAG 2.1), and Quick Start.
38
48
 
39
49
  ## Install
40
50
 
@@ -44,94 +54,96 @@ npx designlang https://example.com
44
54
 
45
55
  # Or install globally
46
56
  npm install -g designlang
47
- ```
48
57
 
49
- ## Features
50
-
51
- ### Multi-Page Crawling
58
+ # As an agent skill (Claude Code, Cursor, Codex, 40+ agents)
59
+ npx skills add Manavarya09/design-extract
60
+ ```
52
61
 
53
- Crawl multiple pages for a site-wide design system:
62
+ ## What Makes This Different
54
63
 
55
- ```bash
56
- designlang https://stripe.com --depth 5
57
- ```
64
+ Most design extraction tools give you colors and fonts. That's it. designlang fills 5 market gaps that no other tool addresses:
58
65
 
59
- ### WCAG Accessibility Scoring
66
+ ### 1. Layout System Extraction
60
67
 
61
- Every extraction includes a WCAG 2.1 contrast analysis:
68
+ Extracts the structural skeleton grid column patterns, flex direction usage, container widths, gap values, and justify/align patterns.
62
69
 
63
70
  ```
64
- A11y: 94% WCAG score (7 failing pairs)
71
+ Layout: 55 grids, 492 flex containers
65
72
  ```
66
73
 
67
- Failing color pairs are highlighted in both the markdown and HTML preview with exact contrast ratios.
74
+ Every other tool gives you the paint. designlang gives you the architecture.
68
75
 
69
- ### Component Screenshots
76
+ ### 2. Responsive Multi-Breakpoint Capture
70
77
 
71
- Capture PNG screenshots of detected UI components:
78
+ Crawls the site at 4 viewports (mobile, tablet, desktop, wide) and maps exactly what changes:
72
79
 
73
80
  ```bash
74
- designlang https://vercel.com --screenshots
81
+ designlang https://vercel.com --responsive
75
82
  ```
76
83
 
77
- Saves screenshots of buttons, cards, inputs, navigation, hero sections, and a full-page capture.
78
-
79
- ### Visual HTML Preview
84
+ ```
85
+ Responsive: 4 viewports, 3 breakpoint changes
86
+ 375px 768px: Nav visibility hidden → visible, Hamburger shown → hidden
87
+ 768px → 1280px: Max grid columns 1 → 3, H1 size 32px → 48px
88
+ ```
80
89
 
81
- Every run generates a `*-preview.html` file a gorgeous dark-themed report you can open in your browser with:
82
- - Color swatches for the full palette
83
- - Live type scale rendering
84
- - Spacing scale visualization
85
- - Shadow cards with actual CSS shadows
86
- - Accessibility score and failing pair analysis
87
- - Component screenshots (when `--screenshots` is used)
90
+ No other tool captures how the design *adapts*, just how it looks at one size.
88
91
 
89
- ### Design Comparison
92
+ ### 3. Interaction State Capture
90
93
 
91
- Compare two sites side-by-side:
94
+ Programmatically hovers and focuses interactive elements, capturing the actual style transitions:
92
95
 
93
96
  ```bash
94
- designlang diff https://vercel.com https://stripe.com
97
+ designlang https://stripe.com --interactions
95
98
  ```
96
99
 
97
- Generates `diff.md` and `diff.html` showing color, typography, spacing, and accessibility differences.
100
+ ```css
101
+ /* Button Hover */
102
+ background-color: rgb(83, 58, 253) → rgb(67, 47, 202);
103
+ box-shadow: none → 0 4px 12px rgba(83, 58, 253, 0.4);
98
104
 
99
- ### Historical Tracking
105
+ /* Input Focus */
106
+ border-color: rgb(200, 200, 200) → rgb(83, 58, 253);
107
+ outline: none → 2px solid rgb(83, 58, 253);
108
+ ```
100
109
 
101
- Track how a site's design evolves over time:
110
+ ### 4. Live Site Sync
102
111
 
103
- ```bash
104
- # Each extraction auto-saves a snapshot
105
- designlang https://stripe.com
112
+ Treat the deployed site as your source of truth, not Figma:
106
113
 
107
- # View history
108
- designlang history https://stripe.com
114
+ ```bash
115
+ designlang sync https://stripe.com --out ./src/tokens
109
116
  ```
110
117
 
111
- Shows color changes, font swaps, accessibility score trends, and CSS variable count over time.
112
-
113
- ### Framework Themes
118
+ Detects design changes and auto-updates your local `design-tokens.json`, `tailwind.config.js`, and `variables.css`.
114
119
 
115
- Generates ready-to-use theme files for:
120
+ ### 5. Multi-Brand Comparison
116
121
 
117
- - **React/CSS-in-JS** — theme object compatible with Chakra UI, Stitches, Vanilla Extract
118
- - **shadcn/ui** — CSS variables in the exact format shadcn expects (paste into globals.css)
119
- - **Tailwind** — full theme extension with colors, fonts, spacing, radii, shadows, screens
122
+ Compare N brands side-by-side:
120
123
 
121
- ## What It Extracts
124
+ ```bash
125
+ designlang brands stripe.com vercel.com github.com linear.app
126
+ ```
122
127
 
123
- | Category | Details |
124
- |----------|---------|
125
- | **Colors** | Full palette with primary/secondary/accent/neutral classification, gradients |
126
- | **Typography** | Font families, type scale, heading/body styles, weight distribution |
127
- | **Spacing** | All unique values with automatic base-unit detection (4px/8px grid) |
128
- | **Border Radii** | Unique values labeled xs through full |
129
- | **Box Shadows** | Parsed and classified by visual weight |
130
- | **CSS Variables** | All `:root` custom properties, categorized |
131
- | **Breakpoints** | Media query breakpoints with standard labels |
132
- | **Animations** | Transitions, easing functions, durations, `@keyframes` |
133
- | **Components** | Buttons, cards, inputs, links with base styles |
134
- | **Accessibility** | WCAG 2.1 contrast ratios for all fg/bg color pairs |
128
+ Generates a matrix with color overlap analysis, typography comparison, spacing systems, and accessibility scores. Outputs both `brands.md` and `brands.html`.
129
+
130
+ ## All Features
131
+
132
+ | Feature | Flag / Command | Description |
133
+ |---------|---------------|-------------|
134
+ | Base extraction | `designlang <url>` | Colors, typography, spacing, shadows, radii, CSS vars, breakpoints, animations, components |
135
+ | Layout system | automatic | Grid patterns, flex usage, container widths, gap values |
136
+ | Accessibility | automatic | WCAG 2.1 contrast ratios for all fg/bg pairs |
137
+ | Dark mode | `--dark` | Extracts dark color scheme |
138
+ | Multi-page | `--depth <n>` | Crawl N internal pages for site-wide tokens |
139
+ | Screenshots | `--screenshots` | Capture buttons, cards, inputs, nav, hero, full page |
140
+ | Responsive | `--responsive` | Crawl at 4 viewports, map breakpoint changes |
141
+ | Interactions | `--interactions` | Capture hover/focus/active state transitions |
142
+ | Everything | `--full` | Enable screenshots + responsive + interactions |
143
+ | Diff | `designlang diff <A> <B>` | Compare two sites (MD + HTML) |
144
+ | Multi-brand | `designlang brands <urls...>` | N-site comparison matrix |
145
+ | Sync | `designlang sync <url>` | Update local tokens from live site |
146
+ | History | `designlang history <url>` | Track design changes over time |
135
147
 
136
148
  ## Full CLI Reference
137
149
 
@@ -145,29 +157,78 @@ Options:
145
157
  --height <px> Viewport height (default: 800)
146
158
  --wait <ms> Wait after page load for SPAs (default: 0)
147
159
  --dark Also extract dark mode styles
148
- --depth <n> Pages to crawl (default: 0, just the URL)
160
+ --depth <n> Internal pages to crawl (default: 0)
149
161
  --screenshots Capture component screenshots
162
+ --responsive Capture at multiple breakpoints
163
+ --interactions Capture hover/focus/active states
164
+ --full Enable all captures
150
165
  --framework <type> Only generate specific theme (react, shadcn)
151
166
  --no-history Skip saving to history
152
167
  --verbose Detailed progress output
153
168
 
154
169
  Commands:
155
170
  diff <urlA> <urlB> Compare two sites' design languages
156
- history <url> View design history for a site
171
+ brands <urls...> Multi-brand comparison matrix
172
+ sync <url> Sync local tokens with live site
173
+ history <url> View design change history
174
+ ```
175
+
176
+ ## Example Output
177
+
178
+ Running `designlang https://vercel.com --full`:
179
+
180
+ ```
181
+ designlang
182
+ https://vercel.com
183
+
184
+ Output files:
185
+ ✓ vercel-com-design-language.md (32.6KB)
186
+ ✓ vercel-com-design-tokens.json (5.6KB)
187
+ ✓ vercel-com-tailwind.config.js (3.4KB)
188
+ ✓ vercel-com-variables.css (18.6KB)
189
+ ✓ vercel-com-preview.html (31.8KB)
190
+ ✓ vercel-com-figma-variables.json (12.4KB)
191
+ ✓ vercel-com-theme.js (1.4KB)
192
+ ✓ vercel-com-shadcn-theme.css (477B)
193
+ ✓ screenshots/button.png
194
+ ✓ screenshots/card.png
195
+ ✓ screenshots/nav.png
196
+ ✓ screenshots/hero.png
197
+ ✓ screenshots/full-page.png
198
+
199
+ Summary:
200
+ Colors: 27 unique colors
201
+ Fonts: Geist, Geist Mono
202
+ Spacing: 18 values (base: 2px)
203
+ Shadows: 11 unique shadows
204
+ Radii: 10 unique values
205
+ Breakpoints: 45 breakpoints
206
+ Components: 4 patterns detected
207
+ CSS Vars: 407 custom properties
208
+ Layout: 55 grids, 492 flex containers
209
+ Responsive: 4 viewports, 3 breakpoint changes
210
+ Interactions: 8 state changes captured
211
+ A11y: 94% WCAG score (7 failing pairs)
157
212
  ```
158
213
 
159
214
  ## How It Works
160
215
 
161
- 1. **Crawl** — Launches headless Chromium via Playwright
162
- 2. **Extract** — `page.evaluate()` walks up to 5,000 DOM elements collecting computed styles
163
- 3. **Process** — 10 extractor modules parse, deduplicate, cluster, and classify the raw data
164
- 4. **Format** — 8 formatter modules generate the output files
165
- 5. **Score** — Accessibility extractor calculates WCAG contrast ratios
166
- 6. **Capture** — Optional Playwright screenshots of detected components
216
+ 1. **Crawl** — Launches headless Chromium via Playwright, waits for network idle and fonts
217
+ 2. **Extract** — Single `page.evaluate()` walks up to 5,000 DOM elements collecting 25+ computed style properties including layout (grid, flex, container) data
218
+ 3. **Process** — 12 extractor modules parse, deduplicate, cluster, and classify the raw data
219
+ 4. **Format** — 8 formatter modules generate output files
220
+ 5. **Score** — Accessibility extractor calculates WCAG contrast ratios for all color pairs
221
+ 6. **Capture** — Optional: screenshots, responsive viewport crawling, interaction state recording
222
+
223
+ ## Agent Skill
167
224
 
168
- ## Claude Code Plugin
225
+ Works with **Claude Code, Cursor, Codex, and 40+ AI coding agents** via the skills ecosystem:
226
+
227
+ ```bash
228
+ npx skills add Manavarya09/design-extract
229
+ ```
169
230
 
170
- **designlang** also works as a [Claude Code](https://claude.ai/claude-code) plugin. Use `/extract-design <url>` in your coding session.
231
+ In Claude Code, use `/extract-design <url>`.
171
232
 
172
233
  ## Contributing
173
234
 
@@ -15,6 +15,12 @@ import { formatFigma } from '../src/formatters/figma.js';
15
15
  import { formatReactTheme, formatShadcnTheme } from '../src/formatters/theme.js';
16
16
  import { diffDesigns, formatDiffMarkdown, formatDiffHtml } from '../src/diff.js';
17
17
  import { saveSnapshot, getHistory, formatHistoryMarkdown } from '../src/history.js';
18
+ import { captureResponsive } from '../src/extractors/responsive.js';
19
+ import { captureInteractions } from '../src/extractors/interactions.js';
20
+ import { syncDesign } from '../src/sync.js';
21
+ import { compareBrands, formatBrandMatrix, formatBrandMatrixHtml } from '../src/multibrand.js';
22
+ import { generateClone } from '../src/clone.js';
23
+ import { watchSite } from '../src/watch.js';
18
24
  import { nameFromUrl } from '../src/utils.js';
19
25
 
20
26
  const program = new Command();
@@ -22,7 +28,7 @@ const program = new Command();
22
28
  program
23
29
  .name('designlang')
24
30
  .description('Extract the complete design language from any website')
25
- .version('2.0.0');
31
+ .version('4.0.0');
26
32
 
27
33
  // ── Main command: extract ──────────────────────────────────────
28
34
  program
@@ -36,6 +42,9 @@ program
36
42
  .option('--depth <n>', 'number of internal pages to also crawl', parseInt, 0)
37
43
  .option('--screenshots', 'capture component screenshots')
38
44
  .option('--framework <type>', 'generate framework theme (react, shadcn)')
45
+ .option('--responsive', 'capture design at multiple breakpoints')
46
+ .option('--interactions', 'capture hover/focus/active states')
47
+ .option('--full', 'enable all extra captures (screenshots, responsive, interactions)')
39
48
  .option('--no-history', 'skip saving to history')
40
49
  .option('--verbose', 'show detailed progress')
41
50
  .action(async (url, opts) => {
@@ -58,10 +67,22 @@ program
58
67
  wait: opts.wait,
59
68
  dark: opts.dark,
60
69
  depth: opts.depth,
61
- screenshots: opts.screenshots,
70
+ screenshots: opts.screenshots || opts.full,
62
71
  outDir,
63
72
  });
64
73
 
74
+ // Responsive capture
75
+ if (opts.responsive || opts.full) {
76
+ spinner.text = 'Capturing responsive breakpoints...';
77
+ design.responsive = await captureResponsive(url, { wait: opts.wait });
78
+ }
79
+
80
+ // Interaction state capture
81
+ if (opts.interactions || opts.full) {
82
+ spinner.text = 'Capturing interaction states...';
83
+ design.interactions = await captureInteractions(url, { width: opts.width, height: parseInt(opts.height) || 800, wait: opts.wait });
84
+ }
85
+
65
86
  spinner.text = 'Generating outputs...';
66
87
  mkdirSync(outDir, { recursive: true });
67
88
 
@@ -125,6 +146,22 @@ program
125
146
  console.log(` ${chalk.gray('Breakpoints:')} ${design.breakpoints.length} breakpoints`);
126
147
  console.log(` ${chalk.gray('Components:')} ${Object.keys(design.components).length} patterns detected`);
127
148
  console.log(` ${chalk.gray('CSS Vars:')} ${Object.values(design.variables).reduce((s, v) => s + Object.keys(v).length, 0)} custom properties`);
149
+ if (design.layout) {
150
+ console.log(` ${chalk.gray('Layout:')} ${design.layout.gridCount} grids, ${design.layout.flexCount} flex containers`);
151
+ }
152
+ if (design.responsive) {
153
+ console.log(` ${chalk.gray('Responsive:')} ${design.responsive.viewports.length} viewports, ${design.responsive.changes.length} breakpoint changes`);
154
+ }
155
+ if (design.interactions) {
156
+ const ic = design.interactions;
157
+ const total = ic.buttons.length + ic.links.length + ic.inputs.length;
158
+ console.log(` ${chalk.gray('Interactions:')} ${total} state changes captured`);
159
+ }
160
+ if (design.score) {
161
+ const s = design.score;
162
+ const gradeColor = s.grade === 'A' ? chalk.green : s.grade === 'B' ? chalk.cyan : s.grade === 'C' ? chalk.yellow : chalk.red;
163
+ console.log(` ${chalk.gray('Design Score:')} ${gradeColor(`${s.overall}/100 (${s.grade})`)}${s.issues.length > 0 ? ` — ${s.issues.length} issues` : ''}`);
164
+ }
128
165
 
129
166
  // Accessibility summary
130
167
  if (design.accessibility) {
@@ -216,4 +253,237 @@ program
216
253
  console.log(formatHistoryMarkdown(url, history));
217
254
  });
218
255
 
256
+ // ── Brands command (multi-site comparison) ─────────────────
257
+ program
258
+ .command('brands <urls...>')
259
+ .description('Compare design languages across multiple brands')
260
+ .option('-o, --out <dir>', 'output directory', './design-brands-output')
261
+ .action(async (urls, opts) => {
262
+ console.log('');
263
+ console.log(chalk.bold(' designlang brands'));
264
+ console.log(chalk.gray(` Comparing ${urls.length} sites`));
265
+ console.log('');
266
+
267
+ const spinner = ora(`Extracting ${urls.length} sites...`).start();
268
+
269
+ try {
270
+ const brands = await compareBrands(urls);
271
+
272
+ const outDir = resolve(opts.out);
273
+ mkdirSync(outDir, { recursive: true });
274
+
275
+ const md = formatBrandMatrix(brands);
276
+ const html = formatBrandMatrixHtml(brands);
277
+
278
+ writeFileSync(join(outDir, 'brands.md'), md, 'utf-8');
279
+ writeFileSync(join(outDir, 'brands.html'), html, 'utf-8');
280
+
281
+ spinner.succeed('Brand comparison complete!');
282
+ console.log('');
283
+ console.log(` ${chalk.green('✓')} ${chalk.cyan('brands.md')} — Markdown matrix`);
284
+ console.log(` ${chalk.green('✓')} ${chalk.cyan('brands.html')} — Visual matrix`);
285
+ console.log('');
286
+ console.log(chalk.gray(` Saved to ${outDir}`));
287
+
288
+ // Quick summary
289
+ const valid = brands.filter(b => !b.error);
290
+ for (const b of valid) {
291
+ console.log(` ${chalk.cyan(b.hostname)}: ${b.design.colors.all.length} colors, ${b.design.typography.families.map(f => f.name).join(', ')}, ${b.design.accessibility?.score ?? '?'}% a11y`);
292
+ }
293
+ console.log('');
294
+
295
+ } catch (err) {
296
+ spinner.fail('Brand comparison failed');
297
+ console.error(chalk.red(`\n ${err.message}\n`));
298
+ process.exit(1);
299
+ }
300
+ });
301
+
302
+ // ── Sync command ────────────────────────────────────────────
303
+ program
304
+ .command('sync <url>')
305
+ .description('Sync local design tokens with a live website')
306
+ .option('-o, --out <dir>', 'directory with token files to update', '.')
307
+ .action(async (url, opts) => {
308
+ if (!url.startsWith('http')) url = `https://${url}`;
309
+
310
+ console.log('');
311
+ console.log(chalk.bold(' designlang sync'));
312
+ console.log(chalk.gray(` ${url}`));
313
+ console.log('');
314
+
315
+ const spinner = ora('Extracting current design...').start();
316
+
317
+ try {
318
+ const result = await syncDesign(url, { out: resolve(opts.out) });
319
+
320
+ if (result.isFirstRun) {
321
+ spinner.succeed('First sync — baseline saved.');
322
+ } else if (result.changes.length === 0) {
323
+ spinner.succeed('No design changes detected.');
324
+ } else {
325
+ spinner.succeed(`${result.changes.length} design changes detected!`);
326
+ console.log('');
327
+ for (const c of result.changes) {
328
+ console.log(` ${chalk.yellow('≠')} ${c.property}: ${c.from} → ${c.to}`);
329
+ }
330
+ }
331
+
332
+ if (result.updatedFiles.length > 0) {
333
+ console.log('');
334
+ console.log(chalk.bold(' Updated files:'));
335
+ for (const f of result.updatedFiles) {
336
+ console.log(` ${chalk.green('✓')} ${chalk.cyan(f)}`);
337
+ }
338
+ }
339
+ console.log('');
340
+
341
+ } catch (err) {
342
+ spinner.fail('Sync failed');
343
+ console.error(chalk.red(`\n ${err.message}\n`));
344
+ process.exit(1);
345
+ }
346
+ });
347
+
348
+ // ── Clone command ───────────────────────────────────────────
349
+ program
350
+ .command('clone <url>')
351
+ .description('Generate a working Next.js starter from a site\'s design')
352
+ .option('-o, --out <dir>', 'output directory', './cloned-design')
353
+ .action(async (url, opts) => {
354
+ if (!url.startsWith('http')) url = `https://${url}`;
355
+
356
+ console.log('');
357
+ console.log(chalk.bold(' designlang clone'));
358
+ console.log(chalk.gray(` ${url}`));
359
+ console.log('');
360
+
361
+ const spinner = ora('Extracting design...').start();
362
+
363
+ try {
364
+ const design = await extractDesignLanguage(url);
365
+ spinner.text = 'Generating Next.js project...';
366
+
367
+ const result = generateClone(design, resolve(opts.out));
368
+
369
+ spinner.succeed('Clone generated!');
370
+ console.log('');
371
+ for (const f of result.files) {
372
+ console.log(` ${chalk.green('✓')} ${chalk.cyan(f)}`);
373
+ }
374
+ console.log('');
375
+ console.log(chalk.bold(' To run:'));
376
+ console.log(chalk.gray(` cd ${opts.out} && npm install && npm run dev`));
377
+ console.log('');
378
+
379
+ } catch (err) {
380
+ spinner.fail('Clone failed');
381
+ console.error(chalk.red(`\n ${err.message}\n`));
382
+ process.exit(1);
383
+ }
384
+ });
385
+
386
+ // ── Watch command ───────────────────────────────────────────
387
+ program
388
+ .command('watch <url>')
389
+ .description('Monitor a site for design changes')
390
+ .option('--interval <minutes>', 'check interval in minutes', parseInt, 60)
391
+ .action(async (url, opts) => {
392
+ if (!url.startsWith('http')) url = `https://${url}`;
393
+ const intervalMs = (opts.interval || 60) * 60 * 1000;
394
+
395
+ console.log('');
396
+ console.log(chalk.bold(' designlang watch'));
397
+ console.log(chalk.gray(` ${url} (every ${opts.interval || 60}min)`));
398
+ console.log('');
399
+
400
+ const check = async () => {
401
+ const spinner = ora('Checking for design changes...').start();
402
+ try {
403
+ const result = await watchSite(url);
404
+
405
+ if (result.isFirstRun) {
406
+ spinner.succeed('Baseline captured. Watching for changes...');
407
+ } else if (result.changes.length === 0) {
408
+ spinner.succeed(`No changes — ${new Date().toLocaleTimeString()}`);
409
+ } else {
410
+ spinner.warn(`${result.changes.length} changes detected!`);
411
+ for (const c of result.changes) {
412
+ console.log(` ${chalk.yellow('≠')} ${c.what}: ${c.from} → ${c.to}`);
413
+ }
414
+ }
415
+ } catch (err) {
416
+ spinner.fail(`Check failed: ${err.message}`);
417
+ }
418
+ };
419
+
420
+ await check();
421
+ console.log(chalk.gray(`\n Next check in ${opts.interval || 60} minutes. Press Ctrl+C to stop.\n`));
422
+ setInterval(check, intervalMs);
423
+ });
424
+
425
+ // ── Score command ───────────────────────────────────────────
426
+ program
427
+ .command('score <url>')
428
+ .description('Score a website\'s design system quality')
429
+ .action(async (url) => {
430
+ if (!url.startsWith('http')) url = `https://${url}`;
431
+
432
+ const spinner = ora('Analyzing design...').start();
433
+
434
+ try {
435
+ const design = await extractDesignLanguage(url);
436
+ const s = design.score;
437
+
438
+ spinner.stop();
439
+ console.log('');
440
+ console.log(chalk.bold(' Design System Score'));
441
+ console.log(chalk.gray(` ${url}`));
442
+ console.log('');
443
+
444
+ const gradeColor = s.grade === 'A' ? chalk.green : s.grade === 'B' ? chalk.cyan : s.grade === 'C' ? chalk.yellow : chalk.red;
445
+ console.log(` ${gradeColor.bold(` ${s.overall}/100 Grade: ${s.grade}`)}`);
446
+ console.log('');
447
+
448
+ // Category breakdown
449
+ const cats = [
450
+ ['Color Discipline', s.scores.colorDiscipline],
451
+ ['Typography', s.scores.typographyConsistency],
452
+ ['Spacing System', s.scores.spacingSystem],
453
+ ['Shadows', s.scores.shadowConsistency],
454
+ ['Border Radii', s.scores.radiusConsistency],
455
+ ['Accessibility', s.scores.accessibility],
456
+ ['Tokenization', s.scores.tokenization],
457
+ ];
458
+
459
+ for (const [name, score] of cats) {
460
+ const bar = '█'.repeat(Math.round(score / 5)) + '░'.repeat(20 - Math.round(score / 5));
461
+ const color = score >= 80 ? chalk.green : score >= 60 ? chalk.yellow : chalk.red;
462
+ console.log(` ${chalk.gray(name.padEnd(20))} ${color(bar)} ${score}`);
463
+ }
464
+
465
+ if (s.strengths.length > 0) {
466
+ console.log('');
467
+ console.log(chalk.bold(' Strengths:'));
468
+ for (const str of s.strengths) {
469
+ console.log(` ${chalk.green('✓')} ${str}`);
470
+ }
471
+ }
472
+
473
+ if (s.issues.length > 0) {
474
+ console.log('');
475
+ console.log(chalk.bold(' Issues:'));
476
+ for (const issue of s.issues) {
477
+ console.log(` ${chalk.yellow('!')} ${issue}`);
478
+ }
479
+ }
480
+ console.log('');
481
+
482
+ } catch (err) {
483
+ spinner.fail('Scoring failed');
484
+ console.error(chalk.red(`\n ${err.message}\n`));
485
+ process.exit(1);
486
+ }
487
+ });
488
+
219
489
  program.parse();
package/designlang.png ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "designlang",
3
- "version": "2.0.0",
3
+ "version": "4.0.0",
4
4
  "description": "Extract the complete design language from any website — colors, typography, spacing, shadows, and more. Outputs AI-optimized markdown, W3C design tokens, Tailwind config, and CSS variables.",
5
5
  "type": "module",
6
6
  "bin": {