designlang 12.9.0 → 12.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +41 -0
- package/bin/design-extract.js +98 -0
- package/package.json +1 -1
- package/src/formatters/grade.js +25 -0
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"name": "designlang",
|
|
10
10
|
"source": "./",
|
|
11
11
|
"description": "Eight 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), /pair (fuse two designs across configurable axes \u2014 colours from one site, typography from another).",
|
|
12
|
-
"version": "12.
|
|
12
|
+
"version": "12.10.0",
|
|
13
13
|
"author": {
|
|
14
14
|
"name": "Manavarya Singh"
|
|
15
15
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "designlang",
|
|
3
3
|
"description": "Extract any website's design language and ship it. Eight slash commands \u2014 /extract, /grade, /battle, /remix, /pack, /theme-swap, /brand, /pair \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, full editorial brand-guidelines books, and design crossovers between two sites.",
|
|
4
|
-
"version": "12.
|
|
4
|
+
"version": "12.10.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Manavarya Singh",
|
|
7
7
|
"url": "https://github.com/Manavarya09"
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [12.10.0] — 2026-05-12
|
|
4
|
+
|
|
5
|
+
**Small ship: \`designlang stats\` + low-confidence warning in grade cards.**
|
|
6
|
+
|
|
7
|
+
Two tight quality-of-life additions on top of the v12.9 extraction pass.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **\`designlang stats <url>\`** — one-screen summary to stdout. Grade, primary, fonts, spacing base, WCAG, stack, library, tone, material, intent — all in ~15 lines. No files written. Use \`-j\` / \`--as-json\` for machine-readable output (CI, scripting).
|
|
12
|
+
|
|
13
|
+
\`\`\`
|
|
14
|
+
Grade B · 87/100
|
|
15
|
+
Primary #533afd ×899 59% conf
|
|
16
|
+
Fonts sohne-var
|
|
17
|
+
Type scale 14 sizes
|
|
18
|
+
Spacing base 2px · 13 steps
|
|
19
|
+
Shape 5 radii · 6 shadows
|
|
20
|
+
Colours 31 tokens
|
|
21
|
+
WCAG 79%
|
|
22
|
+
Stack next · unknown
|
|
23
|
+
Material skeuomorphic
|
|
24
|
+
Tone neutral
|
|
25
|
+
Intent landing
|
|
26
|
+
\`\`\`
|
|
27
|
+
|
|
28
|
+
- **Low-confidence primary warning in \`grade.html\`.** Surfaces the v12.9
|
|
29
|
+
\`primary.confidence\` field when it drops under 0.5 — a soft, amber
|
|
30
|
+
callout above the dimensions grid that tells the reader the brand
|
|
31
|
+
colour is a near-tie pick rather than a runaway leader. Renders as
|
|
32
|
+
ordinary inline note, not an error. Stays out of the way when
|
|
33
|
+
confidence is high (the common case).
|
|
34
|
+
|
|
35
|
+
### Why \`stats\`?
|
|
36
|
+
|
|
37
|
+
Every other command writes files. There was no one-line "what's this
|
|
38
|
+
site made of" path for scripting, CI summaries, or a quick sanity
|
|
39
|
+
check. \`stats\` fills that gap without bloat — it's the read-only,
|
|
40
|
+
zero-side-effect entry point.
|
|
41
|
+
|
|
42
|
+
2 new tests (low-confidence note present + absent paths). 398/398 total.
|
|
43
|
+
|
|
3
44
|
## [12.9.0] — 2026-05-11
|
|
4
45
|
|
|
5
46
|
**Extraction quality pass — the core MVP, fixed.**
|
package/bin/design-extract.js
CHANGED
|
@@ -944,6 +944,104 @@ program
|
|
|
944
944
|
}
|
|
945
945
|
});
|
|
946
946
|
|
|
947
|
+
// ── Stats command — fast stdout summary, no files written ──
|
|
948
|
+
program
|
|
949
|
+
.command('stats <url>')
|
|
950
|
+
.description('Print a concise one-screen summary to stdout — grade, primary, fonts, spacing, voice. No files written.')
|
|
951
|
+
.option('-j, --as-json', 'emit machine-readable JSON to stdout instead of pretty text')
|
|
952
|
+
.action(async (url, opts) => {
|
|
953
|
+
if (!url.startsWith('http')) url = `https://${url}`;
|
|
954
|
+
validateUrl(url);
|
|
955
|
+
|
|
956
|
+
// Quiet path for --as-json: no spinner / chrome noise, just data on stdout.
|
|
957
|
+
// (`--json` is already a global program flag; `--as-json` avoids the clash.)
|
|
958
|
+
const wantJson = !!opts.asJson;
|
|
959
|
+
const spinner = wantJson ? null : ora(`Reading ${url}...`).start();
|
|
960
|
+
try {
|
|
961
|
+
const design = await extractDesignLanguage(url);
|
|
962
|
+
const s = design.score || {};
|
|
963
|
+
const primary = design.colors?.primary;
|
|
964
|
+
const families = (design.typography?.families || []).map(f => f?.name || f).filter(Boolean);
|
|
965
|
+
const summary = {
|
|
966
|
+
url,
|
|
967
|
+
title: design.meta?.title,
|
|
968
|
+
grade: s.grade ?? null,
|
|
969
|
+
score: s.overall ?? null,
|
|
970
|
+
primary: primary
|
|
971
|
+
? { hex: primary.hex, count: primary.count, confidence: primary.confidence ?? null }
|
|
972
|
+
: null,
|
|
973
|
+
families: families.slice(0, 3),
|
|
974
|
+
fontFamilyCount: families.length,
|
|
975
|
+
typeScale: (design.typography?.scale || []).length,
|
|
976
|
+
spacingBase: design.spacing?.base ?? null,
|
|
977
|
+
spacingScale: (design.spacing?.scale || []).length,
|
|
978
|
+
radii: (design.borders?.radii || []).length,
|
|
979
|
+
shadows: (design.shadows?.values || []).length,
|
|
980
|
+
colors: (design.colors?.all || []).length,
|
|
981
|
+
wcag: design.accessibility?.score ?? null,
|
|
982
|
+
material: design.materialLanguage?.label,
|
|
983
|
+
library: design.componentLibrary?.library,
|
|
984
|
+
tone: design.voice?.tone,
|
|
985
|
+
stack: design.stack?.framework,
|
|
986
|
+
intent: design.pageIntent?.type,
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
if (wantJson) {
|
|
990
|
+
if (spinner) spinner.stop();
|
|
991
|
+
process.stdout.write(JSON.stringify(summary, null, 2) + '\n');
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
spinner.stop();
|
|
996
|
+
const gradeColor =
|
|
997
|
+
summary.grade === 'A' ? chalk.green
|
|
998
|
+
: summary.grade === 'B' ? chalk.cyan
|
|
999
|
+
: summary.grade === 'C' ? chalk.yellow
|
|
1000
|
+
: summary.grade === 'D' ? chalk.magenta
|
|
1001
|
+
: chalk.red;
|
|
1002
|
+
const confTag = primary && primary.confidence != null
|
|
1003
|
+
? (primary.confidence < 0.5
|
|
1004
|
+
? chalk.yellow(`~${Math.round(primary.confidence * 100)}% conf`)
|
|
1005
|
+
: chalk.gray(`${Math.round(primary.confidence * 100)}% conf`))
|
|
1006
|
+
: '';
|
|
1007
|
+
const line = (label, value) =>
|
|
1008
|
+
` ${chalk.gray(label.padEnd(12))} ${value}`;
|
|
1009
|
+
|
|
1010
|
+
console.log('');
|
|
1011
|
+
console.log(` ${chalk.bold(url)}`);
|
|
1012
|
+
if (summary.title) console.log(` ${chalk.gray(summary.title)}`);
|
|
1013
|
+
console.log('');
|
|
1014
|
+
console.log(line('Grade', `${gradeColor.bold(summary.grade || '—')} ${chalk.gray('·')} ${chalk.bold(String(summary.score ?? '—') + '/100')}`));
|
|
1015
|
+
if (primary) {
|
|
1016
|
+
console.log(line('Primary', `${chalk.bold(primary.hex)} ${chalk.gray('×' + primary.count)} ${confTag}`));
|
|
1017
|
+
} else {
|
|
1018
|
+
console.log(line('Primary', chalk.gray('—')));
|
|
1019
|
+
}
|
|
1020
|
+
if (families.length) {
|
|
1021
|
+
const head = families[0];
|
|
1022
|
+
const body = families[1] || head;
|
|
1023
|
+
const extra = families.length > 2 ? chalk.gray(` +${families.length - 2}`) : '';
|
|
1024
|
+
console.log(line('Fonts', `${head}${body && body !== head ? chalk.gray(' / ') + body : ''}${extra}`));
|
|
1025
|
+
} else {
|
|
1026
|
+
console.log(line('Fonts', chalk.gray('—')));
|
|
1027
|
+
}
|
|
1028
|
+
console.log(line('Type scale', `${summary.typeScale} sizes`));
|
|
1029
|
+
console.log(line('Spacing', `${summary.spacingBase ? `base ${summary.spacingBase}px` : 'no base'} · ${summary.spacingScale} steps`));
|
|
1030
|
+
console.log(line('Shape', `${summary.radii} radii · ${summary.shadows} shadows`));
|
|
1031
|
+
console.log(line('Colours', `${summary.colors} tokens`));
|
|
1032
|
+
console.log(line('WCAG', summary.wcag != null ? `${summary.wcag}%` : chalk.gray('—')));
|
|
1033
|
+
console.log(line('Stack', [summary.stack, summary.library].filter(Boolean).join(' · ') || chalk.gray('—')));
|
|
1034
|
+
console.log(line('Material', summary.material || chalk.gray('—')));
|
|
1035
|
+
console.log(line('Tone', summary.tone || chalk.gray('—')));
|
|
1036
|
+
console.log(line('Intent', summary.intent || chalk.gray('—')));
|
|
1037
|
+
console.log('');
|
|
1038
|
+
} catch (err) {
|
|
1039
|
+
if (spinner) spinner.fail('Stats failed');
|
|
1040
|
+
console.error(chalk.red(`\n ${err.message}\n`));
|
|
1041
|
+
process.exit(1);
|
|
1042
|
+
}
|
|
1043
|
+
});
|
|
1044
|
+
|
|
947
1045
|
// ── Grade command — shareable HTML report card ─────────────
|
|
948
1046
|
program
|
|
949
1047
|
.command('grade <url>')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "designlang",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.10.0",
|
|
4
4
|
"description": "Extract the complete design language from any website and ship it \u2014 clone to a working Next.js starter, guard tokens with a CI drift bot, or browse everything in a local studio. Outputs W3C DTCG tokens, motion tokens, typed anatomy stubs, Tailwind config, and ready-to-paste v0 / Lovable / Cursor / Claude-Artifacts prompts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/formatters/grade.js
CHANGED
|
@@ -124,6 +124,16 @@ export function formatGrade(design, opts = {}) {
|
|
|
124
124
|
const ogTitle = `${host} — Grade ${s.grade}`;
|
|
125
125
|
const ogDesc = `Design system audit by designlang. ${s.overall}/100 across 8 dimensions.`;
|
|
126
126
|
|
|
127
|
+
// Surface a low-confidence warning when the primary detection was uncertain
|
|
128
|
+
// (e.g. monochrome site, near-tie between top brand candidates). The field
|
|
129
|
+
// arrived in v12.9 — this card now exposes it so readers don't take the
|
|
130
|
+
// primary at face value when it's a soft pick.
|
|
131
|
+
const primaryConfidence = design.colors?.primary?.confidence;
|
|
132
|
+
const lowConfidence = typeof primaryConfidence === 'number' && primaryConfidence < 0.5;
|
|
133
|
+
const confidenceNote = lowConfidence
|
|
134
|
+
? `<p class="confidence-note">Primary detection was low-confidence (${Math.round(primaryConfidence * 100)}%). The brand colour may be a soft pick — review the palette below.</p>`
|
|
135
|
+
: '';
|
|
136
|
+
|
|
127
137
|
const dims = DIMENSIONS
|
|
128
138
|
.filter(([k]) => s.scores[k] !== undefined)
|
|
129
139
|
.map(([k, label, blurb]) => {
|
|
@@ -225,6 +235,20 @@ ${headHref ? `<link href="${esc(headHref)}" rel="stylesheet">` : ''}
|
|
|
225
235
|
section > h2 { font-family: var(--display); font-weight: 400; font-size: 32px; margin: 0 0 8px; letter-spacing: -.005em; }
|
|
226
236
|
section > h2 + .lead { color: var(--ink-soft); margin: 0 0 36px; max-width: 60ch; }
|
|
227
237
|
|
|
238
|
+
/* Low-confidence primary callout — only rendered when v12.9's
|
|
239
|
+
primary.confidence drops under 0.5. Soft warning, not an error. */
|
|
240
|
+
.confidence-note {
|
|
241
|
+
margin: -16px 0 32px;
|
|
242
|
+
padding: 12px 16px;
|
|
243
|
+
background: rgba(212, 145, 0, .08);
|
|
244
|
+
border-left: 2px solid #d49100;
|
|
245
|
+
border-radius: 0 4px 4px 0;
|
|
246
|
+
font-size: 14px;
|
|
247
|
+
color: var(--ink-soft);
|
|
248
|
+
max-width: 60ch;
|
|
249
|
+
}
|
|
250
|
+
[data-theme="dark"] .confidence-note { background: rgba(212, 145, 0, .14); }
|
|
251
|
+
|
|
228
252
|
/* — Dimensions grid — */
|
|
229
253
|
.dims { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px 48px; }
|
|
230
254
|
@media (max-width: 640px) { .dims { grid-template-columns: 1fr; gap: 28px; } }
|
|
@@ -313,6 +337,7 @@ ${headHref ? `<link href="${esc(headHref)}" rel="stylesheet">` : ''}
|
|
|
313
337
|
<section>
|
|
314
338
|
<h2>Eight dimensions, scored.</h2>
|
|
315
339
|
<p class="lead">Each dimension is graded against calibrated thresholds drawn from production design systems (Stripe, Linear, Vercel, GitHub, Apple). The number is the headline; the prose underneath is what to do next.</p>
|
|
340
|
+
${confidenceNote}
|
|
316
341
|
<div class="dims">${dims}</div>
|
|
317
342
|
</section>
|
|
318
343
|
|