@motion-proto/live-tokens 0.40.0 → 0.41.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 (89) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/dist-plugin/{chunk-D77VD4Z6.js → chunk-H4TRUINI.js} +102 -13
  3. package/dist-plugin/index.cjs +288 -67
  4. package/dist-plugin/index.js +171 -55
  5. package/dist-plugin/tokensCssMigrations/index.cjs +102 -13
  6. package/dist-plugin/tokensCssMigrations/index.js +1 -1
  7. package/package.json +3 -2
  8. package/src/app/site.css +97 -20
  9. package/src/editor/core/palettes/colorHarmony.ts +228 -0
  10. package/src/editor/core/palettes/contrast.ts +128 -0
  11. package/src/editor/core/palettes/oklch.ts +8 -0
  12. package/src/editor/core/palettes/paletteDerivation.ts +267 -72
  13. package/src/editor/core/palettes/recommendText.ts +216 -0
  14. package/src/editor/core/palettes/solveTextContrast.ts +251 -0
  15. package/src/editor/core/store/editorCore.ts +2 -1
  16. package/src/editor/core/store/editorPersistence.ts +18 -1
  17. package/src/editor/core/store/editorStore.ts +20 -1
  18. package/src/editor/core/store/editorTypes.ts +4 -0
  19. package/src/editor/core/store/editorViewStore.ts +7 -3
  20. package/src/editor/core/themes/migrations/2026-06-05-palette-unification.ts +11 -9
  21. package/src/editor/core/themes/migrations/2026-07-21-palette-oklch-basis.ts +51 -0
  22. package/src/editor/core/themes/migrations/2026-07-24-base-anchor-placement.ts +37 -0
  23. package/src/editor/core/themes/migrations/2026-07-25-background-spot-to-base.ts +40 -0
  24. package/src/editor/core/themes/themeTypes.ts +23 -3
  25. package/src/editor/docs/CodeBlock.svelte +1 -1
  26. package/src/editor/docs/Docs.svelte +30 -13
  27. package/src/editor/pages/EditorShell.svelte +31 -3
  28. package/src/editor/ui/ColorEditPanel.svelte +22 -16
  29. package/src/editor/ui/EditorViewSwitcher.svelte +43 -9
  30. package/src/editor/ui/FontStackEditor.svelte +0 -1
  31. package/src/editor/ui/PaletteEditor.svelte +117 -155
  32. package/src/editor/ui/ProjectFontsSection.svelte +0 -3
  33. package/src/editor/ui/TextStylesSection.svelte +173 -0
  34. package/src/editor/ui/UILineHeightSelector.svelte +6 -5
  35. package/src/editor/ui/UIPaletteSelector.svelte +6 -1
  36. package/src/editor/ui/UITextTransformSelector.svelte +106 -0
  37. package/src/editor/ui/VariablesTab.svelte +23 -10
  38. package/src/editor/ui/colors/ColorReadouts.svelte +123 -0
  39. package/src/editor/ui/colors/ColorStory.svelte +375 -0
  40. package/src/editor/ui/colors/ColorWheel.svelte +740 -0
  41. package/src/editor/ui/colors/ColorsTab.svelte +508 -0
  42. package/src/editor/ui/colors/HarmonyAxesList.svelte +320 -0
  43. package/src/editor/ui/colors/LightnessBar.svelte +261 -0
  44. package/src/editor/ui/colors/PaletteStepStrip.svelte +142 -0
  45. package/src/editor/ui/colors/colorWheelMath.ts +9 -0
  46. package/src/editor/ui/colors/harmony-icons/analogous.svg +13 -0
  47. package/src/editor/ui/colors/harmony-icons/complementary.svg +10 -0
  48. package/src/editor/ui/colors/harmony-icons/compound.svg +15 -0
  49. package/src/editor/ui/colors/harmony-icons/custom.svg +10 -0
  50. package/src/editor/ui/colors/harmony-icons/monochromatic.svg +11 -0
  51. package/src/editor/ui/colors/harmony-icons/shades.svg +9 -0
  52. package/src/editor/ui/colors/harmony-icons/split-complementary.svg +13 -0
  53. package/src/editor/ui/colors/harmony-icons/square.svg +13 -0
  54. package/src/editor/ui/colors/harmony-icons/tetradic.svg +15 -0
  55. package/src/editor/ui/colors/harmony-icons/triadic.svg +13 -0
  56. package/src/editor/ui/colors/harmonyModeIcons.ts +23 -0
  57. package/src/editor/ui/colors/paletteBaseColor.ts +214 -0
  58. package/src/editor/ui/index.ts +0 -1
  59. package/src/editor/ui/palette/OverridesPanel.svelte +6 -3
  60. package/src/editor/ui/palette/PaletteBase.svelte +16 -8
  61. package/src/editor/ui/palette/dockMagnify.ts +22 -0
  62. package/src/editor/ui/palette/paletteEditorState.ts +5 -4
  63. package/src/editor/ui/palette/paletteMath.ts +93 -181
  64. package/src/editor/ui/sections/textStyles.ts +82 -0
  65. package/src/editor/ui/sections/tokenScales.ts +3 -3
  66. package/src/live-tokens/data/themes/default.json +3 -3
  67. package/src/live-tokens/data/tokens.generated.css +96 -95
  68. package/src/system/components/Badge.svelte +11 -11
  69. package/src/system/components/Button.svelte +7 -7
  70. package/src/system/components/Callout.svelte +8 -8
  71. package/src/system/components/Card.svelte +2 -2
  72. package/src/system/components/CodeSnippet.svelte +1 -1
  73. package/src/system/components/CollapsibleSection.svelte +7 -7
  74. package/src/system/components/CornerBadge.svelte +1 -1
  75. package/src/system/components/Dialog.svelte +2 -2
  76. package/src/system/components/FloatingTokenTags.css +6 -5
  77. package/src/system/components/Input.svelte +6 -6
  78. package/src/system/components/MenuSelect.svelte +4 -4
  79. package/src/system/components/Notification.svelte +8 -8
  80. package/src/system/components/ProgressBar.svelte +2 -2
  81. package/src/system/components/RadioButton.svelte +4 -4
  82. package/src/system/components/SectionDivider.svelte +6 -6
  83. package/src/system/components/SegmentedControl.svelte +5 -5
  84. package/src/system/components/SideNavigation.svelte +12 -12
  85. package/src/system/components/TabBar.svelte +4 -4
  86. package/src/system/components/Table.svelte +2 -2
  87. package/src/system/components/Tooltip.svelte +1 -1
  88. package/src/system/styles/tokens.css +59 -6
  89. package/src/editor/ui/TextTab.svelte +0 -203
