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,505 +0,0 @@
1
- /* ─────────────────────────────────────────────────────────────
2
- designlang website — foundation
3
- Direction: Live Design DNA. Paper-first, one accent.
4
- ───────────────────────────────────────────────────────────── */
5
-
6
- :root {
7
- /* Palette */
8
- --paper: #f3f1ea;
9
- --paper-2: #ece8dd;
10
- --paper-3: #d8d3c5;
11
- --ink: #0a0908;
12
- --ink-2: #403c34;
13
- --ink-3: #8b8778;
14
- --accent: #ff4800;
15
-
16
- /* Type */
17
- --font-body: var(--font-body), -apple-system, 'Segoe UI', system-ui, sans-serif;
18
- --font-mono: var(--font-mono), 'SF Mono', ui-monospace, Menlo, monospace;
19
- --font-display: var(--font-display), Georgia, serif;
20
-
21
- /* Rhythm */
22
- --r1: 4px;
23
- --r2: 8px;
24
- --r3: 12px;
25
- --r4: 16px;
26
- --r5: 24px;
27
- --r6: 32px;
28
- --r7: 48px;
29
- --r8: 64px;
30
- --r9: 96px;
31
- --r10: 144px;
32
-
33
- /* Grid */
34
- --page-max: 1440px;
35
- --page-pad-x: clamp(20px, 4vw, 56px);
36
- --col-count: 12;
37
- --col-gap: 24px;
38
-
39
- /* Hairline */
40
- --hair: 1px solid var(--ink);
41
- --hair-soft: 1px solid var(--ink-3);
42
- }
43
-
44
- * {
45
- margin: 0;
46
- padding: 0;
47
- box-sizing: border-box;
48
- }
49
-
50
- html {
51
- background: var(--paper);
52
- color: var(--ink);
53
- color-scheme: light;
54
- font-family: var(--font-body);
55
- font-size: 16px;
56
- line-height: 1.45;
57
- -webkit-font-smoothing: antialiased;
58
- text-rendering: optimizeLegibility;
59
- scroll-behavior: smooth;
60
- }
61
-
62
- body {
63
- background: var(--paper);
64
- min-height: 100dvh;
65
- overflow-x: hidden;
66
- }
67
-
68
- a {
69
- color: inherit;
70
- text-decoration: none;
71
- border-bottom: 1px solid currentColor;
72
- transition: color 120ms ease, border-color 120ms ease;
73
- }
74
-
75
- a:hover {
76
- color: var(--accent);
77
- }
78
-
79
- button {
80
- font: inherit;
81
- color: inherit;
82
- background: none;
83
- border: 0;
84
- cursor: pointer;
85
- }
86
-
87
- input, textarea, select {
88
- font: inherit;
89
- color: inherit;
90
- background: transparent;
91
- border: 0;
92
- outline: 0;
93
- }
94
-
95
- ::selection {
96
- background: var(--ink);
97
- color: var(--paper);
98
- }
99
-
100
- img, svg { display: block; max-width: 100%; }
101
-
102
- /* ── Typography ─────────────────────────────────────────────── */
103
-
104
- .display {
105
- font-family: var(--font-display);
106
- font-weight: 400;
107
- letter-spacing: -0.03em;
108
- line-height: 0.92;
109
- font-variation-settings: 'opsz' 144, 'SOFT' 30;
110
- }
111
-
112
- .mono {
113
- font-family: var(--font-mono);
114
- font-feature-settings: 'zero' 1, 'ss01' 1;
115
- }
116
-
117
- .eyebrow {
118
- font-family: var(--font-mono);
119
- font-size: 11px;
120
- letter-spacing: 0.14em;
121
- text-transform: uppercase;
122
- color: var(--ink-2);
123
- }
124
-
125
- .section-label {
126
- font-family: var(--font-mono);
127
- font-size: 12px;
128
- letter-spacing: 0.14em;
129
- text-transform: uppercase;
130
- color: var(--ink-2);
131
- display: inline-flex;
132
- align-items: baseline;
133
- gap: 10px;
134
- }
135
-
136
- .section-label::before {
137
- content: '';
138
- width: 24px;
139
- height: 1px;
140
- background: var(--ink);
141
- display: inline-block;
142
- transform: translateY(-4px);
143
- }
144
-
145
- .prose p { max-width: 62ch; }
146
- .prose p + p { margin-top: 1em; }
147
-
148
- /* ── Layout primitives ──────────────────────────────────────── */
149
-
150
- .page {
151
- max-width: var(--page-max);
152
- margin: 0 auto;
153
- padding-inline: var(--page-pad-x);
154
- }
155
-
156
- .stack-lg > * + * { margin-top: var(--r8); }
157
- .stack-md > * + * { margin-top: var(--r5); }
158
- .stack-sm > * + * { margin-top: var(--r3); }
159
-
160
- .grid-12 {
161
- display: grid;
162
- grid-template-columns: repeat(var(--col-count), minmax(0, 1fr));
163
- gap: var(--col-gap);
164
- }
165
-
166
- /* Rule — a hairline with optional section label inline at left edge. */
167
- .rule {
168
- position: relative;
169
- display: flex;
170
- align-items: baseline;
171
- gap: var(--r4);
172
- padding-block: var(--r4);
173
- }
174
-
175
- .rule-line {
176
- flex: 1 1 auto;
177
- height: 1px;
178
- background: var(--ink);
179
- }
180
-
181
- .rule-label {
182
- font-family: var(--font-mono);
183
- font-size: 11px;
184
- letter-spacing: 0.14em;
185
- text-transform: uppercase;
186
- color: var(--ink);
187
- white-space: nowrap;
188
- }
189
-
190
- .rule-number {
191
- color: var(--ink-3);
192
- margin-right: var(--r2);
193
- }
194
-
195
- /* Marginalia — side-column notes; collapse to block on small screens. */
196
- .with-margin {
197
- display: grid;
198
- grid-template-columns: minmax(0, 1fr) minmax(180px, 240px);
199
- gap: var(--col-gap);
200
- align-items: start;
201
- }
202
-
203
- .marginalia {
204
- font-family: var(--font-mono);
205
- font-size: 12px;
206
- line-height: 1.5;
207
- color: var(--ink-2);
208
- border-left: var(--hair);
209
- padding-left: var(--r4);
210
- }
211
-
212
- .marginalia .foot { color: var(--ink-3); }
213
- .marginalia code { color: var(--ink); }
214
-
215
- @media (max-width: 860px) {
216
- .with-margin {
217
- grid-template-columns: 1fr;
218
- }
219
- .marginalia {
220
- border-left: 0;
221
- border-top: var(--hair);
222
- padding-left: 0;
223
- padding-top: var(--r3);
224
- }
225
- }
226
-
227
- /* ── Controls ───────────────────────────────────────────────── */
228
-
229
- .cta {
230
- display: inline-flex;
231
- align-items: center;
232
- gap: 10px;
233
- padding: 14px 22px 15px;
234
- background: var(--ink);
235
- color: var(--paper);
236
- border: var(--hair);
237
- box-shadow: 6px 6px 0 var(--accent);
238
- font-family: var(--font-mono);
239
- font-size: 13px;
240
- letter-spacing: 0.04em;
241
- transition: transform 120ms ease, box-shadow 120ms ease;
242
- }
243
-
244
- .cta:hover {
245
- transform: translate(-2px, -2px);
246
- box-shadow: 8px 8px 0 var(--accent);
247
- color: var(--paper);
248
- }
249
-
250
- .cta:active {
251
- transform: translate(4px, 4px);
252
- box-shadow: 2px 2px 0 var(--accent);
253
- }
254
-
255
- /* Chip — used for small tag/label pieces. */
256
- .chip {
257
- display: inline-flex;
258
- align-items: center;
259
- gap: 6px;
260
- padding: 3px 8px 4px;
261
- border: var(--hair);
262
- font-family: var(--font-mono);
263
- font-size: 11px;
264
- letter-spacing: 0.04em;
265
- background: var(--paper);
266
- color: var(--ink);
267
- }
268
-
269
- /* ── Accessibility ──────────────────────────────────────────── */
270
-
271
- :focus-visible {
272
- outline: 2px solid var(--accent);
273
- outline-offset: 3px;
274
- }
275
-
276
- @media (prefers-reduced-motion: reduce) {
277
- *,
278
- *::before,
279
- *::after {
280
- animation-duration: 0s !important;
281
- animation-iteration-count: 1 !important;
282
- transition-duration: 0s !important;
283
- scroll-behavior: auto !important;
284
- }
285
- }
286
-
287
- /* ── Section scaffold ───────────────────────────────────────── */
288
-
289
- section {
290
- padding-block: var(--r9);
291
- }
292
-
293
- section + section {
294
- border-top: var(--hair);
295
- }
296
-
297
- section[id] {
298
- scroll-margin-top: var(--r6);
299
- }
300
-
301
- h1, h2, h3, h4, h5, h6 { font-weight: 400; }
302
-
303
- h2.display {
304
- font-size: clamp(36px, 6vw, 88px);
305
- }
306
-
307
- h3.display {
308
- font-size: clamp(26px, 3.5vw, 48px);
309
- }
310
-
311
- .hero-title {
312
- font-size: clamp(48px, 11vw, 200px);
313
- }
314
-
315
- /* ── Mobile & tablet pass ───────────────────────────────────── */
316
-
317
- @media (max-width: 860px) {
318
- :root {
319
- --page-pad-x: 18px;
320
- --col-gap: 14px;
321
- }
322
-
323
- section {
324
- padding-block: var(--r7);
325
- }
326
-
327
- .display,
328
- .hero-title,
329
- h1,
330
- h2,
331
- h3 {
332
- overflow-wrap: anywhere;
333
- word-break: break-word;
334
- hyphens: auto;
335
- letter-spacing: -0.02em;
336
- }
337
-
338
- .hero-title {
339
- font-size: clamp(44px, 13vw, 96px);
340
- line-height: 0.95;
341
- }
342
-
343
- h2.display {
344
- font-size: clamp(32px, 8vw, 56px);
345
- }
346
-
347
- h3.display {
348
- font-size: clamp(22px, 6vw, 36px);
349
- }
350
-
351
- .prose p,
352
- .prose {
353
- font-size: 16px !important;
354
- }
355
-
356
- /* Header nav: horizontal scroll instead of overflow. */
357
- header nav {
358
- overflow-x: auto;
359
- overflow-y: hidden;
360
- scrollbar-width: none;
361
- white-space: nowrap;
362
- gap: var(--r4) !important;
363
- font-size: 11px !important;
364
- max-width: 60%;
365
- }
366
-
367
- header nav::-webkit-scrollbar {
368
- display: none;
369
- }
370
-
371
- /* Hero form: stack input over button, no border on button side. */
372
- #extract form {
373
- flex-direction: column;
374
- max-width: 100% !important;
375
- }
376
-
377
- #extract form input {
378
- border-right: 0 !important;
379
- border-bottom: var(--hair);
380
- padding: 14px 16px !important;
381
- font-size: 15px !important;
382
- }
383
-
384
- #extract form button {
385
- width: 100%;
386
- justify-content: space-between;
387
- }
388
-
389
- /* Tables, code blocks, long scroll strips: constrain inside page, scroll on x. */
390
- pre,
391
- table,
392
- .scroll-x {
393
- max-width: 100%;
394
- overflow-x: auto;
395
- -webkit-overflow-scrolling: touch;
396
- }
397
-
398
- pre {
399
- font-size: 12px !important;
400
- padding: var(--r3) var(--r4) !important;
401
- }
402
-
403
- /* Grid-12 defaults: drop to single column on mobile unless overridden. */
404
- .grid-12 {
405
- grid-template-columns: minmax(0, 1fr) !important;
406
- gap: var(--r4) !important;
407
- }
408
-
409
- /* Min-width: 0 on every grid child so content (like a 640-unit SVG) can't
410
- push a 1fr track wider than the viewport. */
411
- .grid-12 > *,
412
- .with-margin > *,
413
- [style*='grid-template-columns'] > * {
414
- min-width: 0;
415
- }
416
-
417
- /* Everything absolutely should not push the page sideways. */
418
- body,
419
- main.page {
420
- max-width: 100vw;
421
- overflow-x: clip;
422
- }
423
-
424
- /* Constrain every SVG, image, and video so fixed-width art can't spill. */
425
- section svg,
426
- section img,
427
- section video,
428
- section canvas {
429
- max-width: 100%;
430
- height: auto;
431
- }
432
-
433
- /* Any element that opts into `.scroll-x` or `.tabstrip` scrolls horizontally
434
- inside its section on narrow viewports. */
435
- .scroll-x,
436
- .tabstrip,
437
- [role='tablist'] {
438
- overflow-x: auto;
439
- -webkit-overflow-scrolling: touch;
440
- scrollbar-width: thin;
441
- }
442
-
443
- /* Token browser: let it scroll-x on tiny viewports. */
444
- section [data-token-browser],
445
- section [data-regions-schematic],
446
- section [data-platform-code],
447
- section [data-specimens-strip] {
448
- overflow-x: auto;
449
- -webkit-overflow-scrolling: touch;
450
- }
451
-
452
- /* Specimen cards / wide cards: the strip itself scrolls; cards keep their
453
- design width. */
454
- [data-specimens-strip] > * {
455
- flex: 0 0 auto;
456
- }
457
-
458
- /* Any container with a fixed min-width: safe max-width safety net. */
459
- main.page > * {
460
- max-width: 100%;
461
- }
462
-
463
- /* Inline-style grid overrides — components use style={{gridTemplateColumns:…}}
464
- so we match the resulting attribute and force single-column on mobile. */
465
- [style*="grid-template-columns: repeat(4"],
466
- [style*="grid-template-columns: repeat(3"],
467
- [style*="grid-template-columns:5fr"],
468
- [style*="grid-template-columns: 5fr"],
469
- [style*="grid-template-columns:100px"],
470
- [style*="grid-template-columns: 100px"] {
471
- grid-template-columns: 1fr !important;
472
- }
473
-
474
- /* Specimens strip: horizontal scroll with snap. Cards stay at their
475
- design-time width; the parent scrolls. */
476
- [style*="scroll-snap-type"] {
477
- padding-inline: 0 !important;
478
- }
479
-
480
- [style*="scroll-snap-type"] > * {
481
- min-width: 80vw;
482
- max-width: 92vw;
483
- }
484
- }
485
-
486
- @media (max-width: 480px) {
487
- .hero-title {
488
- font-size: clamp(40px, 12vw, 72px);
489
- }
490
-
491
- header nav {
492
- max-width: 55%;
493
- gap: 14px !important;
494
- font-size: 10px !important;
495
- letter-spacing: 0.08em !important;
496
- }
497
-
498
- .section-label {
499
- font-size: 11px !important;
500
- }
501
-
502
- pre {
503
- font-size: 11px !important;
504
- }
505
- }
@@ -1,7 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img" aria-label="designlang">
2
- <title>designlang</title>
3
- <rect width="32" height="32" fill="#F3F1EA"/>
4
- <circle cx="13" cy="20" r="7.5" fill="none" stroke="#0A0908" stroke-width="4.5"/>
5
- <rect x="19" y="4" width="4.5" height="25" fill="#0A0908"/>
6
- <rect x="25" y="25" width="4" height="4" fill="#FF4800"/>
7
- </svg>
@@ -1,126 +0,0 @@
1
- import { Fraunces, Instrument_Sans, JetBrains_Mono } from 'next/font/google';
2
- import StructuredData from './components/StructuredData';
3
- import {
4
- SITE_URL,
5
- SITE_NAME,
6
- SITE_TITLE,
7
- SITE_DESCRIPTION,
8
- SITE_KEYWORDS,
9
- } from './seo-config';
10
- import './globals.css';
11
-
12
- const fraunces = Fraunces({
13
- subsets: ['latin'],
14
- variable: '--font-display',
15
- display: 'swap',
16
- });
17
-
18
- const instrumentSans = Instrument_Sans({
19
- subsets: ['latin'],
20
- variable: '--font-body',
21
- display: 'swap',
22
- });
23
-
24
- const mono = JetBrains_Mono({
25
- subsets: ['latin'],
26
- variable: '--font-mono',
27
- display: 'swap',
28
- });
29
-
30
- export const metadata = {
31
- metadataBase: new URL(SITE_URL),
32
- title: {
33
- default: SITE_TITLE,
34
- template: '%s \u2014 designlang',
35
- },
36
- description: SITE_DESCRIPTION,
37
- keywords: SITE_KEYWORDS,
38
- authors: [{ name: 'Manav Arya Singh', url: 'https://manavaryasingh.com' }],
39
- creator: 'Manav Arya Singh',
40
- publisher: 'Manav Arya Singh',
41
- applicationName: SITE_NAME,
42
- category: 'developer tools',
43
- classification:
44
- 'design system extractor, design tokens, design-to-code, AI coding agents, MCP server',
45
- generator: 'Next.js',
46
- alternates: {
47
- canonical: SITE_URL,
48
- },
49
- openGraph: {
50
- type: 'website',
51
- url: SITE_URL,
52
- siteName: SITE_NAME,
53
- title: SITE_TITLE,
54
- description: SITE_DESCRIPTION,
55
- locale: 'en_US',
56
- images: [
57
- {
58
- url: '/opengraph-image',
59
- width: 1200,
60
- height: 630,
61
- alt:
62
- 'designlang specimen card \u2014 a lowercase d mark with one extracted orange token, the wordmark designlang in Fraunces, and a five-swatch palette strip.',
63
- type: 'image/png',
64
- },
65
- ],
66
- },
67
- twitter: {
68
- card: 'summary_large_image',
69
- title: SITE_TITLE,
70
- description: SITE_DESCRIPTION,
71
- images: ['/opengraph-image'],
72
- creator: '@manavaryasingh',
73
- site: '@manavaryasingh',
74
- },
75
- robots: {
76
- index: true,
77
- follow: true,
78
- nocache: false,
79
- googleBot: {
80
- index: true,
81
- follow: true,
82
- 'max-image-preview': 'large',
83
- 'max-snippet': -1,
84
- 'max-video-preview': -1,
85
- },
86
- },
87
- icons: {
88
- icon: [
89
- { url: '/icon.svg', type: 'image/svg+xml' },
90
- { url: '/favicon.svg', type: 'image/svg+xml' },
91
- ],
92
- shortcut: '/icon.svg',
93
- apple: '/icon.svg',
94
- },
95
- manifest: '/site.webmanifest',
96
- formatDetection: {
97
- email: false,
98
- address: false,
99
- telephone: false,
100
- },
101
- };
102
-
103
- export const viewport = {
104
- themeColor: '#F3F1EA',
105
- width: 'device-width',
106
- initialScale: 1,
107
- colorScheme: 'light',
108
- };
109
-
110
- export default function RootLayout({ children }) {
111
- return (
112
- <html
113
- lang="en"
114
- className={`${fraunces.variable} ${instrumentSans.variable} ${mono.variable}`}
115
- >
116
- <head>
117
- <link rel="preconnect" href="https://fonts.googleapis.com" />
118
- <link rel="dns-prefetch" href="https://fonts.gstatic.com" />
119
- <link rel="author" href="https://manavaryasingh.com" />
120
- <link rel="me" href="https://github.com/Manavarya09" />
121
- <StructuredData />
122
- </head>
123
- <body>{children}</body>
124
- </html>
125
- );
126
- }