designlang 12.4.0 → 12.7.1

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.
@@ -1,15 +1,15 @@
1
1
  {
2
- "name": "design-extract",
2
+ "name": "designlang",
3
3
  "owner": {
4
4
  "name": "Manavarya Singh",
5
5
  "url": "https://github.com/Manavarya09"
6
6
  },
7
7
  "plugins": [
8
8
  {
9
- "name": "design-extract",
9
+ "name": "designlang",
10
10
  "source": "./",
11
- "description": "Extract the complete design language from any website colors, typography, spacing, shadows, components, and more. Outputs AI-optimized markdown, W3C design tokens, Tailwind config, and CSS variables.",
12
- "version": "1.0.0",
11
+ "description": "Seven slash commands wrapping the designlang CLI: /extract (full design language \u2192 DTCG, Tailwind, Figma), /grade (shareable HTML report card + SVG badge), /battle (head-to-head graded comparison), /remix (restyle in 6 vocabularies \u2014 brutalist, swiss, art-deco, cyberpunk, soft-ui, editorial), /pack (one downloadable design-system bundle), /theme-swap (OKLCH-correct recolour around a new brand primary), /brand (full editorial brand-guidelines book \u2014 13 chapters, hand-off-ready).",
12
+ "version": "12.7.1",
13
13
  "author": {
14
14
  "name": "Manavarya Singh"
15
15
  },
@@ -17,12 +17,20 @@
17
17
  "tags": [
18
18
  "design-system",
19
19
  "design-tokens",
20
- "css",
20
+ "dtcg",
21
21
  "tailwind",
22
+ "shadcn",
23
+ "figma",
22
24
  "typography",
23
25
  "colors",
24
- "web-scraping"
26
+ "motion",
27
+ "grade",
28
+ "battle",
29
+ "remix",
30
+ "pack",
31
+ "mcp",
32
+ "playwright"
25
33
  ]
26
34
  }
27
35
  ]
28
- }
36
+ }
@@ -1,24 +1,35 @@
1
1
  {
2
- "name": "design-extract",
3
- "description": "Extract the complete design language from any website. Produces W3C design tokens, AI-optimized markdown, Tailwind config, and CSS custom properties.",
4
- "version": "1.0.0",
2
+ "name": "designlang",
3
+ "description": "Extract any website's design language and ship it. Seven slash commands \u2014 /extract, /grade, /battle, /remix, /pack, /theme-swap, /brand \u2014 wrap the designlang CLI to pull DTCG tokens, Tailwind/shadcn/Figma vars, motion + voice, generate shareable graded report cards, head-to-head battle pages, six-vocabulary remixes, downloadable design-system bundles, OKLCH-correct theme recolouring, and full editorial brand-guidelines books.",
4
+ "version": "12.7.1",
5
5
  "author": {
6
6
  "name": "Manavarya Singh",
7
7
  "url": "https://github.com/Manavarya09"
8
8
  },
9
- "homepage": "https://github.com/Manavarya09/design-extract",
9
+ "homepage": "https://designlang.app",
10
10
  "repository": "https://github.com/Manavarya09/design-extract",
11
11
  "license": "MIT",
12
12
  "keywords": [
13
13
  "design-system",
14
14
  "design-tokens",
15
15
  "design-language",
16
- "css",
16
+ "dtcg",
17
17
  "tailwind",
18
+ "shadcn",
19
+ "figma-variables",
20
+ "css",
18
21
  "playwright",
19
22
  "extraction",
20
23
  "colors",
21
- "typography"
24
+ "typography",
25
+ "motion",
26
+ "grade",
27
+ "battle",
28
+ "remix",
29
+ "pack",
30
+ "claude-plugin",
31
+ "mcp"
22
32
  ],
23
- "skills": "./skills/"
24
- }
33
+ "skills": "./skills/",
34
+ "commands": "./commands/"
35
+ }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,199 @@
1
1
  # Changelog
2
2
 