@@ -5,7 +5,7 @@ import {
5
5
  runAdditiveTokensCssMigrations,
6
6
  runTokensCssMigrations,
7
7
  validateTokensCss
8
- } from "./chunk-D77VD4Z6.js";
8
+ } from "./chunk-H4TRUINI.js";
9
9
 
10
10
  // vite-plugin/themeFileApi.ts
11
11
  import fs2 from "fs";
@@ -94,6 +94,10 @@ function isInGamut(r, g, b) {
94
94
  const eps = 1e-4;
95
95
  return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps;
96
96
  }
97
+ function oklchToHexClamped(l, c, h) {
98
+ const g = gamutClamp(l, c, h);
99
+ return oklchToHex(g.l, g.c, g.h);
100
+ }
97
101
  function gamutClamp(l, c, h) {
98
102
  if (l <= 0) return { l: 0, c: 0, h };
99
103
  if (l >= 1) return { l: 1, c: 0, h };
@@ -151,16 +155,16 @@ function sampleCurve(anchors, xPos) {
151
155
 
152
156
  // src/editor/core/palettes/paletteDerivation.ts
153
157
  var PALETTE_SPECS = [
154
- { label: "Neutral", cssNamespace: "neutral", neutral: true, initialColor: "#70787e" },
155
- { label: "Alternate", cssNamespace: "alternate", neutral: true, initialColor: "#817b78" },
156
- { label: "Background", cssNamespace: "canvas", emptySelector: true, initialColor: "#1a1a2e" },
157
- { label: "Brand", cssNamespace: "brand", initialColor: "#c93636" },
158
- { label: "Accent", cssNamespace: "accent", initialColor: "#f49e0b" },
159
- { label: "Special", cssNamespace: "special", initialColor: "#8b5cf6" },
160
- { label: "Info", cssNamespace: "info", initialColor: "#3077e8" },
161
- { label: "Success", cssNamespace: "success", initialColor: "#21c45d" },
162
- { label: "Warning", cssNamespace: "warning", initialColor: "#e66e1a" },
163
- { label: "Danger", cssNamespace: "danger", initialColor: "#e8304f" }
158
+ { label: "Brand", cssNamespace: "brand", initialColor: { l: 0.5573, c: 0.1841, h: 25.49 } },
159
+ { label: "Accent", cssNamespace: "accent", initialColor: { l: 0.7674, c: 0.164, h: 70.44 } },
160
+ { label: "Background", cssNamespace: "canvas", emptySelector: true, initialColor: { l: 0.2284, c: 0.0384, h: 282.93 } },
161
+ { label: "Neutral", cssNamespace: "neutral", neutral: true, initialColor: { l: 0.5679, c: 0.0134, h: 240.07 } },
162
+ { label: "Alternate", cssNamespace: "alternate", neutral: true, displayLabel: "Alternate (neutral)", initialColor: { l: 0.5873, c: 87e-4, h: 48.57 } },
163
+ { label: "Special", cssNamespace: "special", initialColor: { l: 0.6056, c: 0.2189, h: 292.72 } },
164
+ { label: "Info", cssNamespace: "info", initialColor: { l: 0.5871, c: 0.1855, h: 259.56 } },
165
+ { label: "Success", cssNamespace: "success", initialColor: { l: 0.7198, c: 0.1918, h: 149.52 } },
166
+ { label: "Warning", cssNamespace: "warning", initialColor: { l: 0.6704, c: 0.1716, h: 48.75 } },
167
+ { label: "Danger", cssNamespace: "danger", initialColor: { l: 0.6103, c: 0.2165, h: 18.1 } }
164
168
  ];
165
169
  var PALETTE_STEPS = [
166
170
  { label: "100" },
@@ -212,50 +216,123 @@ var SCALES = [
212
216
  ]
213
217
  }
214
218
  ];
219
+ var BW_GUARD_MIN_L = 0.17;
220
+ var BW_GUARD_MAX_L = 0.93;
215
221
  var DEFAULT_PALETTE_LIGHTNESS = () => [makeAnchor(0, 95, 5), makeAnchor(100, 8, 5)];
216
222
  var DEFAULT_PALETTE_SATURATION = () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)];
