designlang 7.2.0 → 9.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.
Files changed (90) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +154 -13
  3. package/bin/design-extract.js +94 -1
  4. package/package.json +9 -3
  5. package/src/config.js +2 -0
  6. package/src/crawler.js +55 -6
  7. package/src/drift.js +137 -0
  8. package/src/extractors/accessibility.js +44 -1
  9. package/src/extractors/colors.js +50 -12
  10. package/src/extractors/component-anatomy.js +123 -0
  11. package/src/extractors/motion.js +184 -0
  12. package/src/extractors/scoring.js +49 -30
  13. package/src/extractors/voice.js +96 -0
  14. package/src/formatters/markdown.js +88 -0
  15. package/src/formatters/motion-tokens.js +22 -0
  16. package/src/index.js +14 -0
  17. package/src/lint.js +198 -0
  18. package/src/visual-diff.js +116 -0
  19. package/.github/FUNDING.yml +0 -1
  20. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -62
  21. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  22. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -28
  23. package/.github/og-preview.png +0 -0
  24. package/.github/workflows/manavarya-bot.yml +0 -17
  25. package/chrome-extension/README.md +0 -41
  26. package/chrome-extension/icons/favicon.svg +0 -7
  27. package/chrome-extension/icons/icon-128.png +0 -0
  28. package/chrome-extension/icons/icon-16.png +0 -0
  29. package/chrome-extension/icons/icon-32.png +0 -0
  30. package/chrome-extension/icons/icon-48.png +0 -0
  31. package/chrome-extension/manifest.json +0 -26
  32. package/chrome-extension/popup.html +0 -167
  33. package/chrome-extension/popup.js +0 -59
  34. package/docs/superpowers/plans/2026-04-18-designlang-v7.md +0 -1121
  35. package/docs/superpowers/specs/2026-04-18-designlang-v7-design.md +0 -150
  36. package/docs/superpowers/specs/2026-04-18-website-redesign-design.md +0 -120
  37. package/docs/superpowers/specs/2026-04-19-designlang-v7-1-design.md +0 -111
  38. package/tests/cli.test.js +0 -84
  39. package/tests/cookies.test.js +0 -98
  40. package/tests/extractors.test.js +0 -792
  41. package/tests/formatters.test.js +0 -709
  42. package/tests/interaction-states.test.js +0 -62
  43. package/tests/mcp.test.js +0 -68
  44. package/tests/modern-css.test.js +0 -104
  45. package/tests/routes-reconciliation.test.js +0 -120
  46. package/tests/utils.test.js +0 -413
  47. package/tests/wide-gamut.test.js +0 -90
  48. package/website/.claude/launch.json +0 -11
  49. package/website/AGENTS.md +0 -5
  50. package/website/CLAUDE.md +0 -1
  51. package/website/README.md +0 -36
  52. package/website/app/api/extract/route.js +0 -245
  53. package/website/app/components/A11ySlider.js +0 -369
  54. package/website/app/components/Comparison.js +0 -286
  55. package/website/app/components/CssHealth.js +0 -243
  56. package/website/app/components/Extractor.js +0 -184
  57. package/website/app/components/HeroExtractor.js +0 -455
  58. package/website/app/components/Marginalia.js +0 -3
  59. package/website/app/components/McpSection.js +0 -223
  60. package/website/app/components/PlatformTabs.js +0 -250
  61. package/website/app/components/RegionsComponents.js +0 -429
  62. package/website/app/components/Rule.js +0 -13
  63. package/website/app/components/Specimens.js +0 -237
  64. package/website/app/components/StructuredData.js +0 -144
  65. package/website/app/components/TokenBrowser.js +0 -344
  66. package/website/app/components/token-browser-sample.js +0 -65
  67. package/website/app/globals.css +0 -505
  68. package/website/app/icon.svg +0 -7
  69. package/website/app/layout.js +0 -126
  70. package/website/app/opengraph-image.js +0 -170
  71. package/website/app/page.js +0 -399
  72. package/website/app/robots.js +0 -15
  73. package/website/app/seo-config.js +0 -82
  74. package/website/app/sitemap.js +0 -18
  75. package/website/jsconfig.json +0 -7
  76. package/website/lib/cache.js +0 -73
  77. package/website/lib/rate-limit.js +0 -30
  78. package/website/lib/rate-limit.test.js +0 -55
  79. package/website/lib/specimens.json +0 -86
  80. package/website/lib/token-helpers.js +0 -70
  81. package/website/lib/url-safety.js +0 -103
  82. package/website/lib/url-safety.test.js +0 -116
  83. package/website/lib/zip-files.js +0 -15
  84. package/website/next.config.mjs +0 -15
  85. package/website/package-lock.json +0 -1353
  86. package/website/package.json +0 -19
  87. package/website/public/favicon.svg +0 -7
  88. package/website/public/logo-specimen.svg +0 -76
  89. package/website/public/mark.svg +0 -12
  90. package/website/public/site.webmanifest +0 -13