3
+ ## [12.7.1] — 2026-05-09
4
+
5
+ **Brand book — visual polish pass.**
6
+
7
+ The v12.7.0 brand book had a real-data deficit: the cover used a generic
8
+ grade-coloured accent strip, every section opened with a philosophical
9
+ lede ("the felt pace of an interface", "form follows feeling"), and
10
+ components were a metadata table instead of a real mock. This pass
11
+ replaces all of that with the extracted values themselves.
12
+
13
+ Changes:
14
+
15
+ - **Cover** now leads with the brand's actual primary as a full-bleed
16
+ band (auto-detected, falls back through secondary/accent/most-used).
17
+ Asymmetric layout — band + label + hex above, host name in massive
18
+ serif below.
19
+ - **Lede paragraphs replaced with one-line data summaries.**
20
+ "1 primary · 1 secondary · 1 accent · 27 neutrals · 88 total" instead
21
+ of "Brand colours carry meaning. Neutrals carry structure."
22
+ - **Type specimen** now uses real headlines extracted from the site's
23
+ voice (`design.voice.sampleHeadings`). Falls back to a single neutral
24
+ pangram only when those are absent. The recycled aphorisms ("quiet
25
+ authority of restraint", "form follows feeling") are gone.
26
+ - **Colour chapter** — primary at full-width hero card, secondary +
27
+ accent at half-width below, neutrals as a flush horizontal strip,
28
+ full palette grid below. Hex labels render *inside* the swatch in
29
+ high-contrast text (auto black/white).
30
+ - **Components chapter** — renders an actual primary + secondary
31
+ button using the extracted brand colour and radius, plus a card
32
+ mockup using extracted surface + text colours. Metadata table moved
33
+ below the visual mock.
34
+ - **Accessibility chapter** — failing pairs render as actual stacked
35
+ colour blocks (foreground text on background with ratio inline),
36
+ not a pure table. Big score number on top.
37
+ - **Tokens chapter** — code blocks now have a header bar with the
38
+ language label and target filename.
39
+ - **"How to use"** trimmed from six rules of thumb to four punchy
40
+ ones, drops the "rule of thumb" framing.
41
+ - **Layout** — section padding moved into the wrap (no more gutter
42
+ around the hero band), TOC now sits on a tinted sub-paper background,
43
+ chapter headers are thinner with a bottom rule.
44
+
45
+ Same 13 chapters, same public API. No breaking changes.
46
+
47
+ 378/378 tests pass (one assertion updated for the new lowercase
48
+ "Brand guidelines" cover label).
49
+
50
+ ## [12.7.0] — 2026-05-09
51
+
52
+ **Brand book — full editorial design-guidelines document for any URL.**
53
+
54
+ \`designlang brand <url>\` produces a self-contained, print-ready HTML
55
+ "brand guidelines book" that documents every dimension of an extracted
56
+ design system. Cover, table of contents, 13 chapters: about, logo,
57
+ colour, typography, spacing, shape, iconography, motion, components,
58
+ voice, accessibility, tokens, how-to-use. Editorial layout, dark-mode
59
+ toggle, smooth-scroll TOC, drop-in code blocks per stack.
60
+
61
+ \`\`\`bash
62
+ npx designlang brand stripe.com
63
+ \`\`\`
64
+
65
+ \`\`\`
66
+ Brand book · 54 tokens · 3 fonts · grade B · https://stripe.com
67
+ ✓ stripe-com.brand.html
68
+ ✓ stripe-com.brand.md
69
+ ✓ stripe-com.brand.json
70
+ \`\`\`
71
+
72
+ ### Why this is different from \`pack\`, \`grade\`, and \`design-language.md\`
73
+
74
+ | Output | Audience | Format |
75
+ |---|---|---|
76
+ | \`pack\` (v12.4) | Devs picking up a design system | Directory of files (tokens, components, Storybook, starter) |
77
+ | \`grade\` (v12.1) | Audit / share | Single audit page with score + verdict |
78
+ | \`design-language.md\` | LLMs | AI-optimized markdown (data-dense) |
79
+ | **\`brand\` (v12.7)** | **Designers + handoff** | **Editorial brand-guidelines book — readable, print-ready, hand-off-ready** |
80
+
81
+ ### Added
82
+
83
+ - New CLI command: \`designlang brand <url> [-o] [-n] [--format] [--open]\`.
84
+ - New formatter \`src/formatters/brand-book.js\` exporting \`formatBrandBook\`
85
+ (HTML book) and \`formatBrandBookMarkdown\` (terse markdown summary).
86
+ - 13 chapters with editorial typography (Instrument Serif display + Inter
87
+ body), generous whitespace, smooth-scroll anchors, dark-mode toggle,
88
+ print stylesheet with page breaks at chapter boundaries.
89
+ - Per-colour section: large swatch + HEX/RGB/HSL/usage grid for brand
90
+ colours, mini-grid for neutrals + full palette.
91
+ - Per-token section: drop-in code blocks for CSS variables, Tailwind
92
+ config, with cross-reference to \`pack\` for the full bundle.
93
+ - Closing "How to use" chapter with six rules of thumb (hierarchy of
94
+ brand colour, two-family discipline, snap-to-scale spacing, tight
95
+ radius set, motion as feedback, accessibility as hard constraint).
96
+ - 7 new tests covering chapter coverage, host/colour/font rendering,
97
+ XSS escaping, sparse-design fallback, and mixed-shape component
98
+ anatomy (the bug that broke the first integration — slots / variants
99
+ arrive as objects, strings, or arrays from the extractor).
100
+
101
+ ### Plugin
102
+
103
+ \`/brand\` is the 7th slash command in the Claude Code plugin
104
+ (\`/extract\`, \`/grade\`, \`/battle\`, \`/remix\`, \`/pack\`, \`/theme-swap\`,
105
+ \`/brand\`). \`.claude-plugin/plugin.json\` and \`marketplace.json\`
106
+ bumped to 12.7.0.
107
+
108
+ ## [12.6.0] — 2026-05-06
109
+
110
+ **Theme-swap — recolour any extracted design around your brand primary.**
111
+
112
+ \`designlang theme-swap <url> --primary <hex>\` takes the existing
113
+ extraction and rotates the brand palette around a new primary while
114
+ preserving everything else: type scale, spacing rhythm, neutrals, motion,
115
+ component anatomy. Closes
116
+ [#57](https://github.com/Manavarya09/design-extract/issues/57).
117
+
118
+ \`\`\`bash
119
+ npx designlang theme-swap stripe.com --primary "#ff4800"
120
+ \`\`\`
121
+
122
+ \`\`\`
123
+ #533afd → #ff4800 · 91 colours · https://stripe.com
124
+ Hue shift: 118.5° · neutrals preserved · type/spacing/motion untouched
125
+
126
+ ✓ stripe-com-themeswap-ff4800.themeswap.html
127
+ ✓ stripe-com-themeswap-ff4800.themeswap.md
128
+ ✓ stripe-com-themeswap-ff4800.themeswap.json
129
+ ✓ stripe-com-themeswap-ff4800.themeswap.tokens.json
130
+ \`\`\`
131
+
132
+ ### Added
133
+
134
+ - New CLI command: \`designlang theme-swap <url> --primary <hex>\`
135
+ with \`--from\`, \`-o\`, \`-n\`, \`--format\`, \`--open\` flags.
136
+ - New module \`src/recolor.js\` exporting \`recolorDesign(design, opts)\`.
137
+ Operates in OKLCH so perceptual lightness stays constant — only hue
138
+ rotates. Auto-detects the source primary; pin it with \`--from\`.
139
+ Neutrals (chroma < 0.04 in OKLCH) are explicitly preserved so body
140
+ text, surfaces, and rule lines stay readable.
141
+ - New formatter \`src/formatters/theme-swap.js\` exporting
142
+ \`formatThemeSwap\` (HTML side-by-side preview) and
143
+ \`formatThemeSwapMarkdown\`.
144
+ - New OKLCH inverse helpers in \`src/utils/color-gamut.js\`:
145
+ \`srgbToOklab\`, \`srgbToOklch\`, \`hexToOklch\`, \`oklchToHex\` —
146
+ with chroma fallback for out-of-gamut colours.
147
+ - The recoloured design is fed through every existing emitter (DTCG,
148
+ Tailwind, shadcn, Figma vars, CSS vars), so the swap propagates
149
+ for free.
150
+ - 10 new tests covering primary-pinning, neutral preservation,
151
+ hue rotation, error paths, \`--from\` override, HTML/markdown shapes,
152
+ and XSS escaping.
153
+
154
+ ### Why
155
+
156
+ People keep asking *"what would Stripe look like in our brand colors?"*.
157
+ \`theme-swap\` answers it in 30 seconds. Bridges \`remix\` (full-vocab
158
+ restyle) and \`apply\` (token write-through to a project).
159
+
160
+ ## [12.5.0] — 2026-05-06
161
+
162
+ **Claude Code plugin — five slash commands wrapping the CLI.**
163
+
164
+ designlang is now a first-class Claude Code plugin. Drop it into any
165
+ session and every CLI verb becomes a slash command:
166
+
167
+ \`\`\`bash
168
+ /plugin install Manavarya09/design-extract
169
+ \`\`\`
170
+
171
+ | Command | What it does |
172
+ |---|---|
173
+ | \`/extract <url>\` | Full extraction → DTCG, Tailwind, Figma, motion, voice |
174
+ | \`/grade <url>\` | Shareable HTML Design Report Card (+ \`--badge\`) |
175
+ | \`/battle <urlA> <urlB>\` | Head-to-head graded battle card |
176
+ | \`/remix <url> --as <vocab>\` | Restyle in 6 vocabularies |
177
+ | \`/pack <url>\` | Bundle every output into one design-system directory |
178
+
179
+ ### Added
180
+
181
+ - \`commands/extract.md\`, \`commands/grade.md\`, \`commands/battle.md\`,
182
+ \`commands/remix.md\`, \`commands/pack.md\` — five slash-command
183
+ manifests with \`description\` + \`argument-hint\` frontmatter and prompt
184
+ bodies that wrap the CLI and surface tight summaries.
185
+ - Refreshed \`.claude-plugin/plugin.json\` (was stale at v1.0.0) — name
186
+ bumped to \`designlang\`, description rewritten around all v12 surfaces,
187
+ added \`commands\` directory pointer + expanded keywords.
188
+ - Refreshed \`.claude-plugin/marketplace.json\` — same updates plus
189
+ marketplace tags.
190
+ - New README section "Claude Code plugin" documenting install + the
191
+ five slash commands. Existing skills-ecosystem section retained for
192
+ Cursor / Codex / 40+ other agents.
193
+
194
+ No CLI behavior change. The slash commands are pure wrappers — they
195
+ shell out to \`npx designlang …\` and read the same output files.
196
+
3
197
  ## [12.4.0] — 2026-05-05
4
198
 
5
199
  **Pack — one command, one polished design-system bundle.**
package/README.md CHANGED
@@ -26,6 +26,8 @@ It also goes where extractors don't: **layout patterns**, **responsive behavior
26
26
 
27
27
  ```bash
28
28
  npx designlang https://stripe.com # extract everything
29
+ npx designlang brand stripe.com # full brand-guidelines book (13 chapters) ← v12.7
30
+ npx designlang theme-swap stripe.com --primary "#ff4800" # recolour around your brand ← v12.6
29
31
  npx designlang pack stripe.com # one polished design-system directory ← v12.4
30
32
  npx designlang remix stripe.com --as cyberpunk # restyle in another vocabulary ← v12.3
31
33
  npx designlang remix stripe.com --all # emit all 6 vocabs at once ← v12.3
@@ -131,7 +133,9 @@ designlang mcp # stdio MCP server for Cursor / Clau
131
133
  | Battle (v12.2) | `designlang battle <A> <B>` | Head-to-head graded battle card with verdict, dimension table, palette comparison |
132
134
  | Badge (v12.2) | `designlang grade --badge` | Shields.io-style SVG badge — `design · B · 87` — drop into any README. Live endpoint: `designlang.app/badge/<host>.svg` |
133
135
  | Remix (v12.3) | `designlang remix <url> --as <vocab>` | Restyle the audited page in another vocabulary (brutalist / swiss / art-deco / cyberpunk / soft-ui / editorial). `--all` emits all 6 |
134
- | Pack (NEW v12.4) | `designlang pack <url>` | Bundle every output (tokens / components / Storybook / starter / prompts) into one polished design-system directory |
136
+ | Pack (v12.4) | `designlang pack <url>` | Bundle every output (tokens / components / Storybook / starter / prompts) into one polished design-system directory |
137
+ | Theme-swap (v12.6) | `designlang theme-swap <url> --primary <hex>` | Recolour the extracted design around a new brand primary. OKLCH hue rotation, neutrals preserved, type/spacing/motion untouched |
138
+ | Brand book (NEW v12.7) | `designlang brand <url>` | Full editorial brand-guidelines document (13 chapters: cover, about, logo, colour, type, spacing, shape, iconography, motion, components, voice, a11y, tokens, how-to-use). Print-ready, dark-mode toggle, hand-off-ready |
135
139
  | Watch | `designlang watch <url>` | Monitor for design changes on interval |
136
140
  | Diff | `designlang diff <A> <B>` | Compare two sites (MD + HTML) |
137
141
  | Multi-brand | `designlang brands <urls...>` | N-site comparison matrix |
@@ -188,6 +192,8 @@ Commands:
188
192
  battle <urlA> <urlB> Head-to-head graded battle card (--format html|md|json|all, --open)
189
193
  remix <url> Restyle in another vocabulary (--as brutalist|swiss|art-deco|cyberpunk|soft-ui|editorial, --all, --list, --open)
190
194
  pack <url> Bundle every output into one design-system directory (--with-clone, --open)
195
+ theme-swap <url> --primary <hex> Recolour around a new brand primary (--from, --format html|md|json|tokens|all, --open)
196
+ brand <url> Generate a full editorial brand-guidelines book (--format html|md|json|all, --open)
191
197
  watch <url> Monitor for design changes on interval
192
198
  diff <urlA> <urlB> Compare two sites' design languages
193
199
  brands <urls...> Multi-brand comparison matrix
@@ -235,16 +241,37 @@ designlang ships surfaces beyond the CLI:
235
241
  | **GitHub Action** | [`github-action/`](github-action/) | "Design regression guard" — diffs tokens on every PR and comments. |
236
242
  | **Chrome extension** | [`chrome-extension/`](chrome-extension/) | One-click handoff from any tab (MV3, `activeTab` only). |
237
243
  | **MCP server** | `npx designlang mcp` | Exposes the extracted design as MCP resources + tools for Cursor, Claude Code, Windsurf, etc. See [`docs/MCP-REGISTRY.md`](docs/MCP-REGISTRY.md). |
244
+ | **Claude Code plugin** | [`.claude-plugin/`](.claude-plugin/) | Five slash commands inside Claude Code — `/extract`, `/grade`, `/battle`, `/remix`, `/pack`. |
238
245
 
239
- ## Agent Skill
246
+ ## Claude Code plugin
240
247
 
241
- Works with **Claude Code, Cursor, Codex, and 40+ AI coding agents** via the skills ecosystem:
248
+ Drop designlang straight into Claude Code as a plugin. Every CLI command becomes a slash command:
249
+
250
+ ```bash
251
+ /plugin install Manavarya09/design-extract
252
+ ```
253
+
254
+ Then inside any Claude Code session:
255
+
256
+ | Slash command | What it does |
257
+ |---|---|
258
+ | `/extract <url>` | Full extraction → DTCG tokens, Tailwind, Figma vars, motion, voice |
259
+ | `/grade <url>` | Shareable HTML "Design Report Card" (+ `--badge` for an SVG) |
260
+ | `/battle <urlA> <urlB>` | Head-to-head graded battle card |
261
+ | `/remix <url> --as <vocab>` | Restyle in brutalist / swiss / art-deco / cyberpunk / soft-ui / editorial |
262
+ | `/pack <url>` | Bundle every output into one design-system directory |
263
+
264
+ Manifest: [`.claude-plugin/plugin.json`](.claude-plugin/plugin.json) · marketplace: [`.claude-plugin/marketplace.json`](.claude-plugin/marketplace.json) · commands: [`commands/`](commands/) · skills: [`skills/`](skills/).
265
+
266
+ ## Agent skill (other ecosystems)
267
+
268
+ Works with **Cursor, Codex, and 40+ AI coding agents** via the skills ecosystem:
242
269
 
243
270
  ```bash
244
271
  npx skills add Manavarya09/design-extract
245
272
  ```
246
273
 
247
- In Claude Code, use `/extract-design <url>`.
274
+ In Cursor / Codex / etc., use `/extract-design <url>`.
248
275
 
249
276
  ## Website
250
277
 
package/SUPPORT.md ADDED
@@ -0,0 +1,22 @@
1
+ # Support
2
+
3
+ Need help with **designlang**?
4
+
5
+ - **Bugs** — open an [issue](https://github.com/Manavarya09/design-extract/issues/new/choose) with steps to reproduce and the URL you were extracting.
6
+ - **Feature requests** — see the [open roadmap](https://github.com/Manavarya09/design-extract/issues?q=is%3Aopen+label%3Aroadmap) (issues #56–#70). Feel free to claim one with a comment, or open a new one.
7
+ - **Security issues** — see [SECURITY.md](SECURITY.md). Please report privately.
8
+ - **Questions** — start a [Discussion](https://github.com/Manavarya09/design-extract/discussions) or DM [@manavaryasingh](https://github.com/Manavarya09).
9
+
10
+ ## Surfaces
11
+
12
+ | Surface | Where to ask |
13
+ |---|---|
14
+ | CLI (`npx designlang`) | issue with the failing command + Node version |
15
+ | Claude Code plugin | issue tagged `claude-code` |
16
+ | MCP server (`designlang mcp`) | issue tagged `mcp` |
17
+ | Website (designlang.app) | issue tagged `website` |
18
+ | VS Code / Raycast / Figma / Chrome extensions | issue tagged with the surface name |
19
+
20
+ ## Response time
21
+
22
+ Best effort within 5 business days. PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
@@ -50,6 +50,9 @@ import { formatScoreBadge } from '../src/formatters/badge.js';
50
50
  import { formatRemix } from '../src/formatters/remix.js';
51
51
  import { VOCABULARIES, getVocabulary, listVocabularies } from '../src/vocabularies/index.js';
52
52
  import { buildPack } from '../src/pack.js';
53
+ import { recolorDesign } from '../src/recolor.js';
54
+ import { formatThemeSwap, formatThemeSwapMarkdown } from '../src/formatters/theme-swap.js';
55
+ import { formatBrandBook, formatBrandBookMarkdown } from '../src/formatters/brand-book.js';
53
56
  import { nameFromUrl } from '../src/utils.js';
54
57
 
55
58
  function validateUrl(url) {
@@ -1225,6 +1228,184 @@ program
1225
1228
  }
1226
1229
  });
1227
1230
 
1231
+ // ── Theme-swap command — recolour an extracted design around a new primary
1232
+ program
1233
+ .command('theme-swap <url>')
1234
+ .description('Recolour the extracted design around a new brand primary (preserves type, spacing, neutrals)')
1235
+ .requiredOption('--primary <hex>', 'target primary colour as hex (e.g. "#ff4800")')
1236
+ .option('--from <hex>', 'override the auto-detected source primary (e.g. when the extractor misclassifies)')
1237
+ .option('-o, --out <dir>', 'output directory', './design-extract-output')
1238
+ .option('-n, --name <name>', 'output file prefix (default: derived from URL + target hex)')
1239
+ .option('--format <fmt>', 'output format: html, md, json, tokens, all', 'all')
1240
+ .option('--open', 'open the HTML preview in the default browser')
1241
+ .action(async (url, opts) => {
1242
+ if (!url.startsWith('http')) url = `https://${url}`;
1243
+ validateUrl(url);
1244
+
1245
+ const spinner = ora(`Extracting ${url}...`).start();
1246
+ try {
1247
+ const original = await extractDesignLanguage(url);
1248
+ spinner.text = `Recolouring around ${opts.primary}...`;
1249
+ const { design: recoloured, summary } = recolorDesign(original, {
1250
+ primary: opts.primary,
1251
+ fromPrimary: opts.from,
1252
+ });
1253
+
1254
+ const outDir = resolve(opts.out);
1255
+ mkdirSync(outDir, { recursive: true });
1256
+ const targetSlug = String(opts.primary).replace(/^#/, '').toLowerCase();
1257
+ const prefix = opts.name || `${nameFromUrl(url)}-themeswap-${targetSlug}`;
1258
+ const written = [];
1259
+
1260
+ if (opts.format === 'all' || opts.format === 'html') {
1261
+ const html = formatThemeSwap(original, recoloured, { version: PKG_VERSION });
1262
+ const p = join(outDir, `${prefix}.themeswap.html`);
1263
+ writeFileSync(p, html);
1264
+ written.push(p);
1265
+ }
1266
+ if (opts.format === 'all' || opts.format === 'md') {
1267
+ const md = formatThemeSwapMarkdown(original, recoloured);
1268
+ const p = join(outDir, `${prefix}.themeswap.md`);
1269
+ writeFileSync(p, md);
1270
+ written.push(p);
1271
+ }
1272
+ if (opts.format === 'all' || opts.format === 'json') {
1273
+ const p = join(outDir, `${prefix}.themeswap.json`);
1274
+ writeFileSync(p, JSON.stringify({
1275
+ url: original.meta?.url,
1276
+ from: summary.from,
1277
+ to: summary.to,
1278
+ hueShift: summary.hueShift,
1279
+ changedColors: summary.changes.length,
1280
+ changes: summary.changes,
1281
+ timestamp: new Date().toISOString(),
1282
+ }, null, 2));
1283
+ written.push(p);
1284
+ }
1285
+ if (opts.format === 'all' || opts.format === 'tokens') {
1286
+ const tokens = formatDtcgTokens(recoloured);
1287
+ const p = join(outDir, `${prefix}.themeswap.tokens.json`);
1288
+ writeFileSync(p, typeof tokens === 'string' ? tokens : JSON.stringify(tokens, null, 2));
1289
+ written.push(p);
1290
+ }
1291
+
1292
+ spinner.stop();
1293
+ console.log('');
1294
+ console.log(` ${chalk.bold(`${summary.from} → ${summary.to}`)} ${chalk.gray('·')} ${chalk.cyan(summary.changes.length)} colours ${chalk.gray('·')} ${chalk.gray(url)}`);
1295
+ console.log(` ${chalk.gray(`Hue shift: ${(summary.hueShift).toFixed(1)}° · neutrals preserved · type/spacing/motion untouched`)}`);
1296
+ console.log('');
1297
+ for (const f of written) console.log(` ${chalk.green('✓')} ${chalk.gray(f)}`);
1298
+ console.log('');
1299
+
1300
+ if (opts.open) {
1301
+ const htmlPath = written.find(p => p.endsWith('.html'));
1302
+ if (htmlPath) {
1303
+ const { spawn } = await import('child_process');
1304
+ const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
1305
+ spawn(cmd, [htmlPath], { detached: true, stdio: 'ignore' }).unref();
1306
+ }
1307
+ }
1308
+ } catch (err) {
1309
+ spinner.fail('Theme-swap failed');
1310
+ console.error(chalk.red(`\n ${err.message}\n`));
1311
+ process.exit(1);
1312
+ }
1313
+ });
1314
+
1315
+ // ── Brand command — full editorial brand-guidelines book ────
1316
+ program
1317
+ .command('brand <url>')
1318
+ .description('Generate a full brand-guidelines book — colour, type, spacing, motion, voice, components, accessibility, tokens, and how-to-use guidance')
1319
+ .option('-o, --out <dir>', 'output directory', './design-extract-output')
1320
+ .option('-n, --name <name>', 'output file prefix (default: derived from URL)')
1321
+ .option('--format <fmt>', 'output format: html, md, json, all', 'all')
1322
+ .option('--open', 'open the HTML book in the default browser')
1323
+ .action(async (url, opts) => {
1324
+ if (!url.startsWith('http')) url = `https://${url}`;
1325
+ validateUrl(url);
1326
+
1327
+ const spinner = ora(`Building brand guidelines for ${url}...`).start();
1328
+ try {
1329
+ // The brand book leans on the full extraction (logo, motion, voice,
1330
+ // anatomy, accessibility), so default to --full unless the caller has
1331
+ // explicitly opted out via env.
1332
+ const design = await extractDesignLanguage(url, {
1333
+ screenshots: true,
1334
+ responsive: false,
1335
+ interactions: false,
1336
+ deepInteract: true,
1337
+ });
1338
+
1339
+ const outDir = resolve(opts.out);
1340
+ mkdirSync(outDir, { recursive: true });
1341
+ const prefix = opts.name || `${nameFromUrl(url)}.brand`;
1342
+ const written = [];
1343
+
1344
+ if (opts.format === 'all' || opts.format === 'html') {
1345
+ const html = formatBrandBook(design, { version: PKG_VERSION });
1346
+ const p = join(outDir, `${prefix}.html`);
1347
+ writeFileSync(p, html);
1348
+ written.push(p);
1349
+ }
1350
+ if (opts.format === 'all' || opts.format === 'md') {
1351
+ const md = formatBrandBookMarkdown(design);
1352
+ const p = join(outDir, `${prefix}.md`);
1353
+ writeFileSync(p, md);
1354
+ written.push(p);
1355
+ }
1356
+ if (opts.format === 'all' || opts.format === 'json') {
1357
+ // A trimmed JSON of the most-used surfaces in the book — useful for
1358
+ // programmatic consumption without re-running extraction.
1359
+ const p = join(outDir, `${prefix}.json`);
1360
+ writeFileSync(p, JSON.stringify({
1361
+ url: design.meta?.url,
1362
+ title: design.meta?.title,
1363
+ timestamp: design.meta?.timestamp,
1364
+ intent: design.pageIntent,
1365
+ material: design.materialLanguage,
1366
+ imagery: design.imageryStyle,
1367
+ library: design.componentLibrary,
1368
+ stack: design.stack,
1369
+ voice: design.voice,
1370
+ colors: design.colors,
1371
+ typography: design.typography,
1372
+ spacing: design.spacing,
1373
+ shadows: design.shadows,
1374
+ borders: design.borders,
1375
+ motion: design.motion,
1376
+ accessibility: design.accessibility,
1377
+ score: design.score,
1378
+ }, null, 2));
1379
+ written.push(p);
1380
+ }
1381
+
1382
+ spinner.stop();
1383
+ const colorCount = (design.colors?.all || []).length;
1384
+ const fontCount = (design.typography?.families || []).length;
1385
+ const grade = design.score?.grade || '—';
1386
+ console.log('');
1387
+ console.log(` ${chalk.bold('Brand book')} ${chalk.gray('·')} ${chalk.cyan(colorCount + ' tokens')} ${chalk.gray('·')} ${chalk.cyan(fontCount + ' fonts')} ${chalk.gray('·')} ${chalk.cyan('grade ' + grade)} ${chalk.gray('·')} ${chalk.gray(url)}`);
1388
+ console.log('');
1389
+ for (const f of written) console.log(` ${chalk.green('✓')} ${chalk.gray(f)}`);
1390
+ console.log('');
1391
+ console.log(chalk.gray(` Open the .html — it's a self-contained, print-ready guidelines book.`));
1392
+ console.log('');
1393
+
1394
+ if (opts.open) {
1395
+ const htmlPath = written.find(p => p.endsWith('.html'));
1396
+ if (htmlPath) {
1397
+ const { spawn } = await import('child_process');
1398
+ const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
1399
+ spawn(cmd, [htmlPath], { detached: true, stdio: 'ignore' }).unref();
1400
+ }
1401
+ }
1402
+ } catch (err) {
1403
+ spinner.fail('Brand book failed');
1404
+ console.error(chalk.red(`\n ${err.message}\n`));
1405
+ process.exit(1);
1406
+ }
1407
+ });
1408
+
1228
1409
  // ── Apply command ──────────────────────────────────────────
1229
1410
  program
1230
1411
  .command('apply <url>')
@@ -0,0 +1,27 @@
1
+ ---
2
+ description: Head-to-head graded battle card between two sites — eight dimensions, bar-by-bar, verdict line.
3
+ argument-hint: <urlA> <urlB>
4
+ ---
5
+
6
+ Pit two sites against each other and emit a single shareable HTML battle card.
7
+
8
+ ```bash
9
+ npx designlang battle $ARGUMENTS
10
+ ```
11
+
12
+ If `$ARGUMENTS` is empty or contains fewer than two URLs, ask the user for both sites.
13
+
14
+ Both sites are extracted in parallel (~30s total). Outputs land in `./design-extract-output/`:
15
+
16
+ - `<a>-vs-<b>.battle.html` — the shareable card
17
+ - `<a>-vs-<b>.battle.md` — markdown summary
18
+ - `<a>-vs-<b>.battle.json` — structured scores
19
+
20
+ After the run:
21
+
22
+ 1. Read the `*.battle.md` file
23
+ 2. Show the user the verdict line ("X wins" / "tie") and the per-dimension table
24
+ 3. Highlight the dimensions where the gap is widest
25
+ 4. Offer to open the HTML card
26
+
27
+ This is pure viral content — battles are designed to be tweeted. Pair with `/grade <url> --badge` so each side has a permanent badge to link back to.
@@ -0,0 +1,59 @@
1
+ ---
2
+ description: Generate a full editorial brand-guidelines book for any URL. 13 chapters covering colour, typography, spacing, shape, iconography, motion, components, voice, accessibility, tokens, and how-to-use guidance. Print-ready, dark-mode toggle, hand-off-ready single HTML.
3
+ argument-hint: <url> [--open]
4
+ ---
5
+
6
+ Build a self-contained, hand-off-ready brand-guidelines book from any live URL.
7
+
8
+ ```bash
9
+ npx designlang brand $ARGUMENTS
10
+ ```
11
+
12
+ If no URL is provided, ask the user which site to document.
13
+
14
+ Output goes to `./design-extract-output/`:
15
+
16
+ - `*.brand.html` — the editorial book (open this — it's a self-contained, print-ready document with TOC, smooth-scroll, dark-mode toggle)
17
+ - `*.brand.md` — terse markdown summary (good for diffing snapshots)
18
+ - `*.brand.json` — structured slice of the design with the surfaces the book renders
19
+
20
+ After the run completes:
21
+
22
+ 1. Read `*.brand.md` to summarise what was captured (host, page intent, material language, tone, palette size, type families, WCAG score)
23
+ 2. Tell the user the headline numbers (`X tokens · Y fonts · grade Z`)
24
+ 3. Offer to open the HTML book (`--open` does this automatically)
25
+ 4. Suggest pairing: `/pack <url>` if they want a developer-facing bundle to drop into a project, `/grade <url>` for the audit, `/theme-swap <url>` to derive a recoloured variant
26
+
27
+ ## What's in the book
28
+
29
+ | § | Chapter | What it documents |
30
+ |---|---|---|
31
+ | 01 | About | Page intent, material language, imagery style, component library, stack, voice tone |
32
+ | 02 | Logo | Extracted SVG logo + clearspace + dimensions |
33
+ | 03 | Colour | Brand colours with HEX/RGB/HSL/usage, neutrals grid, full palette, A11y callout |
34
+ | 04 | Typography | Display + body families, weights, scale table, large specimen |
35
+ | 05 | Spacing | Base unit, scale length, visual rhythm bars |
36
+ | 06 | Shape | Border radii visualised, shadow elevation system |
37
+ | 07 | Iconography | Icon library detection + captured icons grid |
38
+ | 08 | Motion | Feel, durations (animated dots), easings, spring presence, scroll-linked flag |
39
+ | 09 | Components | Detected components with slots/variants/sizes |
40
+ | 10 | Voice & tone | Tone, pronoun posture, heading case, top CTA verbs, sample headings |
41
+ | 11 | Accessibility | WCAG score, failing pairs table, suggested replacements |
42
+ | 12 | Tokens | Drop-in code blocks (CSS vars, Tailwind config) + cross-ref to `pack` |
43
+ | 13 | How to use | Six rules of thumb for derivative work |
44
+
45
+ ## Useful flags
46
+
47
+ | Flag | Effect |
48
+ |---|---|
49
+ | `--format html\|md\|json\|all` | Pick the output(s). Default `all`. |
50
+ | `--out <dir>` | Output directory. Default `./design-extract-output`. |
51
+ | `-n, --name <name>` | Output file prefix. Default derived from URL. |
52
+ | `--open` | Open the HTML book in your default browser. |
53
+
54
+ ## Pairs nicely with
55
+
56
+ - `/pack <url>` — when the recipient wants files to drop into a project, not a guidelines doc
57
+ - `/grade <url>` — when the recipient wants the audit/score, not the full book
58
+ - `/theme-swap <url> --primary <hex>` — when the recipient wants the same system in their brand colour
59
+ - `/remix <url> --as <vocab>` — when the recipient wants a vocabulary swap (brutalist, swiss, etc.)