@motion-proto/live-tokens 0.91.2 → 0.93.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 (49) hide show
  1. package/.claude/skills/live-tokens-create-page/SKILL.md +10 -3
  2. package/.claude/skills/live-tokens-set-type/SKILL.md +3 -3
  3. package/CHANGELOG.md +171 -0
  4. package/bin/lib/tokenVocabulary.mjs +2 -2
  5. package/bin/rules/testRuns.mjs +1 -1
  6. package/bin/rules/tokens.mjs +3 -3
  7. package/dist-plugin/chunk-7XSUHJUH.js +1652 -0
  8. package/dist-plugin/index.cjs +950 -15
  9. package/dist-plugin/index.js +1 -1
  10. package/dist-plugin/setType/index.cjs +23 -2
  11. package/dist-plugin/setType/index.js +23 -2
  12. package/dist-plugin/tokensCssMigrations/index.cjs +954 -15
  13. package/dist-plugin/tokensCssMigrations/index.d.cts +23 -3
  14. package/dist-plugin/tokensCssMigrations/index.d.ts +23 -3
  15. package/dist-plugin/tokensCssMigrations/index.js +5 -1
  16. package/package.json +1 -1
  17. package/src/app/site.css +243 -9
  18. package/src/demo/TestingLoops.svelte +9 -9
  19. package/src/editor/core/fonts/weightCoverage.ts +31 -2
  20. package/src/editor/core/palettes/tokenRegistry.ts +36 -37
  21. package/src/editor/core/store/editorKeybindings.ts +1 -1
  22. package/src/editor/core/themes/parsers/mediaBlocks.ts +46 -0
  23. package/src/editor/core/typeScale/typeScale.ts +341 -0
  24. package/src/editor/skill-atlas/SourcePane.svelte +3 -3
  25. package/src/editor/skill-atlas/skillSources.generated.ts +2 -2
  26. package/src/editor/skill-atlas/trees/create-page.ts +14 -14
  27. package/src/editor/skill-atlas/trees/set-type.ts +2 -2
  28. package/src/editor/ui/UIFontFamilySelector.svelte +5 -1
  29. package/src/editor/ui/UIFontWeightSelector.svelte +4 -0
  30. package/src/editor/ui/UILetterSpacingSelector.svelte +4 -0
  31. package/src/editor/ui/UILineHeightSelector.svelte +4 -0
  32. package/src/editor/ui/UITextTransformSelector.svelte +4 -0
  33. package/src/editor/ui/UIVariantSelector.svelte +6 -8
  34. package/src/editor/ui/VariablesTab.svelte +4 -4
  35. package/src/editor/ui/colors/ColorsTab.svelte +99 -5
  36. package/src/editor/ui/colors/paletteBaseColor.ts +17 -0
  37. package/src/editor/ui/sections/textStyles.ts +44 -92
  38. package/src/editor/ui/text-styles/ControlBody.svelte +100 -0
  39. package/src/editor/ui/text-styles/ResetIcon.svelte +60 -0
  40. package/src/editor/ui/text-styles/StepRow.svelte +330 -0
  41. package/src/editor/ui/text-styles/StyleControl.svelte +128 -0
  42. package/src/editor/ui/text-styles/TextStylesSection.svelte +551 -0
  43. package/src/editor/ui/text-styles/textStyleEdits.ts +308 -0
  44. package/src/editor/ui/variantScales.ts +16 -0
  45. package/src/system/styles/tokens.css +412 -48
  46. package/src/testing-js/page-compliance.contract.js +233 -80
  47. package/src/testing-js/page-compliance.contract.js.map +1 -1
  48. package/dist-plugin/chunk-GWMEYBZ2.js +0 -715
  49. package/src/editor/ui/TextStylesSection.svelte +0 -171