217
- var defaultScaleCurves = {
218
- Surfaces: {
219
- lightness: () => [makeAnchor(0, 15, 5), makeAnchor(100, 47, 5)],
220
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
221
- },
222
- Borders: {
223
- lightness: () => [makeAnchor(0, 25, 5), makeAnchor(100, 80, 5)],
224
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
225
- },
226
- Text: {
227
- lightness: () => [makeAnchor(0, 120, 30), makeAnchor(100, 55, 30)],
228
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)]
223
+ function scaleCurveDefaults(scheme = "dark") {
224
+ if (scheme === "light") {
225
+ return {
226
+ Surfaces: {
227
+ lightness: () => [makeAnchor(0, 98, 5), makeAnchor(100, 82, 5)],
228
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
229
+ },
230
+ Borders: {
231
+ lightness: () => [makeAnchor(0, 92, 5), makeAnchor(100, 45, 5)],
232
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
233
+ },
234
+ Text: {
235
+ lightness: () => [makeAnchor(0, 30, 30), makeAnchor(100, 120, 30)],
236
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)]
237
+ }
238
+ };
229
239
  }
230
- };
240
+ return {
241
+ Surfaces: {
242
+ lightness: () => [makeAnchor(0, 15, 5), makeAnchor(100, 47, 5)],
243
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
244
+ },
245
+ Borders: {
246
+ lightness: () => [makeAnchor(0, 25, 5), makeAnchor(100, 80, 5)],
247
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
248
+ },
249
+ Text: {
250
+ lightness: () => [makeAnchor(0, 120, 30), makeAnchor(100, 55, 30)],
251
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)]
252
+ }
253
+ };
254
+ }
255
+ var defaultScaleCurves = scaleCurveDefaults("dark");
231
256
  function paletteStepKey(label) {
232
257
  return `Palette-${label}`;
233
258
  }
234
259
  function stepKey(scaleTitle, stepName) {
235
260
  return `${scaleTitle}-${stepName}`;
236
261
  }