@@ -1,286 +0,0 @@
1
- import Marginalia from './Marginalia';
2
-
3
- const TOOLS = [
4
- 'designlang',
5
- 'v0',
6
- 'Builder.io Visual Copilot',
7
- 'Style Dictionary',
8
- 'Subframe',
9
- 'Project Wallace',
10
- ];
11
-
12
- // Y = yes, N = no, P = partial
13
- const ROWS = [
14
- {
15
- feature: 'Extracts from a live URL',
16
- cells: ['Y', 'N', 'Y', 'N', 'N', 'Y'],
17
- },
18
- {
19
- feature: 'Emits W3C DTCG tokens',
20
- cells: ['Y', 'N', 'N', 'Y', 'N', 'N'],
21
- },
22
- {
23
- feature: 'Semantic alias layer',
24
- cells: ['Y', 'N', 'N', 'Y', 'P', 'N'],
25
- },
26
- {
27
- feature: 'Multi-platform output (iOS, Android, Flutter)',
28
- cells: ['Y', 'N', 'N', 'Y', 'N', 'N'],
29
- },
30
- {
31
- feature: 'MCP server over stdio',
32
- cells: ['Y', 'N', 'N', 'N', 'N', 'N'],
33
- },
34
- {
35
- feature: 'CSS health audit',
36
- cells: ['Y', 'N', 'N', 'N', 'N', 'Y'],
37
- },
38
- {
39
- feature: 'A11y remediation suggestions',
40
- cells: ['P', 'N', 'P', 'N', 'N', 'P'],
41
- },
42
- {
43
- feature: 'Component cluster detection',
44
- cells: ['P', 'Y', 'Y', 'N', 'Y', 'N'],
45
- },
46
- {
47
- feature: 'Offline / local-only',
48
- cells: ['Y', 'N', 'N', 'Y', 'N', 'N'],
49
- },
50
- {
51
- feature: 'Open source / MIT',
52
- cells: ['Y', 'N', 'N', 'Y', 'N', 'P'],
53
- },
54
- ];
55
-
56
- function Mark({ kind }) {
57
- if (kind === 'Y') {
58
- return (
59
- <svg width="14" height="14" viewBox="0 0 14 14" aria-label="yes" role="img">
60
- <rect x="2" y="2" width="10" height="10" fill="var(--ink)" transform="rotate(45 7 7)" />
61
- </svg>
62
- );
63
- }
64
- if (kind === 'P') {
65
- return (
66
- <svg width="16" height="16" viewBox="0 0 16 16" aria-label="partial" role="img">
67
- <rect
68
- x="3"
69
- y="3"
70
- width="10"
71
- height="10"
72
- fill="var(--accent)"
73
- stroke="var(--ink)"
74
- strokeWidth="1"
75
- transform="rotate(45 8 8)"
76
- />
77
- </svg>
78
- );
79
- }
80
- return (
81
- <span className="mono" style={{ color: 'var(--ink-3)', fontSize: 14 }} aria-label="no">
82
-
83
- </span>
84
- );
85
- }
86
-
87
- export default function Comparison() {
88
- return (
89
- <div>
90
- <div className="with-margin" style={{ marginTop: 'var(--r5)', marginBottom: 'var(--r7)' }}>
91
- <div>
92
- <div className="section-label" style={{ marginBottom: 'var(--r5)' }}>
93
- <span>§08 — Compared</span>
94
- </div>
95
- <h2 className="display" style={{ marginBottom: 'var(--r4)' }}>
96
- Where designlang doesn&apos;t win,<br />
97
- <em style={{ fontStyle: 'italic', color: 'var(--accent)' }}>it says so.</em>
98
- </h2>
99
- <p className="prose" style={{ fontSize: 18, lineHeight: 1.5, maxWidth: '62ch' }}>
100
- We picked five tools doing closely adjacent work. The matrix below is our honest
101
- assessment on 2026-04-18, written by someone who actually uses all six.
102
- </p>
103
- </div>
104
- <Marginalia>
105
- <div>legend</div>
106
- <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 8 }}>
107
- <Mark kind="Y" /> <span>supported</span>
108
- </div>
109
- <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6 }}>
110
- <Mark kind="P" /> <span>partial</span>
111
- </div>
112
- <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6 }}>
113
- <Mark kind="N" /> <span>not supported</span>
114
- </div>
115
- <hr style={{ margin: '12px 0', border: 0, borderTop: '1px solid var(--ink-3)' }} />
116
- <p className="foot">
117
- Open a PR if this matrix is wrong for your tool. We&apos;ll update it or explain
118
- our read.
119
- </p>
120
- </Marginalia>
121
- </div>
122
-
123
- <div style={{ overflowX: 'auto', marginBottom: 'var(--r7)' }}>
124
- <table
125
- style={{
126
- width: '100%',
127
- minWidth: 900,
128
- borderCollapse: 'collapse',
129
- tableLayout: 'fixed',
130
- }}
131
- >
132
- <thead>
133
- <tr>
134
- <th
135
- scope="col"
136
- className="mono"
137
- style={{
138
- textAlign: 'left',
139
- fontSize: 12,
140
- textTransform: 'uppercase',
141
- letterSpacing: '0.12em',
142
- color: 'var(--ink-2)',
143
- padding: '12px 20px 12px 0',
144
- borderBottom: '1px solid var(--ink)',
145
- fontWeight: 400,
146
- width: '28%',
147
- }}
148
- >
149
- Feature
150
- </th>
151
- {TOOLS.map((t, i) => (
152
- <th
153
- key={t}
154
- scope="col"
155
- className="mono"
156
- style={{
157
- textAlign: 'center',
158
- fontSize: 12,
159
- textTransform: 'uppercase',
160
- letterSpacing: '0.08em',
161
- color: i === 0 ? 'var(--ink)' : 'var(--ink-2)',
162
- padding: '12px 8px',
163
- borderBottom: '1px solid var(--ink)',
164
- fontWeight: 400,
165
- }}
166
- >
167
- {t}
168
- </th>
169
- ))}
170
- </tr>
171
- </thead>
172
- <tbody>
173
- {ROWS.map((row, rIdx) => (
174
- <tr key={row.feature}>
175
- <th
176
- scope="row"
177
- style={{
178
- textAlign: 'left',
179
- padding: '14px 20px 14px 0',
180
- fontWeight: 400,
181
- fontSize: 15,
182
- color: 'var(--ink)',
183
- borderBottom: rIdx === ROWS.length - 1 ? 0 : '1px solid var(--paper-3)',
184
- }}
185
- >
186
- {row.feature}
187
- </th>
188
- {row.cells.map((c, i) => (
189
- <td
190
- key={i}
191
- style={{
192
- textAlign: 'center',
193
- padding: '14px 8px',
194
- verticalAlign: 'middle',
195
- borderBottom: rIdx === ROWS.length - 1 ? 0 : '1px solid var(--paper-3)',
196
- }}
197
- >
198
- <span style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
199
- <Mark kind={c} />
200
- </span>
201
- </td>
202
- ))}
203
- </tr>
204
- ))}
205
- </tbody>
206
- </table>
207
- </div>
208
-
209
- <div className="with-margin">
210
- <div>
211
- <h3
212
- className="mono"
213
- style={{
214
- fontSize: 12,
215
- textTransform: 'uppercase',
216
- letterSpacing: '0.14em',
217
- color: 'var(--ink-2)',
218
- marginBottom: 'var(--r4)',
219
- }}
220
- >
221
- What designlang is not
222
- </h3>
223
- <ul
224
- style={{
225
- listStyle: 'none',
226
- padding: 0,
227
- display: 'grid',
228
- gap: 'var(--r3)',
229
- maxWidth: '72ch',
230
- }}
231
- >
232
- <li
233
- style={{
234
- borderLeft: '1px solid var(--ink)',
235
- paddingLeft: 'var(--r4)',
236
- fontSize: 16,
237
- lineHeight: 1.5,
238
- }}
239
- >
240
- <em style={{ fontFamily: 'var(--font-display)', fontStyle: 'italic' }}>
241
- It is not a design-to-code generator.
242
- </em>{' '}
243
- It extracts the <strong>system</strong>, not the components as JSX.
244
- </li>
245
- <li
246
- style={{
247
- borderLeft: '1px solid var(--ink)',
248
- paddingLeft: 'var(--r4)',
249
- fontSize: 16,
250
- lineHeight: 1.5,
251
- }}
252
- >
253
- <em style={{ fontFamily: 'var(--font-display)', fontStyle: 'italic' }}>
254
- It is not a hosted cloud service.
255
- </em>{' '}
256
- The free website extractor is rate-limited; production use should run the CLI or
257
- MCP server locally.
258
- </li>
259
- <li
260
- style={{
261
- borderLeft: '1px solid var(--ink)',
262
- paddingLeft: 'var(--r4)',
263
- fontSize: 16,
264
- lineHeight: 1.5,
265
- }}
266
- >
267
- <em style={{ fontFamily: 'var(--font-display)', fontStyle: 'italic' }}>
268
- It is not a Figma plugin.
269
- </em>{' '}
270
- designlang reads the <strong>rendered DOM</strong>, not the Figma file — the
271
- output is what your users actually see, not what a designer intended.
272
- </li>
273
- </ul>
274
- </div>
275
- <Marginalia>
276
- <div>honesty clause</div>
277
- <p className="foot" style={{ marginTop: 6 }}>
278
- Two partial cells for designlang on purpose — the a11y remediation engine
279
- suggests fixes but won&apos;t rewrite your markup, and component cluster
280
- detection is heuristic, not vision-based.
281
- </p>
282
- </Marginalia>
283
- </div>
284
- </div>
285
- );
286
- }
@@ -1,243 +0,0 @@
1
- // §04 CSS health — mostly static. Static SVG plot, no ResizeObserver needed
2
- // (responsive via 100% width + aspect-ratio). Numbers are the PR B smoke
3
- // sample taken from stripe.com: 14 sheets, 89% unused, 764 !important rules,
4
- // 9,041 duplicate declarations.
5
-
6
- // Synthetic sample shaped from the PR B smoke distribution: a mostly low
7
- // specificity body with a rising-ramp tail — the classic "!important wall"
8
- // silhouette. X = rule order (0..100), Y = collapsed specificity (a*100+b*10+c).
9
- const POINTS = [
10
- [2, 11], [4, 10], [6, 20], [8, 12], [10, 21], [12, 11], [14, 22],
11
- [16, 20], [18, 12], [20, 22], [22, 30], [24, 20], [26, 21], [28, 31],
12
- [30, 22], [32, 30], [34, 31], [36, 22], [38, 32], [40, 30], [42, 40],
13
- [44, 32], [46, 41], [48, 42], [50, 51], [52, 42], [54, 50], [56, 61],
14
- [58, 52], [60, 71], [62, 81], [64, 92], [66, 112], [68, 121], [70, 141],
15
- [72, 161], [74, 172], [76, 191], [78, 202], [80, 221], [82, 232],
16
- [84, 252], [86, 272], [88, 291], [90, 311], [92, 331], [94, 352],
17
- [96, 372], [98, 391], [100, 412],
18
- ];
19
-
20
- const VENDOR_CHIPS = [
21
- ['-webkit-', '183'],
22
- ['-moz-', '41'],
23
- ['-ms-', '7'],
24
- ['-o-', '2'],
25
- ['duplicates', '9,041'],
26
- ];
27
-
28
- const STATS = [
29
- ['89%', 'unused css'],
30
- ['764', '!important rules'],
31
- ['9,041', 'duplicate declarations'],
32
- ['14', 'stylesheets analyzed'],
33
- ];
34
-
35
- import Rule from './Rule';
36
- import Marginalia from './Marginalia';
37
-
38
- function SpecificityPlot() {
39
- // viewBox 100 x 100 (abstract units), axis gutters inside.
40
- const maxY = 500;
41
- const padL = 10;
42
- const padB = 12;
43
- const padT = 4;
44
- const padR = 4;
45
-
46
- return (
47
- <svg
48
- viewBox="0 0 200 125"
49
- preserveAspectRatio="none"
50
- role="img"
51
- aria-label="Specificity scatter plot — rule order vs specificity score"
52
- style={{
53
- width: '100%',
54
- aspectRatio: '16 / 10',
55
- border: '1px solid var(--ink)',
56
- background: 'var(--paper)',
57
- display: 'block',
58
- }}
59
- >
60
- {/* axis lines */}
61
- <line x1={padL} y1={125 - padB} x2={200 - padR} y2={125 - padB} stroke="var(--ink)" strokeWidth="0.5" />
62
- <line x1={padL} y1={padT} x2={padL} y2={125 - padB} stroke="var(--ink)" strokeWidth="0.5" />
63
-
64
- {/* axis labels (mono ~10px visually — SVG units scaled) */}
65
- <text x={padL} y={124} fontFamily="var(--font-mono)" fontSize="3.2" fill="var(--ink-2)" letterSpacing="0.1">
66
- rule order →
67
- </text>
68
- <text
69
- x={padL + 1}
70
- y={padT + 3}
71
- fontFamily="var(--font-mono)"
72
- fontSize="3.2"
73
- fill="var(--ink-2)"
74
- letterSpacing="0.1"
75
- >
76
- ↑ specificity
77
- </text>
78
- <text x={200 - padR - 14} y={124} fontFamily="var(--font-mono)" fontSize="3" fill="var(--ink-3)">
79
- n=50
80
- </text>
81
-
82
- {/* points */}
83
- {POINTS.map(([x, y], i) => {
84
- const px = padL + (x / 100) * (200 - padL - padR);
85
- const py = 125 - padB - (Math.min(y, maxY) / maxY) * (125 - padB - padT);
86
- const outlier = y > 200;
87
- return (
88
- <rect
89
- key={i}
90
- x={px - 1}
91
- y={py - 1}
92
- width="2"
93
- height="2"
94
- fill={outlier ? 'var(--accent)' : 'var(--ink)'}
95
- />
96
- );
97
- })}
98
- </svg>
99
- );
100
- }
101
-
102
- export default function CssHealth() {
103
- return (
104
- <>
105
- <Rule number="04" label="CSS health audit" />
106
- <div className="with-margin" style={{ marginTop: 'var(--r5)' }}>
107
- <div>
108
- <div className="eyebrow" style={{ marginBottom: 'var(--r3)' }}>§04 CSS health</div>
109
- <h2 className="display" style={{ marginBottom: 'var(--r4)' }}>
110
- The stylesheet is the problem.
111
- </h2>
112
- <p className="prose" style={{ fontSize: 18, maxWidth: '62ch', color: 'var(--ink-2)' }}>
113
- Most sites ship 40–90% unused CSS, long walls of <code>!important</code> escalations,
114
- and a specificity graph that rises forever. v7.0 surfaces all of it — not as a
115
- vanity score, but as exact declaration-level evidence.
116
- </p>
117
-
118
- <div
119
- className="grid-12"
120
- style={{
121
- marginTop: 'var(--r7)',
122
- gap: 'var(--col-gap)',
123
- alignItems: 'start',
124
- }}
125
- >
126
- {/* Big numerals — span 5 */}
127
- <div
128
- style={{
129
- gridColumn: 'span 5',
130
- display: 'grid',
131
- gridTemplateColumns: '1fr 1fr',
132
- gap: 'var(--r5) var(--r4)',
133
- }}
134
- >
135
- {STATS.map(([n, label]) => (
136
- <div key={label} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
137
- <div
138
- className="display"
139
- style={{
140
- fontSize: 'clamp(64px, 7vw, 120px)',
141
- lineHeight: 0.95,
142
- fontVariantNumeric: 'tabular-nums',
143
- fontFeatureSettings: "'tnum' 1",
144
- letterSpacing: '-0.04em',
145
- }}
146
- >
147
- {n}
148
- </div>
149
- <div
150
- className="mono"
151
- style={{
152
- fontSize: 11,
153
- letterSpacing: '0.14em',
154
- textTransform: 'uppercase',
155
- color: 'var(--ink-2)',
156
- }}
157
- >
158
- {label}
159
- </div>
160
- </div>
161
- ))}
162
- </div>
163
-
164
- {/* Plot — span 7 */}
165
- <div style={{ gridColumn: 'span 7' }}>
166
- <div
167
- className="mono"
168
- style={{
169
- fontSize: 11,
170
- letterSpacing: '0.14em',
171
- textTransform: 'uppercase',
172
- color: 'var(--ink-2)',
173
- marginBottom: 'var(--r3)',
174
- display: 'flex',
175
- justifyContent: 'space-between',
176
- }}
177
- >
178
- <span>specificity distribution</span>
179
- <span style={{ color: 'var(--accent)' }}>outliers: specificity &gt; 200</span>
180
- </div>
181
- <SpecificityPlot />
182
- <div
183
- className="mono"
184
- style={{
185
- fontSize: 11,
186
- color: 'var(--ink-3)',
187
- marginTop: 'var(--r3)',
188
- }}
189
- >
190
- collapsed score: a×100 + b×10 + c. rising tail = accumulated !important wall.
191
- </div>
192
- </div>
193
- </div>
194
-
195
- {/* Caption strip — vendor prefix chips */}
196
- <div
197
- style={{
198
- marginTop: 'var(--r7)',
199
- display: 'flex',
200
- flexWrap: 'wrap',
201
- gap: 'var(--r3)',
202
- borderTop: 'var(--hair)',
203
- paddingTop: 'var(--r4)',
204
- }}
205
- >
206
- {VENDOR_CHIPS.map(([k, v]) => (
207
- <span
208
- key={k}
209
- className="mono"
210
- style={{
211
- display: 'inline-flex',
212
- alignItems: 'baseline',
213
- gap: 8,
214
- padding: '3px 8px',
215
- border: '1px solid var(--ink)',
216
- fontSize: 11,
217
- letterSpacing: '0.04em',
218
- }}
219
- >
220
- <span style={{ color: 'var(--ink-2)' }}>{k}</span>
221
- <span style={{ fontVariantNumeric: 'tabular-nums' }}>{v}</span>
222
- </span>
223
- ))}
224
- </div>
225
- </div>
226
-
227
- <Marginalia>
228
- <div>additive scoring</div>
229
- <p style={{ marginTop: 6 }}>
230
- Tracked additively: existing score fields kept for back-compat; CSS health joins as a new
231
- dimension.
232
- </p>
233
- <hr style={{ margin: '12px 0', border: 0, borderTop: '1px solid var(--ink-3)' }} />
234
- <div>works on any site</div>
235
- <p className="foot" style={{ marginTop: 6 }}>
236
- Flags rising specificity, zombie declarations, and abandoned <code>!important</code> walls —
237
- with exact selector provenance.
238
- </p>
239
- </Marginalia>
240
- </div>
241
- </>
242
- );
243
- }