@motion-proto/live-tokens 0.74.0 → 0.75.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 (170) hide show
  1. package/.claude/skills/live-tokens-check-compliance/SKILL.md +33 -38
  2. package/.claude/skills/live-tokens-create-component/SKILL copy.md +196 -0
  3. package/.claude/skills/live-tokens-create-component/SKILL.md +198 -146
  4. package/.claude/skills/live-tokens-create-component/references/contract-tests.md +95 -52
  5. package/.claude/skills/live-tokens-create-component/references/intrinsics.md +2 -2
  6. package/.claude/skills/live-tokens-create-component/references/linked-siblings.md +2 -2
  7. package/.claude/skills/live-tokens-create-component/references/sketch-mode.md +12 -12
  8. package/.claude/skills/live-tokens-create-component/references/token-naming.md +2 -1
  9. package/.claude/skills/live-tokens-create-page/SKILL.md +187 -0
  10. package/.claude/skills/live-tokens-create-page/references/interaction-sources.md +66 -0
  11. package/.claude/skills/live-tokens-create-page/references/layout-sources.md +87 -0
  12. package/.claude/skills/live-tokens-create-theme/SKILL.md +52 -48
  13. package/.claude/skills/live-tokens-create-theme/references/design-directions.md +1 -1
  14. package/.claude/skills/live-tokens-fix-findings/SKILL.md +69 -60
  15. package/.claude/skills/live-tokens-pick-component/SKILL.md +64 -79
  16. package/.claude/skills/live-tokens-set-colors/SKILL.md +47 -38
  17. package/.claude/skills/live-tokens-set-geometry/SKILL.md +58 -35
  18. package/.claude/skills/live-tokens-set-geometry/references/geometry-anchors.md +5 -3
  19. package/.claude/skills/live-tokens-set-type/SKILL.md +30 -30
  20. package/CHANGELOG.md +103 -0
  21. package/README.md +3 -3
  22. package/bin/check-component.mjs +91 -19
  23. package/bin/check-page.mjs +102 -21
  24. package/bin/cli.mjs +78 -58
  25. package/bin/contractRunner.mjs +945 -0
  26. package/bin/create.mjs +1 -1
  27. package/bin/lib/catalogue.mjs +37 -30
  28. package/bin/lib/findings.mjs +46 -15
  29. package/bin/lib/report.mjs +3 -3
  30. package/bin/lib/tokenVocabulary.mjs +4 -4
  31. package/bin/migrate-routes.mjs +5 -5
  32. package/bin/migrate.mjs +4 -4
  33. package/bin/save-theme.mjs +8 -9
  34. package/bin/set-colors.mjs +9 -11
  35. package/bin/set-geometry.mjs +7 -7
  36. package/bin/set-type.mjs +5 -7
  37. package/dist-plugin/{chunk-W6Y4BWFB.js → chunk-6WGFOJXO.js} +22 -0
  38. package/dist-plugin/{chunk-7VRTBGJT.js → chunk-PDNL4NC5.js} +9 -2
  39. package/dist-plugin/{chunk-V3YF6CGT.js → chunk-SWXRVZKT.js} +43 -1
  40. package/dist-plugin/{dataPaths-BhWzd5cL.d.cts → dataPaths-BpIK_Obx.d.cts} +1 -0
  41. package/dist-plugin/{dataPaths-BhWzd5cL.d.ts → dataPaths-BpIK_Obx.d.ts} +1 -0
  42. package/dist-plugin/index.cjs +310 -147
  43. package/dist-plugin/index.d.cts +1 -1
  44. package/dist-plugin/index.d.ts +1 -1
  45. package/dist-plugin/index.js +218 -122
  46. package/dist-plugin/migrateData/index.cjs +66 -2
  47. package/dist-plugin/migrateData/index.d.cts +1 -1
  48. package/dist-plugin/migrateData/index.d.ts +1 -1
  49. package/dist-plugin/migrateData/index.js +3 -3
  50. package/dist-plugin/setColors/index.cjs +61 -4
  51. package/dist-plugin/setColors/index.d.cts +1 -1
  52. package/dist-plugin/setColors/index.d.ts +1 -1
  53. package/dist-plugin/setColors/index.js +5 -5
  54. package/dist-plugin/setGeometry/index.cjs +107 -44
  55. package/dist-plugin/setGeometry/index.d.cts +5 -5
  56. package/dist-plugin/setGeometry/index.d.ts +5 -5
  57. package/dist-plugin/setGeometry/index.js +51 -45
  58. package/dist-plugin/setType/index.cjs +15 -0
  59. package/dist-plugin/setType/index.d.cts +1 -1
  60. package/dist-plugin/setType/index.d.ts +1 -1
  61. package/dist-plugin/setType/index.js +1 -1
  62. package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
  63. package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
  64. package/dist-plugin/tokensCssMigrations/index.js +1 -1
  65. package/package.json +35 -8
  66. package/src/app/site.css +19 -9
  67. package/src/editor/bootstrap.ts +2 -0
  68. package/src/editor/component-editor/CollapsibleSectionEditor.svelte +4 -4
  69. package/src/editor/component-editor/DialogEditor.svelte +4 -4
  70. package/src/editor/component-editor/MenuSelectEditor.svelte +4 -1
  71. package/src/editor/component-editor/SegmentedControlEditor.svelte +6 -1
  72. package/src/editor/component-editor/TabBarEditor.svelte +1 -1
  73. package/src/editor/component-editor/TableEditor.svelte +2 -2
  74. package/src/editor/component-editor/scaffolding/TokenLayout.svelte +6 -3
  75. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +41 -20
  76. package/src/editor/core/components/adjustAliases.ts +59 -45
  77. package/src/editor/core/components/aliasKinds.ts +9 -5
  78. package/src/editor/core/preview/themePreview.ts +9 -2
  79. package/src/editor/core/sketch/sketchLayer.ts +22 -0
  80. package/src/editor/core/store/editorStore.ts +10 -1
  81. package/src/editor/core/themes/buildColors.ts +3 -3
  82. package/src/editor/core/themes/liveStateStream.ts +26 -0
  83. package/src/editor/core/themes/migrations/2026-09-07-stroke-role-renames.ts +52 -0
  84. package/src/editor/core/themes/migrations/index.ts +2 -0
  85. package/src/editor/core/themes/themeDocumentSync.ts +22 -11
  86. package/src/editor/core/themes/themeService.ts +9 -2
  87. package/src/editor/pages/ComponentEditorPage.svelte +17 -1
  88. package/src/editor/pages/liveTokensEditorHandle.ts +23 -0
  89. package/src/editor/skill-atlas/SkillAtlas.svelte +75 -573
  90. package/src/editor/skill-atlas/TreeCanvas.svelte +263 -0
  91. package/src/editor/skill-atlas/TreeNodeCard.svelte +136 -53
  92. package/src/editor/skill-atlas/edges.ts +31 -0
  93. package/src/editor/skill-atlas/skillSources.generated.ts +20 -19
  94. package/src/editor/skill-atlas/skillTrees.ts +19 -3840
  95. package/src/editor/skill-atlas/trees/check-compliance.ts +183 -0
  96. package/src/editor/skill-atlas/trees/create-component.ts +275 -0
  97. package/src/editor/skill-atlas/trees/create-page.ts +320 -0
  98. package/src/editor/skill-atlas/trees/create-theme.ts +255 -0
  99. package/src/editor/skill-atlas/trees/fix-findings.ts +469 -0
  100. package/src/editor/skill-atlas/trees/pick-component.ts +300 -0
  101. package/src/editor/skill-atlas/trees/set-colors.ts +148 -0
  102. package/src/editor/skill-atlas/trees/set-geometry.ts +136 -0
  103. package/src/editor/skill-atlas/trees/set-type.ts +142 -0
  104. package/src/editor/skill-atlas/types.ts +3 -4
  105. package/src/editor/skill-atlas/wireLayout.ts +287 -0
  106. package/src/live-tokens/data/themes/autumn.json +15 -15
  107. package/src/live-tokens/data/themes/halloween.json +15 -15
  108. package/src/live-tokens/data/themes/midnight-study.json +15 -15
  109. package/src/live-tokens/data/themes/ocean.json +15 -15
  110. package/src/live-tokens/data/themes/royal-velvet.json +15 -15
  111. package/src/live-tokens/data/themes/sketchy.json +15 -15
  112. package/src/live-tokens/data/themes/spring-meadow.json +15 -15
  113. package/src/live-tokens/data/themes/sunset.json +15 -15
  114. package/src/system/components/Badge.svelte +7 -0
  115. package/src/system/components/Button.svelte +7 -0
  116. package/src/system/components/Callout.svelte +10 -6
  117. package/src/system/components/Card.svelte +23 -4
  118. package/src/system/components/CodeSnippet.svelte +4 -3
  119. package/src/system/components/CollapsibleSection.svelte +23 -8
  120. package/src/system/components/CornerBadge.svelte +6 -0
  121. package/src/system/components/Dialog.svelte +13 -6
  122. package/src/system/components/IconButton.svelte +9 -0
  123. package/src/system/components/Image.svelte +8 -0
  124. package/src/system/components/ImageLightbox.svelte +6 -0
  125. package/src/system/components/InlineEditActions.svelte +7 -0
  126. package/src/system/components/Input.svelte +7 -0
  127. package/src/system/components/MenuSelect.svelte +7 -0
  128. package/src/system/components/Notification.svelte +7 -0
  129. package/src/system/components/Panel.svelte +6 -0
  130. package/src/system/components/ProgressBar.svelte +5 -0
  131. package/src/system/components/RadioButton.svelte +11 -5
  132. package/src/system/components/SectionDivider.svelte +8 -0
  133. package/src/system/components/SegmentedControl.svelte +6 -0
  134. package/src/system/components/SideNavigation.svelte +6 -0
  135. package/src/system/components/Slider.svelte +7 -4
  136. package/src/system/components/TabBar.svelte +15 -9
  137. package/src/system/components/Table.svelte +8 -3
  138. package/src/system/components/Toggle.svelte +4 -4
  139. package/src/system/components/Tooltip.svelte +6 -0
  140. package/src/testing-js/chunk-AO7EZHYV.js +776 -0
  141. package/src/testing-js/chunk-AO7EZHYV.js.map +1 -0
  142. package/src/testing-js/chunk-FAFOAWYL.js +39 -0
  143. package/src/testing-js/chunk-FAFOAWYL.js.map +1 -0
  144. package/src/testing-js/chunk-L73N4NSO.js +23 -0
  145. package/src/testing-js/chunk-L73N4NSO.js.map +1 -0
  146. package/src/testing-js/chunk-LXR3MN6N.js +3063 -0
  147. package/src/testing-js/chunk-LXR3MN6N.js.map +1 -0
  148. package/src/testing-js/chunk-ZMSX6CXR.js +53 -0
  149. package/src/testing-js/chunk-ZMSX6CXR.js.map +1 -0
  150. package/src/testing-js/component-alias.contract.js +81 -0
  151. package/src/testing-js/component-alias.contract.js.map +1 -0
  152. package/src/testing-js/component-editor.contract.js +62 -0
  153. package/src/testing-js/component-editor.contract.js.map +1 -0
  154. package/src/testing-js/component-render.contract.js +568 -0
  155. package/src/testing-js/component-render.contract.js.map +1 -0
  156. package/src/testing-js/index.d.ts +293 -0
  157. package/src/testing-js/index.js +222 -0
  158. package/src/testing-js/index.js.map +1 -0
  159. package/src/testing-js/registry.contract.js +39 -0
  160. package/src/testing-js/registry.contract.js.map +1 -0
  161. package/src/testing-js/vitest-BE6uGF31.d.ts +73 -0
  162. package/src/testing-js/vitest.d.ts +3 -0
  163. package/src/testing-js/vitest.js +13 -0
  164. package/src/testing-js/vitest.js.map +1 -0
  165. package/template/README.md +13 -0
  166. package/template/_gitignore +2 -0
  167. package/template/package.json +2 -1
  168. package/template/src/pages/Home.svelte +4 -18
  169. package/.claude/skills/live-tokens-build-page/SKILL.md +0 -103
  170. package/.claude/skills/live-tokens-build-page/references/layout-sources.md +0 -48
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  CURRENT_COMPONENT_SCHEMA_VERSION
3
- } from "../chunk-V3YF6CGT.js";
3
+ } from "../chunk-SWXRVZKT.js";
4
4
  import {
5
5
  readLiveTokensConfig,
6
6
  resolveDataDirs
7
- } from "../chunk-W6Y4BWFB.js";
7
+ } from "../chunk-6WGFOJXO.js";
8
8
 