237
- function stepIndexToX(index, total) {
238
- return total > 1 ? index / (total - 1) * 100 : 50;
262
+ function stepIndexToX(index) {
263
+ return index / (PALETTE_STEPS.length - 1) * 100;
264
+ }
265
+ function scaleStepToX(step, scale) {
266
+ const idx = scale.steps.indexOf(step);
267
+ return scale.steps.length > 1 ? idx / (scale.steps.length - 1) * 100 : 50;
239
268
  }
240
- function computePaletteColor(index, base, lightnessCurve, saturationCurve, curveOffset) {
241
- const { c: baseC, h } = hexToOklch(base);
242
- const xPos = stepIndexToX(index, PALETTE_STEPS.length);
269
+ function serializeDerivedValue(value) {
270
+ return value.kind === "raw" ? value.css : oklchToHexClamped(value.l, value.c, value.h);
271
+ }
272
+ function computePaletteOklch(index, base, lightnessCurve, saturationCurve, curveOffset) {
273
+ const { c: baseC, h } = base;
274
+ const xPos = stepIndexToX(index);
243
275
  const targetL = Math.max(0, Math.min(100, sampleCurve(lightnessCurve, xPos) + (curveOffset.lightness ?? 0))) / 100;
244
276
  const satMul = Math.max(0, Math.min(2, (sampleCurve(saturationCurve, xPos) + (curveOffset.saturation ?? 0)) / 100));
245
277
  const targetC = baseC * satMul;
246
- const clamped = gamutClamp(targetL, targetC, h);
247
- return oklchToHex(clamped.l, clamped.c, clamped.h);
278
+ return gamutClamp(targetL, targetC, h);
279
+ }
280
+ function nearestPaletteStep(lightnessCurve, baseL) {
281
+ let best = 0;
282
+ let bestD = Infinity;
283
+ for (let i = 0; i < PALETTE_STEPS.length; i++) {
284
+ const d = Math.abs(sampleCurve(lightnessCurve, stepIndexToX(i)) - baseL * 100);
285
+ if (d < bestD) {
286
+ bestD = d;
287
+ best = i;
288
+ }
289
+ }
290
+ return best;
291
+ }
292
+ function setCurveAnchor(curve, x, y) {
293
+ const existing = curve.findIndex((a) => Math.abs(a.x - x) < 0.5);
294
+ if (existing >= 0) {
295
+ const displacedY = curve[existing].y;
296
+ return { curve: curve.map((a, i) => i === existing ? { ...a, x, y } : a), displacedY };
297
+ }
298
+ let insertAt = curve.findIndex((a) => a.x > x);
299
+ if (insertAt < 0) insertAt = curve.length;
300
+ return { curve: [...curve.slice(0, insertAt), makeAnchor(x, y, 15), ...curve.slice(insertAt)] };
301
+ }
302
+ function liftCurveAnchor(curve, x, displacedY) {
303
+ const idx = curve.findIndex((a) => Math.abs(a.x - x) < 0.5);
304
+ if (idx < 0) return curve;
305
+ if (displacedY !== void 0) return curve.map((a, i) => i === idx ? { ...a, y: displacedY } : a);
306
+ if (idx === 0 || idx === curve.length - 1) return curve;
307
+ return curve.filter((_, i) => i !== idx);
308
+ }
309
+ function syncBaseAnchor(cfg) {
310
+ if (cfg.anchorToBase === false) return;
311
+ const prev = cfg.anchorPlacement;
312
+ let lCurve = cfg.lightnessCurve;
313
+ let sCurve = cfg.saturationCurve;
314
+ if (prev) {
315
+ const prevX = stepIndexToX(prev.step);
316
+ lCurve = liftCurveAnchor(lCurve, prevX, prev.displacedL);
317
+ sCurve = liftCurveAnchor(sCurve, prevX, prev.displacedS);
318
+ }
319
+ const step = nearestPaletteStep(lCurve, cfg.baseColor.l);
320
+ const x = stepIndexToX(step);
321
+ const l = setCurveAnchor(lCurve, x, cfg.baseColor.l * 100);
322
+ const s = setCurveAnchor(sCurve, x, 100);
323
+ cfg.lightnessCurve = l.curve;
324
+ cfg.saturationCurve = s.curve;
325
+ cfg.anchorPlacement = { step, displacedL: l.displacedY, displacedS: s.displacedY };
248
326
  }
249
- function computeDerivedColor(step, base, scaleTitle, scaleCurves, curveOffset) {
327
+ function computeDerivedOklch(step, base, scaleTitle, scaleCurves, curveOffset) {
250
328
  const scale = SCALES.find((s) => s.title === scaleTitle);
251
- const idx = scale.steps.indexOf(step);
252
- const xPos = stepIndexToX(idx, scale.steps.length);
329
+ const xPos = scaleStepToX(step, scale);
253
330
  const defs = defaultScaleCurves[scaleTitle];
254
331
  const lCurve = scaleCurves[scaleTitle]?.lightness ?? defs.lightness();
255
332
  const sCurve = scaleCurves[scaleTitle]?.saturation ?? defs.saturation();
256
333
  const lOff = curveOffset[`${scaleTitle}-lightness`] ?? 0;
257
334
  const sOff = curveOffset[`${scaleTitle}-saturation`] ?? 0;
258
- const { l: baseL, c: baseC, h: baseH } = hexToOklch(base);
335
+ const { l: baseL, c: baseC, h: baseH } = base;
259
336
  let targetL;
260
337
  if (scale.isText) {
261
338
  const lMul = Math.max(0, Math.min(2, (sampleCurve(lCurve, xPos) + lOff) / 100));
@@ -265,8 +342,7 @@ function computeDerivedColor(step, base, scaleTitle, scaleCurves, curveOffset) {
265
342
  }
266
343
  const satMul = Math.max(0, Math.min(2, (sampleCurve(sCurve, xPos) + sOff) / 100));
267
344
  const targetC = baseC * satMul;
268
- const clamped = gamutClamp(targetL, targetC, baseH);
269
- return oklchToHex(clamped.l, clamped.c, clamped.h);
345
+ return gamutClamp(targetL, targetC, baseH);
270
346
  }
271
347
  function scaleToCssVar(scaleTitle, stepName, cssNamespace) {
272
348
  if (cssNamespace === null) return null;
@@ -284,7 +360,7 @@ function scaleToCssVar(scaleTitle, stepName, cssNamespace) {
284
360
  }
285
361
  return null;
286
362
  }
287
- function derivePaletteVars(spec, config) {
363
+ function derivePaletteValues(spec, config) {
288
364
  const out = {};
289
365
  if (!config) return out;
290
366
  const baseColor = config.baseColor ?? spec.initialColor;
@@ -295,21 +371,24 @@ function derivePaletteVars(spec, config) {
295
371
  const saturationCurve = config.saturationCurve ?? DEFAULT_PALETTE_SATURATION();
296
372
  PALETTE_STEPS.forEach((ps, index) => {
297
373
  const k = paletteStepKey(ps.label);
298
- const hex = computePaletteColor(index, baseColor, lightnessCurve, saturationCurve, curveOffset);
299
- const effective = k in overrides ? overrides[k] : hex;
300
- out[`--color-${spec.cssNamespace}-${ps.label}`] = effective;
374
+ const value = k in overrides ? { kind: "color", ...overrides[k] } : { kind: "color", ...computePaletteOklch(index, baseColor, lightnessCurve, saturationCurve, curveOffset) };
375
+ out[`--color-${spec.cssNamespace}-${ps.label}`] = value;
301
376
  });
302
377
  for (const scale of SCALES) {
303
378
  for (const step of scale.steps) {
304
379
  const k = stepKey(scale.title, step.name);
305
- const hex = k in overrides ? overrides[k] : computeDerivedColor(step, baseColor, scale.title, scaleCurves, curveOffset);
380
+ const value = k in overrides ? { kind: "color", ...overrides[k] } : { kind: "color", ...computeDerivedOklch(step, baseColor, scale.title, scaleCurves, curveOffset) };
306
381
  const varName = scaleToCssVar(scale.title, step.name, spec.cssNamespace);
307
- if (varName) out[varName] = hex;
382
+ if (varName) out[varName] = value;
308
383
  }
309
384
  }
385
+ const textPrimary = out["--text-primary"];
386
+ if (textPrimary?.kind === "color") {
387
+ const mirroredL = Math.max(BW_GUARD_MIN_L, Math.min(BW_GUARD_MAX_L, 1 - textPrimary.l));
388
+ out["--text-inverted"] = { kind: "color", ...gamutClamp(mirroredL, textPrimary.c, textPrimary.h) };
389
+ }
310
390
  if (spec.emptySelector) {
311
391
  const emptyMode = config.emptyMode ?? "solid";
312
- const emptyStep = config.emptyStep ?? "850";
313
392
  const gradientStyle = config.gradientStyle ?? "linear";
314
393
  const gradientAngle = config.gradientAngle ?? 180;
315
394
  const gradientReverse = config.gradientReverse ?? false;
@@ -319,14 +398,17 @@ function derivePaletteVars(spec, config) {
319
398
  { position: 100, paletteLabel: "950" }
320
399
  ];
321
400
  if (emptyMode === "solid") {
322
- const stepHex = out[`--color-${spec.cssNamespace}-${emptyStep}`];
323
- if (stepHex) out["--page-bg"] = stepHex;
324
- out["--page-bg-attachment"] = "scroll";
401
+ out["--page-bg"] = { kind: "color", ...baseColor };
402
+ out["--page-bg-attachment"] = { kind: "raw", css: "scroll" };
325
403
  } else {
326
404
  const sortedStops = [...gradientStops].sort(
327
405
  (a, b) => gradientReverse ? b.position - a.position : a.position - b.position
328
406
  );
329
- const stopsCss = sortedStops.map((s) => `${out[`--color-${spec.cssNamespace}-${s.paletteLabel}`] ?? "#000000"} ${s.position}%`).join(", ");
407
+ const stopsCss = sortedStops.map((s) => {
408
+ const stopValue = out[`--color-${spec.cssNamespace}-${s.paletteLabel}`];
409
+ const hex = stopValue ? serializeDerivedValue(stopValue) : "#000000";
410
+ return `${hex} ${s.position}%`;
411
+ }).join(", ");
330
412
  let gradient;
331
413
  switch (gradientStyle) {
332
414
  case "radial":
@@ -338,16 +420,30 @@ function derivePaletteVars(spec, config) {
338
420
  default:
339
421
  gradient = `linear-gradient(${gradientAngle}deg, ${stopsCss})`;
340
422
  }
341
- out["--page-bg"] = gradient;
342
- out["--page-bg-attachment"] = gradientSize === "window" ? "fixed" : "scroll";
423
+ out["--page-bg"] = { kind: "raw", css: gradient };
424
+ out["--page-bg-attachment"] = { kind: "raw", css: gradientSize === "window" ? "fixed" : "scroll" };
343
425
  }
344
426
  }
345
427
  return out;
346
428
  }
347
- function palettesToVars(palettes) {
429
+ function derivePaletteVars(spec, config) {
430
+ const out = {};
431
+ for (const [k, v] of Object.entries(derivePaletteValues(spec, config))) {
432
+ out[k] = serializeDerivedValue(v);
433
+ }
434
+ return out;
435
+ }
436
+ function palettesToValues(palettes) {
348
437
  const out = {};
349
438
  for (const spec of PALETTE_SPECS) {
350
- Object.assign(out, derivePaletteVars(spec, palettes[spec.label]));
439
+ Object.assign(out, derivePaletteValues(spec, palettes[spec.label]));
440
+ }
441
+ return out;
442
+ }
443
+ function palettesToVars(palettes) {
444
+ const out = {};
445
+ for (const [k, v] of Object.entries(palettesToValues(palettes))) {
446
+ out[k] = serializeDerivedValue(v);
351
447
  }
352
448
  return out;
353
449
  }
@@ -362,8 +458,8 @@ function reconcilePalettesFromCssVars(palettes, cssVars) {
362
458
  if (current._imported === true) {
363
459
  const anchorHex = cssVars[`--color-${spec.cssNamespace}-500`];
364
460
  if (anchorHex && HEX_RE.test(anchorHex.trim())) {
365
- const hex = anchorHex.trim();
366
- next[spec.label] = { ...current, baseColor: hex, _imported: false };
461
+ next[spec.label] = { ...current, baseColor: hexToOklch(anchorHex.trim()), _imported: false };
462
+ syncBaseAnchor(next[spec.label]);
367
463
  snapped.add(spec.label);
368
464
  } else {
369
465
  next[spec.label] = { ...current, _imported: false };
@@ -376,6 +472,26 @@ function reconcilePalettesFromCssVars(palettes, cssVars) {
376
472
  return { palettes: next, consumed, snapped };
377
473
  }
378
474
 
475
+ // src/editor/core/themes/migrations/2026-07-21-palette-oklch-basis.ts
476
+ var round = (v, dp) => {
477
+ const f = 10 ** dp;
478
+ return Math.round(v * f) / f;
479
+ };
480
+ function toStoredOklch(value) {
481
+ if (typeof value !== "string") return value;
482
+ const { l, c, h } = hexToOklch(value);
483
+ return { l: round(l, 4), c: round(c, 4), h: round(h, 2) };
484
+ }
485
+ function migratePaletteColorsToOklch(editorConfigs) {
486
+ const out = {};
487
+ for (const [label, cfg] of Object.entries(editorConfigs)) {
488
+ const overrides = {};
489
+ for (const [k, v] of Object.entries(cfg.overrides ?? {})) overrides[k] = toStoredOklch(v);
490
+ out[label] = { ...cfg, baseColor: toStoredOklch(cfg.baseColor), overrides };
491
+ }
492
+ return out;
493
+ }
494
+
379
495
  // vite-plugin/files/versionedFileResourceServer.ts
380
496
  import fs from "fs";
381
497
  import path from "path";
@@ -654,7 +770,7 @@ function themeFileApi(opts) {
654
770
  let themeVarCount = 0;
655
771
  if (themeData) {
656
772
  const cssVars = { ...themeData.cssVariables || {} };
657
- Object.assign(cssVars, palettesToVars(themeData.editorConfigs ?? {}));
773
+ Object.assign(cssVars, palettesToVars(migratePaletteColorsToOklch(themeData.editorConfigs ?? {})));
658
774
  const resolvedFontVars = resolveFontStacks(themeData);
659
775
  for (const [name, value] of Object.entries(resolvedFontVars)) {
660
776
  cssVars[name] = value;
@@ -156,20 +156,9 @@ function escapeRe(s) {
156
156
  var tokensCssMigration_2026_05_29_typographyScaleAdditions = {
157
157
  id: "2026-05-29-typography-scale-additions",
158
158
  kind: "additive",
159
- description: "Add --line-height-{xs..xl}, --letter-spacing-* and --ease-out-quart scales",
159
+ description: "Add --letter-spacing-* and --ease-out-quart scales",
160
160
  apply(css) {
161
161
  let out = css;
162
- out = ensureScale(out, {
163
- sectionComment: "Line height (t-shirt scale)",
164
- anchorPrefixes: ["--line-height-", "--font-size-", "--font-weight-", "--font-"],
165
- entries: [
166
- { name: "--line-height-xs", value: "1" },
167
- { name: "--line-height-sm", value: "1.25" },
168
- { name: "--line-height-md", value: "1.5" },
169
- { name: "--line-height-lg", value: "1.75" },
170
- { name: "--line-height-xl", value: "2" }
171
- ]
172
- });
173
162
  out = ensureScale(out, {
174
163
  sectionComment: "Letter spacing",
175
164
  anchorPrefixes: ["--letter-spacing-", "--line-height-", "--font-size-", "--font-"],
@@ -312,6 +301,104 @@ var tokensCssMigration_2026_06_04_easingColorAndTypescaleAdditions = {
312
301
  }
313
302
  };
314
303
 
304
+ // vite-plugin/tokensCssMigrations/migrations/2026-07-20-line-height-rename.ts
305
+ var RENAMES = [
306
+ ["--line-height-xs", "--line-height-none"],
307
+ ["--line-height-sm", "--line-height-tighter"],
308
+ ["--line-height-md", "--line-height-normal"],
309
+ ["--line-height-lg", "--line-height-relaxed"]
310
+ ];
311
+ var SCALE = [
312
+ { name: "--line-height-none", value: "1" },
313
+ { name: "--line-height-tightest", value: "1.1" },
314
+ { name: "--line-height-tighter", value: "1.25" },
315
+ { name: "--line-height-tight", value: "1.35" },
316
+ { name: "--line-height-normal", value: "1.5" },
317
+ { name: "--line-height-relaxed", value: "1.75" }
318
+ ];
319
+ var tokensCssMigration_2026_07_20_lineHeightRename = {
320
+ id: "2026-07-20-line-height-rename",
321
+ kind: "breaking",
322
+ description: "Reshape --line-height-{xs..xl} to leading vocabulary (none/tightest/tighter/tight/normal/relaxed); retire the 2.0 slot",
323
+ apply(css) {
324
+ let out = css;
325
+ for (const [oldName, newName] of RENAMES) out = renameToken(out, oldName, newName);
326
+ out = removeToken(out, "--line-height-xl");
327
+ out = ensureScale(out, {
328
+ anchorPrefixes: ["--line-height-", "--font-size-", "--font-weight-", "--font-"],
329
+ entries: SCALE
330
+ });
331
+ return out;
332
+ }
333
+ };
334
+
335
+ // vite-plugin/tokensCssMigrations/migrations/2026-07-20-semantic-text-styles.ts
336
+ var BUNDLE = [
337
+ { name: "--heading-xl-font-family", value: "var(--font-display)" },
338
+ { name: "--heading-xl-font-size", value: "var(--font-size-4xl)" },
339
+ { name: "--heading-xl-font-weight", value: "var(--font-weight-semibold)" },
340
+ { name: "--heading-xl-line-height", value: "var(--line-height-tightest)" },
341
+ { name: "--heading-xl-letter-spacing", value: "var(--letter-spacing-normal)" },
342
+ { name: "--heading-lg-font-family", value: "var(--font-display)" },
343
+ { name: "--heading-lg-font-size", value: "var(--font-size-2xl)" },
344
+ { name: "--heading-lg-font-weight", value: "var(--font-weight-semibold)" },
345
+ { name: "--heading-lg-line-height", value: "var(--line-height-tightest)" },
346
+ { name: "--heading-lg-letter-spacing", value: "var(--letter-spacing-normal)" },
347
+ { name: "--heading-md-font-family", value: "var(--font-display)" },
348
+ { name: "--heading-md-font-size", value: "var(--font-size-xl)" },
349
+ { name: "--heading-md-font-weight", value: "var(--font-weight-semibold)" },
350
+ { name: "--heading-md-line-height", value: "var(--line-height-tighter)" },
351
+ { name: "--heading-md-letter-spacing", value: "var(--letter-spacing-normal)" },
352
+ { name: "--heading-sm-font-family", value: "var(--font-sans)" },
353
+ { name: "--heading-sm-font-size", value: "var(--font-size-lg)" },
354
+ { name: "--heading-sm-font-weight", value: "var(--font-weight-semibold)" },
355
+ { name: "--heading-sm-line-height", value: "var(--line-height-tighter)" },
356
+ { name: "--heading-sm-letter-spacing", value: "var(--letter-spacing-normal)" },
357
+ { name: "--body-md-font-family", value: "var(--font-sans)" },
358
+ { name: "--body-md-font-size", value: "var(--font-size-md)" },
359
+ { name: "--body-md-font-weight", value: "var(--font-weight-normal)" },
360
+ { name: "--body-md-line-height", value: "var(--line-height-normal)" },
361
+ { name: "--body-md-letter-spacing", value: "var(--letter-spacing-normal)" },
362
+ { name: "--body-sm-font-family", value: "var(--font-sans)" },
363
+ { name: "--body-sm-font-size", value: "var(--font-size-sm)" },
364
+ { name: "--body-sm-font-weight", value: "var(--font-weight-normal)" },
365
+ { name: "--body-sm-line-height", value: "var(--line-height-tight)" },
366
+ { name: "--body-sm-letter-spacing", value: "var(--letter-spacing-normal)" },
367
+ { name: "--code-font-family", value: "var(--font-mono)" },
368
+ { name: "--code-font-size", value: "var(--font-size-sm)" },
369
+ { name: "--code-font-weight", value: "var(--font-weight-normal)" },
370
+ { name: "--code-line-height", value: "var(--line-height-normal)" },
371
+ { name: "--code-letter-spacing", value: "var(--letter-spacing-normal)" },
372
+ { name: "--eyebrow-font-family", value: "var(--font-sans)" },
373
+ { name: "--eyebrow-font-size", value: "var(--font-size-sm)" },
374
+ { name: "--eyebrow-font-weight", value: "var(--font-weight-normal)" },
375
+ { name: "--eyebrow-line-height", value: "var(--line-height-normal)" },
376
+ { name: "--eyebrow-letter-spacing", value: "var(--letter-spacing-normal)" },
377
+ { name: "--eyebrow-text-transform", value: "none" }
378
+ ];
379
+ var tokensCssMigration_2026_07_20_semanticTextStyles = {
380
+ id: "2026-07-20-semantic-text-styles",
381
+ kind: "additive",
382
+ description: "Add the semantic text-style bundles (--heading-*/--body-*/--code-*/--eyebrow-*)",
383
+ apply(css) {
384
+ return ensureScale(css, {
385
+ sectionComment: "Text styles \u2014 semantic typography roles",
386
+ anchorPrefixes: [
387
+ "--eyebrow-",
388
+ "--code-",
389
+ "--body-",
390
+ "--heading-",
391
+ "--letter-spacing-",
392
+ "--line-height-",
393
+ "--font-weight-",
394
+ "--font-size-",
395
+ "--font-"
396
+ ],
397
+ entries: BUNDLE
398
+ });
399
+ }
400
+ };
401
+
315
402
  // vite-plugin/files/dataPaths.ts
316
403
  var import_fs = __toESM(require("fs"), 1);
317
404
  var import_path = __toESM(require("path"), 1);
@@ -351,7 +438,9 @@ var TOKENS_CSS_MIGRATIONS = [
351
438
  tokensCssMigration_2026_05_29_sectiondividerLegacyAxisCleanup,
352
439
  tokensCssMigration_2026_06_03_transformScaleAdditions,
353
440
  tokensCssMigration_2026_06_04_removeDeadSizeIconScale,
354
- tokensCssMigration_2026_06_04_easingColorAndTypescaleAdditions
441
+ tokensCssMigration_2026_06_04_easingColorAndTypescaleAdditions,
442
+ tokensCssMigration_2026_07_20_lineHeightRename,
443
+ tokensCssMigration_2026_07_20_semanticTextStyles
355
444
  ];
356
445
  function runTokensCssMigrations(css) {
357
446
  return foldMigrations(css, () => true);
@@ -13,7 +13,7 @@ import {
13
13
  runTokensCssMigrations,
14
14
  semverBumpType,
15
15
  validateTokensCss
16
- } from "../chunk-D77VD4Z6.js";
16
+ } from "../chunk-H4TRUINI.js";
17
17
  export {
18
18
  TOKENS_CSS_MIGRATIONS,
19
19
  collectDefinedTokens,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motion-proto/live-tokens",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
4
4
  "type": "module",
5
5
  "description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
6
6
  "keywords": [
@@ -32,7 +32,8 @@
32
32
  "template",
33
33
  "!**/*.test.ts",
34
34
  "!**/*.spec.ts",
35
- "!**/__tests__/**"
35
+ "!**/__tests__/**",
36
+ "!**/__snapshots__/**"
36
37
  ],
37
38
  "bin": {
38
39
  "live-tokens": "./bin/cli.mjs"