@@ -47,12 +47,16 @@ interface TokensCssMigration {
47
47
  * sectioning, and formatting survive untouched. They are idempotent **by
48
48
  * presence**: `ensure` skips a token that already exists, `rename` only fires
49
49
  * when the old name is present and the new one is absent, `remove` is a no-op
50
- * when the name is gone. Re-running a migration therefore never double-applies,
50
+ * when the name is gone, `set` writes nothing when the value already matches,
51
+ * and `appendMediaBlock` skips a name the block declares. Re-running a migration therefore never double-applies,
51
52
  * which is what lets the CLI run safely without a schema-version stamp.
52
53
  */
53
54
  /** Names declared as custom properties (`--x: …;`) anywhere in the source. */
54
55
  declare function collectDefinedTokens(css: string): Set<string>;
55
- /** Declared name → value, last declaration winning (as the cascade would). */
56
+ /**
57
+ * Declared name → top-level value, last declaration winning. Declarations
58
+ * inside `@media` are breakpoint re-points and never count.
59
+ */
56
60
  declare function collectTokenValues(css: string): Map<string, string>;
57
61
  /** Names referenced via `var(--x)` anywhere in the source. */
58
62
  declare function collectReferencedTokens(css: string): Set<string>;
@@ -96,6 +100,22 @@ declare function removeToken(css: string, name: string): string;
96
100
  * No-op when nothing matches. Use for retiring a whole retired namespace.
97
101
  */
98
102
  declare function removeTokensMatching(css: string, predicate: (name: string) => boolean): string;
103
+ interface SetTokenValueOptions {
104
+ /** Rewrite only when the current value equals this one, so a consumer's edit stays. */
105
+ from?: string;
106
+ }
107
+ /**
108
+ * Rewrite the value of `name` where the top-level `:root` declares it. A
109
+ * re-declaration inside `@media` keeps its value. No-op when the top-level
110
+ * `:root` does not declare `name`.
111
+ */
112
+ declare function setTokenValue(css: string, name: string, value: string, opts?: SetTokenValueOptions): string;
113
+ /**
114
+ * Append each entry that no top-level `@media <query>` block declares yet to
115
+ * that block's `:root`. The query must match exactly. With no such block, a new
116
+ * one goes at the end of the file.
117
+ */
118
+ declare function appendMediaBlock(css: string, query: string, entries: ScaleEntry[]): string;
99
119
 
100
120
  /**
101
121
  * Registered tokens-css migrations, applied in array order. Unlike the
@@ -194,4 +214,4 @@ interface ValidateInput {
194
214
  */
195
215
  declare function validateTokensCss(input: ValidateInput): MissingToken[];
196
216
 
197
- export { type BreakingGateResult, type ComponentSource, type ContractViolation, type MissingToken, type RunResult, type SemverBump, TOKENS_CSS_MIGRATIONS, type TokensCssMigration, type ValidateInput, collectDefinedTokens, collectReferencedTokens, collectTokenValues, enforceBreakingRequiresMajor, ensureScale, findContractViolations, removeToken, removeTokensMatching, renameToken, runAdditiveTokensCssMigrations, runTokensCssMigrations, semverBumpType, validateTokensCss };
217
+ export { type BreakingGateResult, type ComponentSource, type ContractViolation, type MissingToken, type RunResult, type SemverBump, TOKENS_CSS_MIGRATIONS, type TokensCssMigration, type ValidateInput, appendMediaBlock, collectDefinedTokens, collectReferencedTokens, collectTokenValues, enforceBreakingRequiresMajor, ensureScale, findContractViolations, removeToken, removeTokensMatching, renameToken, runAdditiveTokensCssMigrations, runTokensCssMigrations, semverBumpType, setTokenValue, validateTokensCss };
@@ -47,12 +47,16 @@ interface TokensCssMigration {
47
47
  * sectioning, and formatting survive untouched. They are idempotent **by
48
48
  * presence**: `ensure` skips a token that already exists, `rename` only fires
49
49
  * when the old name is present and the new one is absent, `remove` is a no-op
50
- * when the name is gone. Re-running a migration therefore never double-applies,
50
+ * when the name is gone, `set` writes nothing when the value already matches,
51
+ * and `appendMediaBlock` skips a name the block declares. Re-running a migration therefore never double-applies,
51
52
  * which is what lets the CLI run safely without a schema-version stamp.
52
53
  */
53
54
  /** Names declared as custom properties (`--x: …;`) anywhere in the source. */
54
55
  declare function collectDefinedTokens(css: string): Set<string>;
55
- /** Declared name → value, last declaration winning (as the cascade would). */
56
+ /**
57
+ * Declared name → top-level value, last declaration winning. Declarations
58
+ * inside `@media` are breakpoint re-points and never count.
59
+ */
56
60
  declare function collectTokenValues(css: string): Map<string, string>;
57
61
  /** Names referenced via `var(--x)` anywhere in the source. */
58
62
  declare function collectReferencedTokens(css: string): Set<string>;
@@ -96,6 +100,22 @@ declare function removeToken(css: string, name: string): string;
96
100
  * No-op when nothing matches. Use for retiring a whole retired namespace.
97
101
  */
98
102
  declare function removeTokensMatching(css: string, predicate: (name: string) => boolean): string;
103
+ interface SetTokenValueOptions {
104
+ /** Rewrite only when the current value equals this one, so a consumer's edit stays. */
105
+ from?: string;
106
+ }
107
+ /**
108
+ * Rewrite the value of `name` where the top-level `:root` declares it. A
109
+ * re-declaration inside `@media` keeps its value. No-op when the top-level
110
+ * `:root` does not declare `name`.
111
+ */
112
+ declare function setTokenValue(css: string, name: string, value: string, opts?: SetTokenValueOptions): string;
113
+ /**
114
+ * Append each entry that no top-level `@media <query>` block declares yet to
115
+ * that block's `:root`. The query must match exactly. With no such block, a new
116
+ * one goes at the end of the file.
117
+ */
118
+ declare function appendMediaBlock(css: string, query: string, entries: ScaleEntry[]): string;
99
119
 
100
120
  /**
101
121
  * Registered tokens-css migrations, applied in array order. Unlike the
@@ -194,4 +214,4 @@ interface ValidateInput {
194
214
  */
195
215
  declare function validateTokensCss(input: ValidateInput): MissingToken[];
196
216
 
197
- export { type BreakingGateResult, type ComponentSource, type ContractViolation, type MissingToken, type RunResult, type SemverBump, TOKENS_CSS_MIGRATIONS, type TokensCssMigration, type ValidateInput, collectDefinedTokens, collectReferencedTokens, collectTokenValues, enforceBreakingRequiresMajor, ensureScale, findContractViolations, removeToken, removeTokensMatching, renameToken, runAdditiveTokensCssMigrations, runTokensCssMigrations, semverBumpType, validateTokensCss };
217
+ export { type BreakingGateResult, type ComponentSource, type ContractViolation, type MissingToken, type RunResult, type SemverBump, TOKENS_CSS_MIGRATIONS, type TokensCssMigration, type ValidateInput, appendMediaBlock, collectDefinedTokens, collectReferencedTokens, collectTokenValues, enforceBreakingRequiresMajor, ensureScale, findContractViolations, removeToken, removeTokensMatching, renameToken, runAdditiveTokensCssMigrations, runTokensCssMigrations, semverBumpType, setTokenValue, validateTokensCss };
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  TOKENS_CSS_MIGRATIONS,
3
+ appendMediaBlock,
3
4
  collectDefinedTokens,
4
5
  collectReferencedTokens,
5
6
  collectTokenValues,
@@ -12,14 +13,16 @@ import {
12
13
  runAdditiveTokensCssMigrations,
13
14
  runTokensCssMigrations,
14
15
  semverBumpType,
16
+ setTokenValue,
15
17
  validateTokensCss
16
- } from "../chunk-GWMEYBZ2.js";
18
+ } from "../chunk-7XSUHJUH.js";
17
19
  import "../chunk-7TQQTHI6.js";
18
20
  import {
19
21
  readLiveTokensConfig
20
22
  } from "../chunk-6WGFOJXO.js";
21
23
  export {
22
24
  TOKENS_CSS_MIGRATIONS,
25
+ appendMediaBlock,
23
26
  collectDefinedTokens,
24
27
  collectReferencedTokens,
25
28
  collectTokenValues,
@@ -33,5 +36,6 @@ export {
33
36
  runAdditiveTokensCssMigrations,
34
37
  runTokensCssMigrations,
35
38
  semverBumpType,
39
+ setTokenValue,
36
40
  validateTokensCss
37
41
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motion-proto/live-tokens",
3
- "version": "0.91.2",
3
+ "version": "0.93.0",
4
4
  "type": "module",
5
5
  "description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
6
6
  "keywords": [
package/src/app/site.css CHANGED
@@ -52,6 +52,28 @@ h4 {
52
52
  margin: var(--space-16) 0 var(--space-4);
53
53
  }
54
54
 
55
+ h5 {
56
+ font-family: var(--heading-xs-font-family);
57
+ font-size: var(--heading-xs-font-size);
58
+ font-weight: var(--heading-xs-font-weight);
59
+ line-height: var(--heading-xs-line-height);
60
+ letter-spacing: var(--heading-xs-letter-spacing);
61
+ color: var(--text-primary);
62
+ margin: var(--space-16) 0 var(--space-4);
63
+ overflow-wrap: break-word;
64
+ }
65
+
66
+ h6 {
67
+ font-family: var(--heading-2xs-font-family);
68
+ font-size: var(--heading-2xs-font-size);
69
+ font-weight: var(--heading-2xs-font-weight);
70
+ line-height: var(--heading-2xs-line-height);
71
+ letter-spacing: var(--heading-2xs-letter-spacing);
72
+ color: var(--text-primary);
73
+ margin: var(--space-16) 0 var(--space-4);
74
+ overflow-wrap: break-word;
75
+ }
76
+
55
77
  @media (max-width: 768px) {
56
78
  h1 {
57
79
  margin-bottom: var(--space-8);
@@ -80,19 +102,31 @@ p:last-child {
80
102
  margin-bottom: 0;
81
103
  }
82
104
 
105
+ small {
106
+ font-family: var(--body-sm-font-family);
107
+ font-size: var(--body-sm-font-size);
108
+ font-weight: var(--body-sm-font-weight);
109
+ line-height: var(--body-sm-line-height);
110
+ letter-spacing: var(--body-sm-letter-spacing);
111
+ }
112
+
83
113
  code {
84
- font-family: var(--code-font-family);
85
- font-size: var(--code-font-size);
86
- font-weight: var(--code-font-weight);
87
- letter-spacing: var(--code-letter-spacing);
114
+ font-family: var(--code-md-font-family);
115
+ font-size: 0.875em;
116
+ font-weight: var(--code-md-font-weight);
117
+ letter-spacing: var(--code-md-letter-spacing);
88
118
  }
89
119
 
90
120
  pre {
91
- font-family: var(--code-font-family);
92
- font-size: var(--code-font-size);
93
- font-weight: var(--code-font-weight);
94
- line-height: var(--code-line-height);
95
- letter-spacing: var(--code-letter-spacing);
121
+ font-family: var(--code-md-font-family);
122
+ font-size: var(--code-md-font-size);
123
+ font-weight: var(--code-md-font-weight);
124
+ line-height: var(--code-md-line-height);
125
+ letter-spacing: var(--code-md-letter-spacing);
126
+ }
127
+
128
+ pre code {
129
+ font-size: inherit;
96
130
  }
97
131
 
98
132
  a {
@@ -255,3 +289,203 @@ blockquote {
255
289
  letter-spacing: var(--eyebrow-letter-spacing);
256
290
  text-transform: var(--eyebrow-text-transform);
257
291
  }
292
+
293
+ .display-2xl {
294
+ font-family: var(--display-2xl-font-family);
295
+ font-size: var(--display-2xl-font-size);
296
+ font-weight: var(--display-2xl-font-weight);
297
+ line-height: var(--display-2xl-line-height);
298
+ letter-spacing: var(--display-2xl-letter-spacing);
299
+ }
300
+
301
+ .display-xl {
302
+ font-family: var(--display-xl-font-family);
303
+ font-size: var(--display-xl-font-size);
304
+ font-weight: var(--display-xl-font-weight);
305
+ line-height: var(--display-xl-line-height);
306
+ letter-spacing: var(--display-xl-letter-spacing);
307
+ }
308
+
309
+ .display-lg {
310
+ font-family: var(--display-lg-font-family);
311
+ font-size: var(--display-lg-font-size);
312
+ font-weight: var(--display-lg-font-weight);
313
+ line-height: var(--display-lg-line-height);
314
+ letter-spacing: var(--display-lg-letter-spacing);
315
+ }
316
+
317
+ .display-md {
318
+ font-family: var(--display-md-font-family);
319
+ font-size: var(--display-md-font-size);
320
+ font-weight: var(--display-md-font-weight);
321
+ line-height: var(--display-md-line-height);
322
+ letter-spacing: var(--display-md-letter-spacing);
323
+ }
324
+
325
+ .display-sm {
326
+ font-family: var(--display-sm-font-family);
327
+ font-size: var(--display-sm-font-size);
328
+ font-weight: var(--display-sm-font-weight);
329
+ line-height: var(--display-sm-line-height);
330
+ letter-spacing: var(--display-sm-letter-spacing);
331
+ }
332
+
333
+ .display-xs {
334
+ font-family: var(--display-xs-font-family);
335
+ font-size: var(--display-xs-font-size);
336
+ font-weight: var(--display-xs-font-weight);
337
+ line-height: var(--display-xs-line-height);
338
+ letter-spacing: var(--display-xs-letter-spacing);
339
+ }
340
+
341
+ .display-2xs {
342
+ font-family: var(--display-2xs-font-family);
343
+ font-size: var(--display-2xs-font-size);
344
+ font-weight: var(--display-2xs-font-weight);
345
+ line-height: var(--display-2xs-line-height);
346
+ letter-spacing: var(--display-2xs-letter-spacing);
347
+ }
348
+
349
+ .heading-2xl {
350
+ font-family: var(--heading-2xl-font-family);
351
+ font-size: var(--heading-2xl-font-size);
352
+ font-weight: var(--heading-2xl-font-weight);
353
+ line-height: var(--heading-2xl-line-height);
354
+ letter-spacing: var(--heading-2xl-letter-spacing);
355
+ }
356
+
357
+ .heading-xs {
358
+ font-family: var(--heading-xs-font-family);
359
+ font-size: var(--heading-xs-font-size);
360
+ font-weight: var(--heading-xs-font-weight);
361
+ line-height: var(--heading-xs-line-height);
362
+ letter-spacing: var(--heading-xs-letter-spacing);
363
+ }
364
+
365
+ .heading-2xs {
366
+ font-family: var(--heading-2xs-font-family);
367
+ font-size: var(--heading-2xs-font-size);
368
+ font-weight: var(--heading-2xs-font-weight);
369
+ line-height: var(--heading-2xs-line-height);
370
+ letter-spacing: var(--heading-2xs-letter-spacing);
371
+ }
372
+
373
+ .body-2xl {
374
+ font-family: var(--body-2xl-font-family);
375
+ font-size: var(--body-2xl-font-size);
376
+ font-weight: var(--body-2xl-font-weight);
377
+ line-height: var(--body-2xl-line-height);
378
+ letter-spacing: var(--body-2xl-letter-spacing);
379
+ }
380
+
381
+ .body-xl {
382
+ font-family: var(--body-xl-font-family);
383
+ font-size: var(--body-xl-font-size);
384
+ font-weight: var(--body-xl-font-weight);
385
+ line-height: var(--body-xl-line-height);
386
+ letter-spacing: var(--body-xl-letter-spacing);
387
+ }
388
+
389
+ .body-lg {
390
+ font-family: var(--body-lg-font-family);
391
+ font-size: var(--body-lg-font-size);
392
+ font-weight: var(--body-lg-font-weight);
393
+ line-height: var(--body-lg-line-height);
394
+ letter-spacing: var(--body-lg-letter-spacing);
395
+ }
396
+
397
+ .body-xs {
398
+ font-family: var(--body-xs-font-family);
399
+ font-size: var(--body-xs-font-size);
400
+ font-weight: var(--body-xs-font-weight);
401
+ line-height: var(--body-xs-line-height);
402
+ letter-spacing: var(--body-xs-letter-spacing);
403
+ }
404
+
405
+ .body-2xs {
406
+ font-family: var(--body-2xs-font-family);
407
+ font-size: var(--body-2xs-font-size);
408
+ font-weight: var(--body-2xs-font-weight);
409
+ line-height: var(--body-2xs-line-height);
410
+ letter-spacing: var(--body-2xs-letter-spacing);
411
+ }
412
+
413
+ .editorial-2xl {
414
+ font-family: var(--editorial-2xl-font-family);
415
+ font-size: var(--editorial-2xl-font-size);
416
+ font-weight: var(--editorial-2xl-font-weight);
417
+ line-height: var(--editorial-2xl-line-height);
418
+ letter-spacing: var(--editorial-2xl-letter-spacing);
419
+ }
420
+
421
+ .editorial-xs {
422
+ font-family: var(--editorial-xs-font-family);
423
+ font-size: var(--editorial-xs-font-size);
424
+ font-weight: var(--editorial-xs-font-weight);
425
+ line-height: var(--editorial-xs-line-height);
426
+ letter-spacing: var(--editorial-xs-letter-spacing);
427
+ }
428
+
429
+ .editorial-2xs {
430
+ font-family: var(--editorial-2xs-font-family);
431
+ font-size: var(--editorial-2xs-font-size);
432
+ font-weight: var(--editorial-2xs-font-weight);
433
+ line-height: var(--editorial-2xs-line-height);
434
+ letter-spacing: var(--editorial-2xs-letter-spacing);
435
+ }
436
+
437
+ .code-2xl {
438
+ font-family: var(--code-2xl-font-family);
439
+ font-size: var(--code-2xl-font-size);
440
+ font-weight: var(--code-2xl-font-weight);
441
+ line-height: var(--code-2xl-line-height);
442
+ letter-spacing: var(--code-2xl-letter-spacing);
443
+ }
444
+
445
+ .code-xl {
446
+ font-family: var(--code-xl-font-family);
447
+ font-size: var(--code-xl-font-size);
448
+ font-weight: var(--code-xl-font-weight);
449
+ line-height: var(--code-xl-line-height);
450
+ letter-spacing: var(--code-xl-letter-spacing);
451
+ }
452
+
453
+ .code-lg {
454
+ font-family: var(--code-lg-font-family);
455
+ font-size: var(--code-lg-font-size);
456
+ font-weight: var(--code-lg-font-weight);
457
+ line-height: var(--code-lg-line-height);
458
+ letter-spacing: var(--code-lg-letter-spacing);
459
+ }
460
+
461
+ .code-md {
462
+ font-family: var(--code-md-font-family);
463
+ font-size: var(--code-md-font-size);
464
+ font-weight: var(--code-md-font-weight);
465
+ line-height: var(--code-md-line-height);
466
+ letter-spacing: var(--code-md-letter-spacing);
467
+ }
468
+
469
+ .code-sm {
470
+ font-family: var(--code-sm-font-family);
471
+ font-size: var(--code-sm-font-size);
472
+ font-weight: var(--code-sm-font-weight);
473
+ line-height: var(--code-sm-line-height);
474
+ letter-spacing: var(--code-sm-letter-spacing);
475
+ }
476
+
477
+ .code-xs {
478
+ font-family: var(--code-xs-font-family);
479
+ font-size: var(--code-xs-font-size);
480
+ font-weight: var(--code-xs-font-weight);
481
+ line-height: var(--code-xs-line-height);
482
+ letter-spacing: var(--code-xs-letter-spacing);
483
+ }
484
+
485
+ .code-2xs {
486
+ font-family: var(--code-2xs-font-family);
487
+ font-size: var(--code-2xs-font-size);
488
+ font-weight: var(--code-2xs-font-weight);
489
+ line-height: var(--code-2xs-line-height);
490
+ letter-spacing: var(--code-2xs-letter-spacing);
491
+ }
@@ -607,10 +607,10 @@
607
607
 
608
608
  code {
609
609
  font-family: var(--code-font-family);
610
- font-size: var(--code-font-size);
610
+ font-size: var(--code-md-font-size);
611
611
  font-weight: var(--code-font-weight);
612
- line-height: var(--code-line-height);
613
- letter-spacing: var(--code-letter-spacing);
612
+ line-height: var(--code-md-line-height);
613
+ letter-spacing: var(--code-md-letter-spacing);
614
614
  background: var(--tint-low);
615
615
  padding-inline: var(--space-4);
616
616
  border-radius: var(--radius-sm);
@@ -863,10 +863,10 @@
863
863
 
864
864
  .dg :global(.rule) {
865
865
  font-family: var(--code-font-family);
866
- font-size: var(--code-font-size);
866
+ font-size: var(--code-md-font-size);
867
867
  font-weight: var(--code-font-weight);
868
- line-height: var(--code-line-height);
869
- letter-spacing: var(--code-letter-spacing);
868
+ line-height: var(--code-md-line-height);
869
+ letter-spacing: var(--code-md-letter-spacing);
870
870
  fill: var(--text-accent);
871
871
  }
872
872
 
@@ -901,10 +901,10 @@
901
901
  padding-top: var(--space-24);
902
902
  border-top: var(--border-width-1) solid var(--border-neutral-subtle);
903
903
  font-family: var(--code-font-family);
904
- font-size: var(--code-font-size);
904
+ font-size: var(--code-md-font-size);
905
905
  font-weight: var(--code-font-weight);
906
- line-height: var(--code-line-height);
907
- letter-spacing: var(--code-letter-spacing);
906
+ line-height: var(--code-md-line-height);
907
+ letter-spacing: var(--code-md-letter-spacing);
908
908
  color: var(--text-secondary);
909
909
  }
910
910
 
@@ -18,6 +18,7 @@ export interface FaceCoverage {
18
18
 
19
19
  const STACK_REF = /var\(\s*(--font-(?:display|sans|serif|mono))\s*\)/;
20
20
  const VAR_REF = /var\(\s*(--[a-z0-9-]+)/i;
21
+ const ALIAS = /^var\(\s*(--[a-z0-9-]+)\s*\)$/i;
21
22
 
22
23
  function declarations(css: string): Map<string, string> {
23
24
  const decls = new Map<string, string>();
@@ -27,6 +28,28 @@ function declarations(css: string): Map<string, string> {
27
28
  return decls;
28
29
  }
29
30
 
31
+ /**
32
+ * Follows whole-value aliases, such as a text style's `var(--heading-font-weight)`,
33
+ * until `read` accepts a value. A `--font-*` stack or weight token ends the walk,
34
+ * so an alias between stacks, such as `--font-editorial`, counts for no stack.
35
+ */
36
+ function follow<T>(
37
+ decls: Map<string, string>,
38
+ value: string | undefined,
39
+ read: (v: string) => T | undefined,
40
+ ): T | undefined {
41
+ const seen = new Set<string>();
42
+ while (value !== undefined) {
43
+ const hit = read(value);
44
+ if (hit !== undefined) return hit;
45
+ const next = value.match(ALIAS)?.[1];
46
+ if (!next || next.startsWith('--font-') || seen.has(next)) return undefined;
47
+ seen.add(next);
48
+ value = decls.get(next);
49
+ }
50
+ return undefined;
51
+ }
52
+
30
53
  /**
31
54
  * Every weight the token contract asks of each font stack, read off the
32
55
  * `-font-family` / `-font-weight` pairing that typography tokens follow.
@@ -50,10 +73,16 @@ export function requiredWeights(
50
73
  for (const [name, value] of decls) {
51
74
  const familyVariable = inferFontFamilyVariable(name);
52
75
  if (!familyVariable) continue;
53
- const stack = decls.get(familyVariable)?.match(STACK_REF)?.[1] as FontStackVariable | undefined;
76
+ const stack = follow(
77
+ decls,
78
+ decls.get(familyVariable),
79
+ (v) => v.match(STACK_REF)?.[1] as FontStackVariable | undefined,
80
+ );
54
81
  if (!stack) continue;
55
82
 
56
- const weight = /^\d+$/.test(value) ? Number(value) : scale.get(value.match(VAR_REF)?.[1] ?? '');
83
+ const weight = follow(decls, value, (v) =>
84
+ /^\d+$/.test(v) ? Number(v) : scale.get(v.match(VAR_REF)?.[1] ?? ''),
85
+ );
57
86
  if (weight === undefined) continue;
58
87
 
59
88
  const key = `${stack}:${weight}`;
@@ -23,6 +23,8 @@ import tokensCss from '../../../system/styles/tokens.css?raw';
23
23
  import { editorState } from '../store/editorStore';
24
24
  import type { EditorState } from '../store/editorTypes';
25
25
  import { extractGlobalRootBody } from '../themes/parsers/globalRootBlock';
26
+ import { stripMediaBlocks } from '../themes/parsers/mediaBlocks';
27
+ import { parseColorOpacity } from '../themes/parsers/colorOpacity';
26
28
  import { refToCss } from '../store/cssVarRef';
27
29
 
28
30
  // Re-exported for tests and downstream consumers that previously imported it
@@ -43,39 +45,51 @@ export interface TokenRegistry {
43
45
 
44
46
  /**
45
47
  * Pure constructor: parses a CSS source string (possibly concatenated from
46
- * multiple files) and returns a registry bound to that snapshot.
48
+ * multiple files) and returns a registry bound to that snapshot. A declaration
49
+ * inside `@media` is a breakpoint re-point and never counts as declared.
47
50
  */
48
51
  export function buildTokenRegistry(cssText: string): TokenRegistry {
49
52
  const declarations = new Map<string, string>();
50
53
  const re = /(--[a-z0-9-]+)\s*:\s*([^;]+);/gi;
51
54
  let m: RegExpExecArray | null;
52
- while ((m = re.exec(cssText)) !== null) {
55
+ const topLevel = stripMediaBlocks(cssText);
56
+ while ((m = re.exec(topLevel)) !== null) {
53
57
  declarations.set(m[1], m[2].trim());
54
58
  }
55
59
 
56
- function resolveAliasChain(varName: string): string[] {
57
- const chain = [varName];
58
- const visited = new Set<string>([varName]);
59
- let current = varName;
60
- while (true) {
61
- const decl = declarations.get(current);
62
- if (!decl) return chain;
63
- const aliasMatch = decl.match(/var\((--[a-z0-9-]+)\)/i);
64
- if (!aliasMatch) return chain;
65
- const next = aliasMatch[1];
66
- if (visited.has(next)) return chain;
67
- visited.add(next);
68
- chain.push(next);
69
- current = next;
70
- }
71
- }
72
-
60
+ const getDeclaredValue = (v: string): string | null => declarations.get(v) ?? null;
73
61
  return {
74
- getDeclaredValue: (v) => declarations.get(v) ?? null,
75
- resolveAliasChain,
62
+ getDeclaredValue,
63
+ resolveAliasChain: (varName) => walkAliasChain(varName, getDeclaredValue),
76
64
  };
77
65
  }
78
66
 
67
+ const ALIAS_RE = /^var\(\s*(--[a-z0-9-]+)\s*\)$/i;
68
+
69
+ /**
70
+ * The token a declaration aliases: the whole value is `var(--x)`, or `--x` at
71
+ * reduced opacity. A `var()` inside any other expression is arithmetic on the
72
+ * token, so `calc(var(--x) * 2)` aliases nothing.
73
+ */
74
+ function aliasTarget(decl: string): string | null {
75
+ return decl.match(ALIAS_RE)?.[1] ?? parseColorOpacity(decl)?.name ?? null;
76
+ }
77
+
78
+ function walkAliasChain(varName: string, getDeclared: (v: string) => string | null): string[] {
79
+ const chain = [varName];
80
+ const visited = new Set<string>([varName]);
81
+ let current = varName;
82
+ while (true) {
83
+ const decl = getDeclared(current);
84
+ if (!decl) return chain;
85
+ const next = aliasTarget(decl);
86
+ if (!next || visited.has(next)) return chain;
87
+ visited.add(next);
88
+ chain.push(next);
89
+ current = next;
90
+ }
91
+ }
92
+
79
93
  const componentTokenCss = Object.values(componentSources)
80
94
  .map(extractGlobalRootBody)
81
95
  .join('\n');
@@ -119,22 +133,7 @@ function buildOverlayRegistry(
119
133
  overrides.has(v) ? overrides.get(v)! : base.getDeclaredValue(v);
120
134
  return {
121
135
  getDeclaredValue: getDeclared,
122
- resolveAliasChain(varName: string): string[] {
123
- const chain = [varName];
124
- const visited = new Set<string>([varName]);
125
- let current = varName;
126
- while (true) {
127
- const decl = getDeclared(current);
128
- if (!decl) return chain;
129
- const aliasMatch = decl.match(/var\((--[a-z0-9-]+)\)/i);
130
- if (!aliasMatch) return chain;
131
- const next = aliasMatch[1];
132
- if (visited.has(next)) return chain;
133
- visited.add(next);
134
- chain.push(next);
135
- current = next;
136
- }
137
- },
136
+ resolveAliasChain: (varName) => walkAliasChain(varName, getDeclared),
138
137
  };
139
138
  }
140
139
 
@@ -41,7 +41,7 @@ const TEXT_INPUT_TYPES = new Set([
41
41
  'date', 'datetime-local', 'month', 'time', 'week',
42
42
  ]);
43
43
 
44
- function isEditable(el: HTMLElement): boolean {
44
+ export function isEditable(el: HTMLElement): boolean {
45
45
  const tag = el.tagName;
46
46
  if (tag === 'TEXTAREA') return true;
47
47
  if (tag === 'INPUT') {
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `css` with every `@media` block removed. Readers that want a token's declared
3
+ * default go through it, so a breakpoint re-point never stands in for the
4
+ * top-level value.
5
+ *
6
+ * Dependency-free so the vite plugin build can import it, as it does
7
+ * `globalRootBlock`.
8
+ */
9
+ export function stripMediaBlocks(css: string): string {
10
+ const kept: string[] = [];
11
+ let from = 0;
12
+ let i = 0;
13
+ while (i < css.length) {
14
+ if (css.startsWith('/*', i)) {
15
+ i = commentEnd(css, i);
16
+ } else if (css[i] === '@' && /^@media\b/i.test(css.slice(i, i + 7))) {
17
+ kept.push(css.slice(from, i));
18
+ i = from = blockEnd(css, i);
19
+ } else {
20
+ i++;
21
+ }
22
+ }
23
+ kept.push(css.slice(from));
24
+ return kept.join('');
25
+ }
26
+
27
+ function commentEnd(css: string, start: number): number {
28
+ const end = css.indexOf('*/', start + 2);
29
+ return end === -1 ? css.length : end + 2;
30
+ }
31
+
32
+ /** Index just past the brace that closes the first block opened at or after `start`. */
33
+ function blockEnd(css: string, start: number): number {
34
+ let depth = 0;
35
+ let i = start;
36
+ while (i < css.length) {
37
+ if (css.startsWith('/*', i)) {
38
+ i = commentEnd(css, i);
39
+ continue;
40
+ }
41
+ const c = css[i++];
42
+ if (c === '{') depth++;
43
+ else if (c === '}' && --depth === 0) return i;
44
+ }
45
+ return css.length;
46
+ }