designlang 1.0.0 → 2.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 +108 -83
- package/bin/design-extract.js +126 -11
- package/package.json +1 -1
- package/src/crawler.js +125 -20
- package/src/diff.js +146 -0
- package/src/extractors/accessibility.js +95 -0
- package/src/formatters/figma.js +83 -0
- package/src/formatters/markdown.js +36 -1
- package/src/formatters/preview.js +237 -0
- package/src/formatters/theme.js +128 -0
- package/src/history.js +103 -0
- package/src/index.js +9 -0
package/README.md
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<h1 align="center">designlang</h1>
|
|
3
|
-
<p align="center">Extract the complete design language from any website.</p>
|
|
3
|
+
<p align="center">Extract the complete design language from any website in seconds.</p>
|
|
4
4
|
</p>
|
|
5
5
|
|
|
6
6
|
<p align="center">
|
|
7
7
|
<a href="https://www.npmjs.com/package/designlang"><img src="https://img.shields.io/npm/v/designlang?color=blue&label=npm" alt="npm version"></a>
|
|
8
|
-
<a href="https://github.com/Manavarya09/
|
|
8
|
+
<a href="https://github.com/Manavarya09/design-extract/blob/main/LICENSE"><img src="https://img.shields.io/github/license/Manavarya09/design-extract" alt="license"></a>
|
|
9
9
|
<a href="https://nodejs.org"><img src="https://img.shields.io/node/v/designlang" alt="node version"></a>
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
**designlang**
|
|
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.
|
|
15
|
+
|
|
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.
|
|
17
|
+
|
|
18
|
+
**No other tool does all of this from a single command.**
|
|
15
19
|
|
|
16
20
|
## Quick Start
|
|
17
21
|
|
|
@@ -19,134 +23,155 @@
|
|
|
19
23
|
npx designlang https://stripe.com
|
|
20
24
|
```
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
## What You Get (8 Files)
|
|
23
27
|
|
|
24
28
|
| File | What it is |
|
|
25
29
|
|------|------------|
|
|
26
|
-
| `*-design-language.md` | AI-optimized markdown — the full design system
|
|
27
|
-
| `*-
|
|
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 |
|
|
28
33
|
| `*-tailwind.config.js` | Drop-in Tailwind CSS theme extension |
|
|
29
34
|
| `*-variables.css` | CSS custom properties ready to import |
|
|
35
|
+
| `*-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 |
|
|
30
38
|
|
|
31
39
|
## Install
|
|
32
40
|
|
|
33
41
|
```bash
|
|
34
|
-
# Use directly
|
|
42
|
+
# Use directly (no install needed)
|
|
35
43
|
npx designlang https://example.com
|
|
36
44
|
|
|
37
45
|
# Or install globally
|
|
38
46
|
npm install -g designlang
|
|
39
|
-
designlang https://example.com
|
|
40
47
|
```
|
|
41
48
|
|
|
42
|
-
|
|
49
|
+
## Features
|
|
43
50
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
| Category | Details |
|
|
47
|
-
|----------|---------|
|
|
48
|
-
| **Colors** | Full palette with primary/secondary/accent/neutral classification, gradients, background & text colors |
|
|
49
|
-
| **Typography** | Font families, type scale (heading/body/caption), weights, line heights, letter spacing |
|
|
50
|
-
| **Spacing** | All unique values with automatic base-unit detection (e.g. 4px grid) |
|
|
51
|
-
| **Border Radii** | Unique values labeled xs through full |
|
|
52
|
-
| **Box Shadows** | Parsed and classified by visual weight (xs/sm/md/lg/xl) |
|
|
53
|
-
| **CSS Variables** | All `:root` custom properties, categorized by type |
|
|
54
|
-
| **Breakpoints** | Media query breakpoints with standard labels (sm/md/lg/xl) |
|
|
55
|
-
| **Animations** | Transitions, easing functions, durations, `@keyframes` |
|
|
56
|
-
| **Components** | Detected patterns for buttons, cards, inputs, links — with base styles |
|
|
51
|
+
### Multi-Page Crawling
|
|
57
52
|
|
|
58
|
-
|
|
53
|
+
Crawl multiple pages for a site-wide design system:
|
|
59
54
|
|
|
55
|
+
```bash
|
|
56
|
+
designlang https://stripe.com --depth 5
|
|
60
57
|
```
|
|
61
|
-
designlang <url> [options]
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
-h, --height <px> Viewport height (default: 800)
|
|
68
|
-
--wait <ms> Wait after page load for SPAs (default: 0)
|
|
69
|
-
--dark Also extract dark mode color scheme
|
|
70
|
-
--verbose Show detailed extraction progress
|
|
71
|
-
-V, --version Show version
|
|
72
|
-
-h, --help Show help
|
|
59
|
+
### WCAG Accessibility Scoring
|
|
60
|
+
|
|
61
|
+
Every extraction includes a WCAG 2.1 contrast analysis:
|
|
62
|
+
|
|
73
63
|
```
|
|
64
|
+
A11y: 94% WCAG score (7 failing pairs)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Failing color pairs are highlighted in both the markdown and HTML preview with exact contrast ratios.
|
|
74
68
|
|
|
75
|
-
###
|
|
69
|
+
### Component Screenshots
|
|
70
|
+
|
|
71
|
+
Capture PNG screenshots of detected UI components:
|
|
76
72
|
|
|
77
73
|
```bash
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
designlang https://vercel.com --screenshots
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Saves screenshots of buttons, cards, inputs, navigation, hero sections, and a full-page capture.
|
|
80
78
|
|
|
81
|
-
|
|
82
|
-
designlang https://stripe.com --out ./stripe-design
|
|
79
|
+
### Visual HTML Preview
|
|
83
80
|
|
|
84
|
-
|
|
85
|
-
|
|
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)
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
designlang https://app.example.com --wait 3000
|
|
89
|
+
### Design Comparison
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
Compare two sites side-by-side:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
designlang diff https://vercel.com https://stripe.com
|
|
92
95
|
```
|
|
93
96
|
|
|
94
|
-
|
|
97
|
+
Generates `diff.md` and `diff.html` showing color, typography, spacing, and accessibility differences.
|
|
95
98
|
|
|
96
|
-
|
|
99
|
+
### Historical Tracking
|
|
97
100
|
|
|
98
|
-
|
|
99
|
-
- Typography scale with size, weight, line-height, and letter-spacing
|
|
100
|
-
- Spacing scale with token names and px/rem conversions
|
|
101
|
-
- CSS code blocks for shadows, component patterns, and animations
|
|
102
|
-
- A "Quick Start" section with step-by-step instructions to recreate the design
|
|
101
|
+
Track how a site's design evolves over time:
|
|
103
102
|
|
|
104
|
-
|
|
103
|
+
```bash
|
|
104
|
+
# Each extraction auto-saves a snapshot
|
|
105
|
+
designlang https://stripe.com
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
# View history
|
|
108
|
+
designlang history https://stripe.com
|
|
109
|
+
```
|
|
107
110
|
|
|
108
|
-
|
|
111
|
+
Shows color changes, font swaps, accessibility score trends, and CSS variable count over time.
|
|
109
112
|
|
|
110
|
-
|
|
113
|
+
### Framework Themes
|
|
111
114
|
|
|
112
|
-
|
|
113
|
-
/extract-design https://stripe.com
|
|
114
|
-
```
|
|
115
|
+
Generates ready-to-use theme files for:
|
|
115
116
|
|
|
116
|
-
|
|
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
|
|
117
120
|
|
|
118
|
-
##
|
|
121
|
+
## What It Extracts
|
|
122
|
+
|
|
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 |
|
|
135
|
+
|
|
136
|
+
## Full CLI Reference
|
|
119
137
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
3. **Process** — Nine extractor modules parse, deduplicate, cluster, and classify the raw style data into a unified design object
|
|
123
|
-
4. **Format** — Four formatter modules generate the output files
|
|
138
|
+
```
|
|
139
|
+
designlang <url> [options]
|
|
124
140
|
|
|
125
|
-
|
|
141
|
+
Options:
|
|
142
|
+
-o, --out <dir> Output directory (default: ./design-extract-output)
|
|
143
|
+
-n, --name <name> Output file prefix (default: derived from URL)
|
|
144
|
+
-w, --width <px> Viewport width (default: 1280)
|
|
145
|
+
--height <px> Viewport height (default: 800)
|
|
146
|
+
--wait <ms> Wait after page load for SPAs (default: 0)
|
|
147
|
+
--dark Also extract dark mode styles
|
|
148
|
+
--depth <n> Pages to crawl (default: 0, just the URL)
|
|
149
|
+
--screenshots Capture component screenshots
|
|
150
|
+
--framework <type> Only generate specific theme (react, shadcn)
|
|
151
|
+
--no-history Skip saving to history
|
|
152
|
+
--verbose Detailed progress output
|
|
153
|
+
|
|
154
|
+
Commands:
|
|
155
|
+
diff <urlA> <urlB> Compare two sites' design languages
|
|
156
|
+
history <url> View design history for a site
|
|
157
|
+
```
|
|
126
158
|
|
|
127
|
-
|
|
128
|
-
- **Shadow DOM** — Elements inside closed shadow roots are not accessible. Open shadow roots are partially supported.
|
|
129
|
-
- **CSS-in-JS** — Styles injected at runtime (styled-components, Emotion) are captured via computed styles but not as raw CSS rules.
|
|
130
|
-
- **Element cap** — DOM traversal is capped at 5,000 elements to prevent hanging on very large pages.
|
|
159
|
+
## How It Works
|
|
131
160
|
|
|
132
|
-
|
|
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
|
|
133
167
|
|
|
134
|
-
|
|
135
|
-
- [Commander](https://github.com/tj/commander.js/) — CLI framework
|
|
136
|
-
- [Chalk](https://github.com/chalk/chalk) + [Ora](https://github.com/sindresorhus/ora) — terminal styling
|
|
168
|
+
## Claude Code Plugin
|
|
137
169
|
|
|
138
|
-
|
|
170
|
+
**designlang** also works as a [Claude Code](https://claude.ai/claude-code) plugin. Use `/extract-design <url>` in your coding session.
|
|
139
171
|
|
|
140
172
|
## Contributing
|
|
141
173
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
1. Fork the repo
|
|
145
|
-
2. Create a feature branch (`git checkout -b feature/my-feature`)
|
|
146
|
-
3. Make your changes
|
|
147
|
-
4. Test on a few websites (`node bin/designlangtract.js https://example.com`)
|
|
148
|
-
5. Commit and push
|
|
149
|
-
6. Open a pull request
|
|
174
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). PRs welcome!
|
|
150
175
|
|
|
151
176
|
## License
|
|
152
177
|
|
package/bin/design-extract.js
CHANGED
|
@@ -10,6 +10,11 @@ import { formatMarkdown } from '../src/formatters/markdown.js';
|
|
|
10
10
|
import { formatTokens } from '../src/formatters/tokens.js';
|
|
11
11
|
import { formatTailwind } from '../src/formatters/tailwind.js';
|
|
12
12
|
import { formatCssVars } from '../src/formatters/css-vars.js';
|
|
13
|
+
import { formatPreview } from '../src/formatters/preview.js';
|
|
14
|
+
import { formatFigma } from '../src/formatters/figma.js';
|
|
15
|
+
import { formatReactTheme, formatShadcnTheme } from '../src/formatters/theme.js';
|
|
16
|
+
import { diffDesigns, formatDiffMarkdown, formatDiffHtml } from '../src/diff.js';
|
|
17
|
+
import { saveSnapshot, getHistory, formatHistoryMarkdown } from '../src/history.js';
|
|
13
18
|
import { nameFromUrl } from '../src/utils.js';
|
|
14
19
|
|
|
15
20
|
const program = new Command();
|
|
@@ -17,40 +22,47 @@ const program = new Command();
|
|
|
17
22
|
program
|
|
18
23
|
.name('designlang')
|
|
19
24
|
.description('Extract the complete design language from any website')
|
|
20
|
-
.version('
|
|
25
|
+
.version('2.0.0');
|
|
26
|
+
|
|
27
|
+
// ── Main command: extract ──────────────────────────────────────
|
|
28
|
+
program
|
|
21
29
|
.argument('<url>', 'URL to extract design language from')
|
|
22
30
|
.option('-o, --out <dir>', 'output directory', './design-extract-output')
|
|
23
31
|
.option('-n, --name <name>', 'output file prefix (default: derived from URL)')
|
|
24
32
|
.option('-w, --width <px>', 'viewport width', parseInt, 1280)
|
|
25
|
-
.option('
|
|
33
|
+
.option('--height <px>', 'viewport height', parseInt, 800)
|
|
26
34
|
.option('--wait <ms>', 'wait after page load (ms)', parseInt, 0)
|
|
27
35
|
.option('--dark', 'also extract dark mode styles')
|
|
36
|
+
.option('--depth <n>', 'number of internal pages to also crawl', parseInt, 0)
|
|
37
|
+
.option('--screenshots', 'capture component screenshots')
|
|
38
|
+
.option('--framework <type>', 'generate framework theme (react, shadcn)')
|
|
39
|
+
.option('--no-history', 'skip saving to history')
|
|
28
40
|
.option('--verbose', 'show detailed progress')
|
|
29
41
|
.action(async (url, opts) => {
|
|
30
|
-
// Ensure URL has protocol
|
|
31
42
|
if (!url.startsWith('http')) url = `https://${url}`;
|
|
32
|
-
|
|
33
43
|
const prefix = opts.name || nameFromUrl(url);
|
|
34
44
|
const outDir = resolve(opts.out);
|
|
35
45
|
|
|
36
46
|
console.log('');
|
|
37
|
-
console.log(chalk.bold('
|
|
38
|
-
console.log(chalk.gray(` ${url}`));
|
|
47
|
+
console.log(chalk.bold(' designlang'));
|
|
48
|
+
console.log(chalk.gray(` ${url}${opts.depth > 0 ? ` (+ ${opts.depth} pages)` : ''}`));
|
|
39
49
|
console.log('');
|
|
40
50
|
|
|
41
51
|
const spinner = ora('Launching browser...').start();
|
|
42
52
|
|
|
43
53
|
try {
|
|
44
|
-
spinner.text =
|
|
54
|
+
spinner.text = `Crawling${opts.depth > 0 ? ` (depth: ${opts.depth})` : ''}...`;
|
|
45
55
|
const design = await extractDesignLanguage(url, {
|
|
46
56
|
width: opts.width,
|
|
47
|
-
height: opts.height,
|
|
57
|
+
height: parseInt(opts.height) || 800,
|
|
48
58
|
wait: opts.wait,
|
|
49
59
|
dark: opts.dark,
|
|
60
|
+
depth: opts.depth,
|
|
61
|
+
screenshots: opts.screenshots,
|
|
62
|
+
outDir,
|
|
50
63
|
});
|
|
51
64
|
|
|
52
|
-
spinner.text = 'Generating
|
|
53
|
-
|
|
65
|
+
spinner.text = 'Generating outputs...';
|
|
54
66
|
mkdirSync(outDir, { recursive: true });
|
|
55
67
|
|
|
56
68
|
const files = [
|
|
@@ -58,12 +70,31 @@ program
|
|
|
58
70
|
{ name: `${prefix}-design-tokens.json`, content: formatTokens(design), label: 'Design Tokens (W3C)' },
|
|
59
71
|
{ name: `${prefix}-tailwind.config.js`, content: formatTailwind(design), label: 'Tailwind Config' },
|
|
60
72
|
{ name: `${prefix}-variables.css`, content: formatCssVars(design), label: 'CSS Variables' },
|
|
73
|
+
{ name: `${prefix}-preview.html`, content: formatPreview(design), label: 'Visual Preview' },
|
|
74
|
+
{ name: `${prefix}-figma-variables.json`, content: formatFigma(design), label: 'Figma Variables' },
|
|
61
75
|
];
|
|
62
76
|
|
|
77
|
+
// Framework-specific themes
|
|
78
|
+
if (opts.framework === 'react') {
|
|
79
|
+
files.push({ name: `${prefix}-theme.js`, content: formatReactTheme(design), label: 'React Theme' });
|
|
80
|
+
} else if (opts.framework === 'shadcn') {
|
|
81
|
+
files.push({ name: `${prefix}-shadcn-theme.css`, content: formatShadcnTheme(design), label: 'shadcn/ui Theme' });
|
|
82
|
+
} else {
|
|
83
|
+
// Generate both by default
|
|
84
|
+
files.push({ name: `${prefix}-theme.js`, content: formatReactTheme(design), label: 'React Theme' });
|
|
85
|
+
files.push({ name: `${prefix}-shadcn-theme.css`, content: formatShadcnTheme(design), label: 'shadcn/ui Theme' });
|
|
86
|
+
}
|
|
87
|
+
|
|
63
88
|
for (const file of files) {
|
|
64
89
|
writeFileSync(join(outDir, file.name), file.content, 'utf-8');
|
|
65
90
|
}
|
|
66
91
|
|
|
92
|
+
// Save to history
|
|
93
|
+
if (opts.history !== false) {
|
|
94
|
+
const histInfo = saveSnapshot(design);
|
|
95
|
+
if (opts.verbose) spinner.info(`Snapshot #${histInfo.snapshotCount} saved for ${histInfo.hostname}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
67
98
|
spinner.succeed('Extraction complete!');
|
|
68
99
|
console.log('');
|
|
69
100
|
console.log(chalk.bold(' Output files:'));
|
|
@@ -72,12 +103,20 @@ program
|
|
|
72
103
|
const sizeStr = size > 1024 ? `${(size / 1024).toFixed(1)}KB` : `${size}B`;
|
|
73
104
|
console.log(` ${chalk.green('✓')} ${chalk.cyan(file.name)} ${chalk.gray(`(${sizeStr})`)} — ${file.label}`);
|
|
74
105
|
}
|
|
106
|
+
if (opts.screenshots && design.componentScreenshots && Object.keys(design.componentScreenshots).length > 0) {
|
|
107
|
+
for (const [, info] of Object.entries(design.componentScreenshots)) {
|
|
108
|
+
console.log(` ${chalk.green('✓')} ${chalk.cyan(info.path)} — ${info.label} screenshot`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
75
111
|
console.log('');
|
|
76
112
|
console.log(chalk.gray(` Saved to ${outDir}`));
|
|
77
113
|
|
|
78
|
-
// Summary
|
|
114
|
+
// Summary
|
|
79
115
|
console.log('');
|
|
80
116
|
console.log(chalk.bold(' Summary:'));
|
|
117
|
+
if (design.meta.pagesAnalyzed > 1) {
|
|
118
|
+
console.log(` ${chalk.gray('Pages:')} ${design.meta.pagesAnalyzed} pages analyzed`);
|
|
119
|
+
}
|
|
81
120
|
console.log(` ${chalk.gray('Colors:')} ${design.colors.all.length} unique colors`);
|
|
82
121
|
console.log(` ${chalk.gray('Fonts:')} ${design.typography.families.map(f => f.name).join(', ') || 'none detected'}`);
|
|
83
122
|
console.log(` ${chalk.gray('Spacing:')} ${design.spacing.scale.length} values${design.spacing.base ? ` (base: ${design.spacing.base}px)` : ''}`);
|
|
@@ -86,6 +125,13 @@ program
|
|
|
86
125
|
console.log(` ${chalk.gray('Breakpoints:')} ${design.breakpoints.length} breakpoints`);
|
|
87
126
|
console.log(` ${chalk.gray('Components:')} ${Object.keys(design.components).length} patterns detected`);
|
|
88
127
|
console.log(` ${chalk.gray('CSS Vars:')} ${Object.values(design.variables).reduce((s, v) => s + Object.keys(v).length, 0)} custom properties`);
|
|
128
|
+
|
|
129
|
+
// Accessibility summary
|
|
130
|
+
if (design.accessibility) {
|
|
131
|
+
const a = design.accessibility;
|
|
132
|
+
const scoreColor = a.score >= 80 ? chalk.green : a.score >= 50 ? chalk.yellow : chalk.red;
|
|
133
|
+
console.log(` ${chalk.gray('A11y:')} ${scoreColor(`${a.score}% WCAG score`)} (${a.failCount} failing pairs)`);
|
|
134
|
+
}
|
|
89
135
|
console.log('');
|
|
90
136
|
|
|
91
137
|
} catch (err) {
|
|
@@ -101,4 +147,73 @@ program
|
|
|
101
147
|
}
|
|
102
148
|
});
|
|
103
149
|
|
|
150
|
+
// ── Diff command ──────────────────────────────────────────────
|
|
151
|
+
program
|
|
152
|
+
.command('diff <urlA> <urlB>')
|
|
153
|
+
.description('Compare design languages of two websites')
|
|
154
|
+
.option('-o, --out <dir>', 'output directory', './design-diff-output')
|
|
155
|
+
.action(async (urlA, urlB, opts) => {
|
|
156
|
+
if (!urlA.startsWith('http')) urlA = `https://${urlA}`;
|
|
157
|
+
if (!urlB.startsWith('http')) urlB = `https://${urlB}`;
|
|
158
|
+
|
|
159
|
+
console.log('');
|
|
160
|
+
console.log(chalk.bold(' designlang diff'));
|
|
161
|
+
console.log(chalk.gray(` ${urlA}`));
|
|
162
|
+
console.log(chalk.gray(` ${urlB}`));
|
|
163
|
+
console.log('');
|
|
164
|
+
|
|
165
|
+
const spinner = ora('Extracting Site A...').start();
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const designA = await extractDesignLanguage(urlA);
|
|
169
|
+
spinner.text = 'Extracting Site B...';
|
|
170
|
+
const designB = await extractDesignLanguage(urlB);
|
|
171
|
+
|
|
172
|
+
spinner.text = 'Comparing...';
|
|
173
|
+
const diff = diffDesigns(designA, designB);
|
|
174
|
+
|
|
175
|
+
const outDir = resolve(opts.out);
|
|
176
|
+
mkdirSync(outDir, { recursive: true });
|
|
177
|
+
|
|
178
|
+
const mdContent = formatDiffMarkdown(diff);
|
|
179
|
+
const htmlContent = formatDiffHtml(diff);
|
|
180
|
+
|
|
181
|
+
writeFileSync(join(outDir, 'diff.md'), mdContent, 'utf-8');
|
|
182
|
+
writeFileSync(join(outDir, 'diff.html'), htmlContent, 'utf-8');
|
|
183
|
+
|
|
184
|
+
spinner.succeed('Comparison complete!');
|
|
185
|
+
console.log('');
|
|
186
|
+
console.log(` ${chalk.green('✓')} ${chalk.cyan('diff.md')} — Markdown comparison`);
|
|
187
|
+
console.log(` ${chalk.green('✓')} ${chalk.cyan('diff.html')} — Visual comparison`);
|
|
188
|
+
console.log('');
|
|
189
|
+
console.log(chalk.gray(` Saved to ${outDir}`));
|
|
190
|
+
|
|
191
|
+
// Quick summary
|
|
192
|
+
for (const s of diff.sections) {
|
|
193
|
+
if (s.changed && s.changed.length > 0) {
|
|
194
|
+
for (const c of s.changed) {
|
|
195
|
+
console.log(` ${chalk.yellow('≠')} ${s.name} — ${c.property}: ${c.a} → ${c.b}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
console.log('');
|
|
200
|
+
|
|
201
|
+
} catch (err) {
|
|
202
|
+
spinner.fail('Comparison failed');
|
|
203
|
+
console.error(chalk.red(`\n ${err.message}\n`));
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// ── History command ──────────────────────────────────────────
|
|
209
|
+
program
|
|
210
|
+
.command('history <url>')
|
|
211
|
+
.description('View design history for a website')
|
|
212
|
+
.action(async (url) => {
|
|
213
|
+
if (!url.startsWith('http')) url = `https://${url}`;
|
|
214
|
+
const history = getHistory(url);
|
|
215
|
+
console.log('');
|
|
216
|
+
console.log(formatHistoryMarkdown(url, history));
|
|
217
|
+
});
|
|
218
|
+
|
|
104
219
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "designlang",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.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": {
|