9
9
  // src/editor/core/components/aliasKinds.ts
10
10
  var KIND_RULES = [
@@ -32,7 +32,10 @@ var KIND_RULES = [
32
32
  prefix: ["--text-"]
33
33
  },
34
34
  { kind: "radius", suffix: ["-radius"], prefix: ["--radius-"] },
35
- { kind: "divider-width", suffix: ["-divider-width", "-divider-thickness"] },
35
+ // Three stroke roles share the `--border-width-*` scale: a border encloses,
36
+ // a divider separates, an accent emphasises. `set-geometry` moves each role
37
+ // on its own, so a suffix has to say which line it names.
38
+ { kind: "divider-width", suffix: ["-divider-width", "-divider-thickness", "-hairline-thickness", "-thickness"] },
36
39
  { kind: "divider-height", suffix: ["-divider-height", "-track-height"] },
37
40
  { kind: "divider-inset", suffix: ["-divider-inset", "-inset"] },
38
41
  { kind: "dot-size", suffix: ["-dot-size"] },
@@ -43,11 +46,8 @@ var KIND_RULES = [
43
46
  { kind: "gap", suffix: ["-gap"] },
44
47
  { kind: "duration", suffix: ["-duration"], prefix: ["--duration-"] },
45
48
  { kind: "easing", suffix: ["-easing"], prefix: ["--ease-"] },
46
- {
47
- kind: "border-width",
48
- suffix: ["-border-width", "-accent-width", "-hairline-thickness", "-thickness"],
49
- prefix: ["--border-width-"]
50
- },
49
+ { kind: "accent-width", suffix: ["-accent-width", "-indicator-width"] },
50
+ { kind: "border-width", suffix: ["-border-width"], prefix: ["--border-width-"] },
51
51
  { kind: "border", suffix: ["-border"], prefix: ["--border-"] },
52
52
  // A dimension with no more specific name behind it — a panel's width, an
53
53
  // avatar's size. Last of the geometry rules, so every `-border-width`,
@@ -90,7 +90,7 @@ function matchesKind(variable, kind) {
90
90
 
91
91
  // src/editor/core/components/adjustAliases.ts
92
92
  var RADIUS_FULL = "--radius-full";
93
- var RADIUS_RUNGS = [
93
+ var RADIUS_STEPS = [
94
94
  "--radius-none",
95
95
  "--radius-sm",
96
96
  "--radius-md",
@@ -114,12 +114,13 @@ var SPACE_SETTABLE = [
114
114
  "--space-32",
115
115
  "--space-48"
116
116
  ];
117
- var SPACE_RUNGS = SPACE_SETTABLE;
118
- var INSET_RUNGS = SPACE_SETTABLE.slice(SPACE_SETTABLE.indexOf("--space-4"));
119
- var TEXT_INSET_RUNGS = SPACE_SETTABLE.slice(SPACE_SETTABLE.indexOf("--space-6"));
117
+ var SPACE_STEPS = SPACE_SETTABLE;
118
+ var INSET_STEPS = SPACE_SETTABLE.slice(SPACE_SETTABLE.indexOf("--space-4"));
119
+ var TEXT_INSET_STEPS = SPACE_SETTABLE.slice(SPACE_SETTABLE.indexOf("--space-6"));
120
120
  var SPACE_FAMILY = [...SPACE_SETTABLE, "--space-40", "--space-64", "--space-96", "--space-128"];
121
- var BORDER_WIDTH_RUNGS = [
122
- "--border-width-0",
121
+ var BORDER_WIDTH_NONE = "--border-width-0";
122
+ var BORDER_WIDTH_SETTABLE = [
123
+ BORDER_WIDTH_NONE,
123
124
  "--border-width-1",
124
125
  "--border-width-2",
125
126
  "--border-width-3",
@@ -133,70 +134,75 @@ var BORDER_WIDTH_RUNGS = [
133
134
  "--border-width-20",
134
135
  "--border-width-24"
135
136
  ];
136
- var LADDERS = {
137
+ var STROKE_STEPS = BORDER_WIDTH_SETTABLE.slice(1);
138
+ var STROKE_SCALE = {
139
+ steps: STROKE_STEPS,
140
+ settable: BORDER_WIDTH_SETTABLE,
141
+ family: BORDER_WIDTH_SETTABLE
142
+ };
143
+ var SCALES = {
137
144
  radius: {
138
- rungs: RADIUS_RUNGS,
139
- settable: [...RADIUS_RUNGS, RADIUS_FULL],
140
- family: [...RADIUS_RUNGS, RADIUS_FULL]
145
+ steps: RADIUS_STEPS,
146
+ settable: [...RADIUS_STEPS, RADIUS_FULL],
147
+ family: [...RADIUS_STEPS, RADIUS_FULL]
141
148
  },
142
- padding: { rungs: SPACE_RUNGS, settable: SPACE_SETTABLE, family: SPACE_FAMILY },
143
- gap: { rungs: SPACE_RUNGS, settable: SPACE_SETTABLE, family: SPACE_FAMILY },
144
- "border-width": {
145
- rungs: BORDER_WIDTH_RUNGS,
146
- settable: BORDER_WIDTH_RUNGS,
147
- family: BORDER_WIDTH_RUNGS
148
- }
149
+ padding: { steps: SPACE_STEPS, settable: SPACE_SETTABLE, family: SPACE_FAMILY },
150
+ gap: { steps: SPACE_STEPS, settable: SPACE_SETTABLE, family: SPACE_FAMILY },
151
+ "border-width": STROKE_SCALE,
152
+ "divider-width": STROKE_SCALE,
153
+ "accent-width": STROKE_SCALE
149
154
  };
150
155
  var TOKEN_NAME = /^--[a-z0-9-]+$/;
151
- function rungsFor(op, variable, aliases) {
152
- if (op.kind === "radius") return op.full ? [...RADIUS_RUNGS, RADIUS_FULL] : RADIUS_RUNGS;
153
- if (op.kind !== "padding") return LADDERS[op.kind].rungs;
156
+ function stepsFor(op, variable, aliases) {
157
+ if (op.kind === "radius") return op.full ? [...RADIUS_STEPS, RADIUS_FULL] : RADIUS_STEPS;
158
+ if (op.kind !== "padding") return SCALES[op.kind].steps;
154
159
  const base = stripSide(variable);
155
- if (!base.endsWith("-padding")) return LADDERS.padding.rungs;
160
+ if (!base.endsWith("-padding")) return SCALES.padding.steps;
156
161
  const variant = base.slice(0, -"-padding".length);
157
- return `${variant}-text-font-size` in aliases ? TEXT_INSET_RUNGS : INSET_RUNGS;
162
+ return `${variant}-text-font-size` in aliases ? TEXT_INSET_STEPS : INSET_STEPS;
158
163
  }
159
164
  function spaceStep(token) {
160
165
  return Number(token.slice("--space-".length));
161
166
  }
162
- function snapRung(token, rungs, direction) {
167
+ function snapStep(token, steps, direction) {
163
168
  const step = spaceStep(token);
164
- if (direction > 0) return rungs.findIndex((rung) => spaceStep(rung) > step);
165
- for (let index = rungs.length - 1; index >= 0; index--) {
166
- if (spaceStep(rungs[index]) < step) return index;
169
+ if (direction > 0) return steps.findIndex((candidate) => spaceStep(candidate) > step);
170
+ for (let index = steps.length - 1; index >= 0; index--) {
171
+ if (spaceStep(steps[index]) < step) return index;
167
172
  }
168
173
  return -1;
169
174
  }
170
175
  function resolve(op, variable, value, aliases) {
171
176
  if (typeof value !== "string" || !TOKEN_NAME.test(value)) return { skip: "raw-value" };
172
- if (!LADDERS[op.kind].family.includes(value)) return { skip: "off-ladder" };
177
+ if (!SCALES[op.kind].family.includes(value)) return { skip: "off-scale" };
173
178
  if (op.set !== void 0) return { from: value, to: op.set };
174
- const rungs = rungsFor(op, variable, aliases);
175
- let index = rungs.indexOf(value);
179
+ const steps = stepsFor(op, variable, aliases);
180
+ let index = steps.indexOf(value);
176
181
  let shift = op.shift;
177
182
  if (index < 0) {
178
183
  if (value === RADIUS_FULL) return { skip: "pill-preserved" };
179
- index = snapRung(value, rungs, shift);
184
+ if (value === BORDER_WIDTH_NONE) return { skip: "none-preserved" };
185
+ index = snapStep(value, steps, shift);
180
186
  if (index < 0) return { skip: "clamped" };
181
187
  shift -= Math.sign(shift);
182
188
  }
183
189
  const wanted = index + shift;
184
- const landed = Math.min(Math.max(wanted, 0), rungs.length - 1);
185
- if (landed !== wanted && rungs[landed] === value) return { skip: "clamped" };
186
- return { from: value, to: rungs[landed] };
190
+ const landed = Math.min(Math.max(wanted, 0), steps.length - 1);
191
+ if (landed !== wanted && steps[landed] === value) return { skip: "clamped" };
192
+ return { from: value, to: steps[landed] };
187
193
  }
188
194
  function validateOp(op, configs) {
189
- const ladder = LADDERS[op.kind];
190
- if (!ladder) throw new Error(`Unknown kind "${op.kind}" (expected ${Object.keys(LADDERS).join(", ")})`);
195
+ const scale = SCALES[op.kind];
196
+ if (!scale) throw new Error(`Unknown kind "${op.kind}" (expected ${Object.keys(SCALES).join(", ")})`);
191
197
  if (op.target !== void 0 && !(op.target in configs)) {
192
198
  throw new Error(`Unknown target component "${op.target}"`);
193
199
  }
194
200
  const hasSet = op.set !== void 0;
195
201
  const hasShift = op.shift !== void 0;
196
202
  if (hasSet === hasShift) throw new Error(`A ${op.kind} op needs exactly one of "set" or "shift"`);
197
- const settable = ladder.settable;
203
+ const settable = scale.settable;
198
204
  if (hasSet && !settable.includes(op.set)) {
199
- throw new Error(`"${op.set}" is not on the ${op.kind} ladder`);
205
+ throw new Error(`"${op.set}" is not on the ${op.kind} scale`);
200
206
  }
201
207
  if (hasShift && !Number.isInteger(op.shift)) {
202
208
  throw new Error(`"shift" must be a whole number of steps, got ${op.shift}`);
@@ -376,6 +376,11 @@ function weightCoverage(requirements, faces) {
376
376
  var import_fs = __toESM(require("fs"), 1);
377
377
  var import_path = __toESM(require("path"), 1);
378
378
  var DEFAULT_DATA_DIR = "src/live-tokens/data";
379
+ var TEST_DATA_DIR_ENV = "LIVE_TOKENS_TEST_DATA_DIR";
380
+ function testDataDir() {
381
+ const raw = process.env[TEST_DATA_DIR_ENV];
382
+ return raw && raw.trim() !== "" ? import_path.default.resolve(raw.trim()) : void 0;
383
+ }
379
384
  var KNOWN_CONFIG_KEYS = /* @__PURE__ */ new Set([
380
385
  "dataDir",
381
386
  "colorsAndTypeDir",
@@ -409,6 +414,16 @@ function readLiveTokensConfig() {
409
414
  }
410
415
  }
411
416
  function resolveDataDirs(opts = {}) {
417
+ const isolated = testDataDir();
418
+ if (isolated) {
419
+ return {
420
+ dataDir: isolated,
421
+ colorsAndTypeDir: import_path.default.join(isolated, "colors-and-type"),
422
+ componentConfigsDir: import_path.default.join(isolated, "component-configs"),
423
+ themesDir: import_path.default.join(isolated, "themes"),
424
+ sketchStylesDir: import_path.default.join(isolated, "sketch-styles")
425
+ };
426
+ }
412
427
  const fileConfig = readLiveTokensConfig();
413
428
  const dataDirRaw = opts.dataDir ?? fileConfig.dataDir ?? DEFAULT_DATA_DIR;
414
429
  const dataDir = import_path.default.resolve(dataDirRaw);
@@ -1,5 +1,5 @@
1
1
  import { c as FontStackVariable, C as ColorsAndType, b as FontStack, F as FontSource } from '../themeTypes-BxRtuN5V.cjs';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-BhWzd5cL.cjs';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-BpIK_Obx.cjs';
3
3
 
4
4
  type PairingSlot = 'display' | 'body' | 'serif' | 'mono' | 'editorial';
5
5
  interface PairingFace {
@@ -1,5 +1,5 @@
1
1
  import { c as FontStackVariable, C as ColorsAndType, b as FontStack, F as FontSource } from '../themeTypes-BxRtuN5V.js';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-BhWzd5cL.js';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-BpIK_Obx.js';
3
3
 
4
4
  type PairingSlot = 'display' | 'body' | 'serif' | 'mono' | 'editorial';
5
5
  interface PairingFace {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  readLiveTokensConfig,
3
3
  resolveDataDirs
4
- } from "../chunk-W6Y4BWFB.js";
4
+ } from "../chunk-6WGFOJXO.js";
5
5
 
6
6
  // src/editor/core/fonts/fontParse.ts
7
7
  function parseGoogleFontsUrl(url2) {
@@ -1,4 +1,4 @@
1
- export { r as readLiveTokensConfig } from '../dataPaths-BhWzd5cL.cjs';
1
+ export { r as readLiveTokensConfig } from '../dataPaths-BpIK_Obx.cjs';
2
2
 
3
3
  /**
4
4
  * A `tokens-css` migration: a pure, idempotent transform on the text of a
@@ -1,4 +1,4 @@
1
- export { r as readLiveTokensConfig } from '../dataPaths-BhWzd5cL.js';
1
+ export { r as readLiveTokensConfig } from '../dataPaths-BpIK_Obx.js';
2
2
 
3
3
  /**
4
4
  * A `tokens-css` migration: a pure, idempotent transform on the text of a
@@ -17,7 +17,7 @@ import {
17
17
  import "../chunk-7TQQTHI6.js";
18
18
  import {
19
19
  readLiveTokensConfig
20
- } from "../chunk-W6Y4BWFB.js";
20
+ } from "../chunk-6WGFOJXO.js";
21
21
  export {
22
22
  TOKENS_CSS_MIGRATIONS,
23
23
  collectDefinedTokens,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motion-proto/live-tokens",
3
- "version": "0.74.0",
3
+ "version": "0.75.0",
4
4
  "type": "module",
5
5
  "description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
6
6
  "keywords": [
@@ -49,6 +49,7 @@
49
49
  "src/live-tokens/data/sketch-styles/napkin.json",
50
50
  "src/live-tokens/data/sketch-styles/dry.json",
51
51
  "dist-plugin",
52
+ "src/testing-js",
52
53
  ".claude/skills",
53
54
  "!.claude/skills/**/skill-alt.md",
54
55
  "bin",
@@ -117,6 +118,14 @@
117
118
  "types": "./src/editor/core/sketch/index.ts",
118
119
  "default": "./src/editor/core/sketch/index.ts"
119
120
  },
121
+ "./testing": {
122
+ "types": "./src/testing-js/index.d.ts",
123
+ "default": "./src/testing-js/index.js"
124
+ },
125
+ "./testing/vitest": {
126
+ "types": "./src/testing-js/vitest.d.ts",
127
+ "default": "./src/testing-js/vitest.js"
128
+ },
120
129
  "./components/*": {
121
130
  "svelte": "./src/system/components/*",
122
131
  "default": "./src/system/components/*"
@@ -139,14 +148,16 @@
139
148
  "check": "svelte-check --tsconfig ./tsconfig.json",
140
149
  "test": "vitest run",
141
150
  "test:watch": "vitest",
142
- "test:e2e": "npm run test:e2e:contract && npm run test:e2e:stateful",
143
- "test:e2e:contract": "playwright test tests/e2e/component-render-contract.spec.ts tests/e2e/component-alias-contract.spec.ts --workers=${PLAYWRIGHT_WORKERS:-4}",
151
+ "test:e2e": "npm run test:e2e:contract && npm run test:e2e:contract-defects && npm run test:e2e:stateful",
152
+ "test:e2e:contract": "playwright test --project=contract --workers=${PLAYWRIGHT_WORKERS:-4}",
144
153
  "test:e2e:stateful": "playwright test tests/e2e/live-editing.spec.ts tests/e2e/theme-workflow.spec.ts --workers=1",
145
- "test:e2e:components": "playwright test tests/e2e/component-render-contract.spec.ts",
154
+ "test:e2e:contract-defects": "playwright test --project=contract-defects --workers=${PLAYWRIGHT_WORKERS:-4}",
155
+ "test:e2e:components": "playwright test --project=contract src/testing/component-render.contract.ts",
146
156
  "test:e2e:ui": "playwright test --ui",
147
- "prepare:e2e": "node scripts/prepare-playwright-data.mjs",
157
+ "test:registry-contract": "vitest run --config vitest.contract.config.ts",
148
158
  "build:plugin": "tsup",
149
- "build:lib": "npm run build:plugin",
159
+ "build:testing": "tsup --config tsup.testing.config.ts",
160
+ "build:lib": "npm run build:plugin && npm run build:testing",
150
161
  "deploy:local": "bash scripts/deploy-local.sh",
151
162
  "check:no-style-imports": "node scripts/check-no-style-imports.mjs",
152
163
  "check:no-tooling-imports": "node scripts/check-no-tooling-imports.mjs",
@@ -160,6 +171,7 @@
160
171
  "check:token-contract": "node scripts/check-token-contract.mjs",
161
172
  "check:preset-themes": "node scripts/check-preset-themes.mjs",
162
173
  "check:skills": "node scripts/check-skills.mjs",
174
+ "check:cli-strings": "node scripts/check-cli-strings.mjs",
163
175
  "check:skill-sources": "node scripts/sync-skill-sources.mjs --check",
164
176
  "check:skill-atlas": "node scripts/sync-skill-atlas.mjs",
165
177
  "sync:component-defaults": "node scripts/sync-component-defaults.mjs --write",
@@ -170,13 +182,28 @@
170
182
  "collapse:theme": "node scripts/collapse-theme-to-default.mjs",
171
183
  "check:smoke-install": "bash scripts/smoke-install.sh",
172
184
  "check:smoke-create": "bash scripts/smoke-create.sh",
173
- "prepublishOnly": "npm run check:no-style-imports && npm run check:no-tooling-imports && npm run check:slot-prose && npm run check:overlay-portal && npm run check:editor-font-isolation && npm run check:component-defaults && npm run check:pages && npm run check:production-is-default && npm run check:docs-content && npm run build:lib && npm run check:token-contract && npm run check:preset-themes && npm run check:skills && npm run check:skill-sources && npm run check:skill-atlas && npm run check:smoke-install && npm run check:smoke-create"
185
+ "check:smoke-component-tests": "bash scripts/smoke-component-tests.sh",
186
+ "prepublishOnly": "npm run check:no-style-imports && npm run check:no-tooling-imports && npm run check:slot-prose && npm run check:overlay-portal && npm run check:editor-font-isolation && npm run check:component-defaults && npm run check:pages && npm run check:production-is-default && npm run check:docs-content && npm run build:lib && npm run check:token-contract && npm run check:preset-themes && npm run check:skills && npm run check:cli-strings && npm run check:skill-sources && npm run check:skill-atlas && npm run check:smoke-install && npm run check:smoke-create && npm run check:smoke-component-tests"
174
187
  },
175
188
  "peerDependencies": {
189
+ "@playwright/test": "^1.55",
176
190
  "@sveltejs/vite-plugin-svelte": "^7.0",
191
+ "happy-dom": "^20.0",
177
192
  "sass": "^1.0",
178
193
  "svelte": "^5",
179
- "vite": "^8"
194
+ "vite": "^8",
195
+ "vitest": "^4.0"
196
+ },
197
+ "peerDependenciesMeta": {
198
+ "@playwright/test": {
199
+ "optional": true
200
+ },
201
+ "happy-dom": {
202
+ "optional": true
203
+ },
204
+ "vitest": {
205
+ "optional": true
206
+ }
180
207
  },
181
208
  "devDependencies": {
182
209
  "@playwright/test": "^1.62.1",
package/src/app/site.css CHANGED
@@ -1,11 +1,11 @@
1
1
  /*
2
- * Site Styles — global typography for the themed pages
2
+ * Site Styles: global typography for the themed pages
3
3
  *
4
4
  * Unscoped element selectors (h1, h2, p, a, ul li, …) consume the design
5
5
  * tokens in tokens.css and recolor with the user's theme. Loaded globally
6
6
  * from main.ts.
7
7
  *
8
- * Pair with: ui-editor.css (--ui-* chrome, opposite scope — neutral and
8
+ * Pair with: ui-editor.css (--ui-* chrome, opposite scope: neutral and
9
9
  * theme-immune; only loaded on editor pages).
10
10
  */
11
11
 
@@ -69,8 +69,10 @@ h4 {
69
69
  p {
70
70
  font-family: var(--body-md-font-family);
71
71
  font-size: var(--body-md-font-size);
72
- color: var(--text-secondary);
72
+ font-weight: var(--body-md-font-weight);
73
73
  line-height: var(--body-md-line-height);
74
+ letter-spacing: var(--body-md-letter-spacing);
75
+ color: var(--text-secondary);
74
76
  margin: 0 0 var(--space-16);
75
77
  }
76
78
 
@@ -81,12 +83,16 @@ p:last-child {
81
83
  code {
82
84
  font-family: var(--code-font-family);
83
85
  font-size: var(--code-font-size);
86
+ font-weight: var(--code-font-weight);
87
+ letter-spacing: var(--code-letter-spacing);
84
88
  }
85
89
 
86
90
  pre {
87
91
  font-family: var(--code-font-family);
88
92
  font-size: var(--code-font-size);
93
+ font-weight: var(--code-font-weight);
89
94
  line-height: var(--code-line-height);
95
+ letter-spacing: var(--code-letter-spacing);
90
96
  }
91
97
 
92
98
  a {
@@ -111,10 +117,12 @@ ul {
111
117
  }
112
118
 
113
119
  ul li {
114
- font-family: var(--font-serif);
115
- font-size: var(--font-size-md);
120
+ font-family: var(--body-md-font-family);
121
+ font-size: var(--body-md-font-size);
122
+ font-weight: var(--body-md-font-weight);
123
+ line-height: var(--body-md-line-height);
124
+ letter-spacing: var(--body-md-letter-spacing);
116
125
  color: var(--text-secondary);
117
- line-height: var(--line-height-relaxed);
118
126
  margin-bottom: var(--space-4);
119
127
  }
120
128
 
@@ -128,10 +136,12 @@ ol {
128
136
  }
129
137
 
130
138
  ol li {
131
- font-family: var(--font-sans);
132
- font-size: var(--font-size-md);
139
+ font-family: var(--body-md-font-family);
140
+ font-size: var(--body-md-font-size);
141
+ font-weight: var(--body-md-font-weight);
142
+ line-height: var(--body-md-line-height);
143
+ letter-spacing: var(--body-md-letter-spacing);
133
144
  color: var(--text-secondary);
134
- line-height: 1.6;
135
145
  margin-bottom: var(--space-4);
136
146
  }
137
147
 
@@ -20,6 +20,7 @@ import * as router from './core/routing/router';
20
20
  import * as columnsOverlay from './overlay/columnsOverlay';
21
21
  import * as editorStore from './core/store/editorStore';
22
22
  import * as themeDocumentSync from './core/themes/themeDocumentSync';
23
+ import * as liveStateStream from './core/themes/liveStateStream';
23
24
  import { initializeTheme } from './core/themes/themeInit';
24
25
  import { registerComponent, type RegisterComponentEntry } from './component-editor/registry';
25
26
  import { registerSketchStyle, type RegisterSketchStyleInput } from './core/sketch/sketchRegistry';
@@ -52,6 +53,7 @@ export async function bootLiveTokens(
52
53
 
53
54
  if (import.meta.env.DEV) {
54
55
  themeDocumentSync.init();
56
+ liveStateStream.init();
55
57
  if (opts.components) {
56
58
  for (const entry of opts.components) {
57
59
  registerComponent(entry);
@@ -20,7 +20,7 @@
20
20
  };
21
21
 
22
22
  // Header tokens per variant. Chromeless has no chrome; divider exposes the
23
- // bottom-border (the divider line) per state; container's outer chrome lives
23
+ // hairline under the header per state; container's outer chrome lives
24
24
  // in the Container part so the header strip just owns surface + padding + text.
25
25
  function headerStateTokens(v: Variant, s: HeaderState): Token[] {
26
26
  const p = `--collapsiblesection-${v}-${s}`;
@@ -32,8 +32,8 @@
32
32
  ];
33
33
  if (v === 'divider') {
34
34
  base.splice(1, 0,
35
- { label: 'divider color', groupKey: 'border', variable: `${p}-border` },
36
- { label: 'divider width', canBeLinked: true, groupKey: 'border-width', variable: `${p}-border-width` },
35
+ { label: 'divider color', groupKey: 'hairline-color', variable: `${p}-hairline-color` },
36
+ { label: 'divider thickness', canBeLinked: true, groupKey: 'hairline-thickness', variable: `${p}-hairline-thickness` },
37
37
  );
38
38
  }
39
39
  return base;
@@ -107,7 +107,7 @@
107
107
  [`--collapsiblesection-${v}-${s}-label-font-weight`, `${v} ${s}`],
108
108
  [`--collapsiblesection-${v}-${s}-label-line-height`, `${v} ${s}`],
109
109
  ];
110
- if (v === 'divider') base.push([`--collapsiblesection-divider-${s}-border-width`, `divider ${s}`]);
110
+ if (v === 'divider') base.push([`--collapsiblesection-divider-${s}-hairline-thickness`, `divider ${s}`]);
111
111
  return base;
112
112
  })),
113
113
  ['--collapsiblesection-container-frame-border-width', 'container frame'],
@@ -28,8 +28,8 @@
28
28
  ],
29
29
  header: [
30
30
  { label: 'surface color', groupKey: 'surface', variable: '--dialog-header-surface' },
31
- { label: 'border color', groupKey: 'border', variable: '--dialog-header-border' },
32
- { label: 'border width', groupKey: 'width', variable: '--dialog-header-border-width' },
31
+ { label: 'divider color', groupKey: 'border', variable: '--dialog-header-divider' },
32
+ { label: 'divider width', groupKey: 'width', variable: '--dialog-header-divider-width' },
33
33
  { label: 'padding', groupKey: 'padding', variable: '--dialog-header-padding' },
34
34
  { label: 'close icon color', groupKey: 'icon', variable: '--dialog-close-icon' },
35
35
  { label: 'close icon size', groupKey: 'size', variable: '--dialog-close-icon-size' },
@@ -38,8 +38,8 @@
38
38
  { label: 'padding', groupKey: 'padding', variable: '--dialog-body-padding' },
39
39
  ],
40
40
  footer: [
41
- { label: 'border color', groupKey: 'border', variable: '--dialog-footer-border' },
42
- { label: 'border width', groupKey: 'width', variable: '--dialog-footer-border-width' },
41
+ { label: 'divider color', groupKey: 'border', variable: '--dialog-footer-divider' },
42
+ { label: 'divider width', groupKey: 'width', variable: '--dialog-footer-divider-width' },
43
43
  { label: 'padding', groupKey: 'padding', variable: '--dialog-footer-padding' },
44
44
  ],
45
45
  };
@@ -81,7 +81,10 @@
81
81
  }],
82
82
  };
83
83
 
84
- const typeGroupTokens: Token[] = buildTypeGroupTokens(typeGroups);
84
+ const typeGroupTokens: Token[] = buildTypeGroupTokens(typeGroups, {
85
+ component,
86
+ variants: ['default', 'hover', 'selected', 'disabled'],
87
+ });
85
88
  export const allTokens: Token[] = [...Object.values(states).flat(), ...typeGroupTokens];
86
89
 
87
90
  const linkableContexts = new Map<string, string>([
@@ -110,7 +110,12 @@
110
110
 
111
111
  // allTokens unions BOTH sizes so the store registers every editable variable.
112
112
  // The visibleStates filter is purely UI (which subset to render now).
113
- const typeGroupTokens: Token[] = buildTypeGroupTokens(typeGroups);
113
+ // option-hover precedes option: the stripper removes the first match and
114
+ // would otherwise leave hover-text behind.
115
+ const typeGroupTokens: Token[] = buildTypeGroupTokens(typeGroups, {
116
+ component,
117
+ variants: ['option-hover', 'option', 'selected', 'disabled'],
118
+ });
114
119
  export const allTokens: Token[] = [
115
120
  ...Object.values(defaultStates).flat(),
116
121
  ...Object.values(smallStates).flat(),
@@ -20,7 +20,7 @@
20
20
  { label: 'padding', element: 'frame', canBeLinked: true, groupKey: 'padding', variable: `--tabbar-${s}-padding` },
21
21
  { label: 'size', element: 'icon', canBeLinked: true, groupKey: 'icon-size', variable: `--tabbar-${s}-icon-size` },
22
22
  { label: 'color', element: 'indicator', groupKey: 'indicator-color', variable: `--tabbar-${s}-border` },
23
- { label: 'width', element: 'indicator', canBeLinked: true, groupKey: 'indicator-border-width', variable: `--tabbar-${s}-indicator-border-width` },
23
+ { label: 'width', element: 'indicator', canBeLinked: true, groupKey: 'indicator-width', variable: `--tabbar-${s}-indicator-width` },
24
24
  ];
25
25
  }
26
26
  function tabStateTypeGroups(s: TabState): TypeGroupConfig[] {
@@ -15,8 +15,8 @@
15
15
  ],
16
16
  header: [
17
17
  { label: 'surface color', groupKey: 'header-surface', variable: '--table-default-header-surface' },
18
- { label: 'border color', canBeLinked: true, groupKey: 'border', variable: '--table-default-header-border' },
19
- { label: 'border width', canBeLinked: true, groupKey: 'width', variable: '--table-default-header-border-width' },
18
+ { label: 'divider color', canBeLinked: true, groupKey: 'border', variable: '--table-default-header-divider' },
19
+ { label: 'divider width', canBeLinked: true, groupKey: 'width', variable: '--table-default-header-divider-width' },
20
20
  { label: 'padding', groupKey: 'header-padding', variable: '--table-default-header-padding' },
21
21
  ],
22
22
  cell: [
@@ -68,7 +68,6 @@
68
68
  'font-size',
69
69
  'line-height',
70
70
  'letter-spacing',
71
- 'divider-width',
72
71
  'divider-height',
73
72
  'divider-inset',
74
73
  'dot-size',
@@ -85,6 +84,8 @@
85
84
  'text-color',
86
85
  'surface',
87
86
  'border-width',
87
+ 'divider-width',
88
+ 'accent-width',
88
89
  'border',
89
90
  ];
90
91
  const orderRank: Record<Kind, number> = Object.fromEntries(
@@ -135,6 +136,7 @@
135
136
  'letter-spacing': { component: UILetterSpacingSelector },
136
137
  'border-width': { component: UIVariantSelector, extra: () => ({ ...BORDER_WIDTH }) },
137
138
  'divider-width': { component: UIVariantSelector, extra: () => ({ ...BORDER_WIDTH }) },
139
+ 'accent-width': { component: UIVariantSelector, extra: () => ({ ...BORDER_WIDTH }) },
138
140
  'divider-height': { component: UIVariantSelector, extra: () => ({ ...DIVIDER_HEIGHT }) },
139
141
  'divider-inset': { component: UIVariantSelector, extra: () => ({ ...DIVIDER_INSET }) },
140
142
  'dot-size': { component: UIVariantSelector, extra: () => ({ ...DOT_SIZE }) },
@@ -165,7 +167,7 @@
165
167
  };
166
168
 
167
169
  /** Multi-col rank: same as `orderRank` but with `text-color` hoisted between
168
- `line-height` and `divider-width` so typography reads as one logical block
170
+ `line-height` and `divider-height` so typography reads as one logical block
169
171
  in column flow. Single-col mode keeps `orderRank` (linked-first sort
170
172
  already segregates text-color to the bottom). */
171
173
  const multiColRank: Record<Kind, number> = (() => {
@@ -176,7 +178,6 @@
176
178
  'line-height',
177
179
  'letter-spacing',
178
180
  'text-color',
179
- 'divider-width',
180
181
  'divider-height',
181
182
  'divider-inset',
182
183
  'dot-size',
@@ -192,6 +193,8 @@
192
193
  'shadow',
193
194
  'surface',
194
195
  'border-width',
196
+ 'divider-width',
197
+ 'accent-width',
195
198
  'border',
196
199
  ];
197
200
  return Object.fromEntries(reordered.map((k, i) => [k, i])) as Record<Kind, number>;