@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
@@ -135,6 +135,10 @@ function isInGamut(r, g, b) {
135
135
  const eps = 1e-4;
136
136
  return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps;
137
137
  }
138
+ function oklchToHexClamped(l, c, h) {
139
+ const g = gamutClamp(l, c, h);
140
+ return oklchToHex(g.l, g.c, g.h);
141
+ }
138
142
  function gamutClamp(l, c, h) {
139
143
  if (l <= 0) return { l: 0, c: 0, h };
140
144
  if (l >= 1) return { l: 1, c: 0, h };
@@ -192,16 +196,16 @@ function sampleCurve(anchors, xPos) {
192
196
 
193
197
  // src/editor/core/palettes/paletteDerivation.ts
194
198
  var PALETTE_SPECS = [
195
- { label: "Neutral", cssNamespace: "neutral", neutral: true, initialColor: "#70787e" },
196
- { label: "Alternate", cssNamespace: "alternate", neutral: true, initialColor: "#817b78" },
197
- { label: "Background", cssNamespace: "canvas", emptySelector: true, initialColor: "#1a1a2e" },
198
- { label: "Brand", cssNamespace: "brand", initialColor: "#c93636" },
199
- { label: "Accent", cssNamespace: "accent", initialColor: "#f49e0b" },
200
- { label: "Special", cssNamespace: "special", initialColor: "#8b5cf6" },
201
- { label: "Info", cssNamespace: "info", initialColor: "#3077e8" },
202
- { label: "Success", cssNamespace: "success", initialColor: "#21c45d" },
203
- { label: "Warning", cssNamespace: "warning", initialColor: "#e66e1a" },
204
- { label: "Danger", cssNamespace: "danger", initialColor: "#e8304f" }
199
+ { label: "Brand", cssNamespace: "brand", initialColor: { l: 0.5573, c: 0.1841, h: 25.49 } },
200
+ { label: "Accent", cssNamespace: "accent", initialColor: { l: 0.7674, c: 0.164, h: 70.44 } },
201
+ { label: "Background", cssNamespace: "canvas", emptySelector: true, initialColor: { l: 0.2284, c: 0.0384, h: 282.93 } },
202
+ { label: "Neutral", cssNamespace: "neutral", neutral: true, initialColor: { l: 0.5679, c: 0.0134, h: 240.07 } },
203
+ { label: "Alternate", cssNamespace: "alternate", neutral: true, displayLabel: "Alternate (neutral)", initialColor: { l: 0.5873, c: 87e-4, h: 48.57 } },
204
+ { label: "Special", cssNamespace: "special", initialColor: { l: 0.6056, c: 0.2189, h: 292.72 } },
205
+ { label: "Info", cssNamespace: "info", initialColor: { l: 0.5871, c: 0.1855, h: 259.56 } },
206
+ { label: "Success", cssNamespace: "success", initialColor: { l: 0.7198, c: 0.1918, h: 149.52 } },
207
+ { label: "Warning", cssNamespace: "warning", initialColor: { l: 0.6704, c: 0.1716, h: 48.75 } },
208
+ { label: "Danger", cssNamespace: "danger", initialColor: { l: 0.6103, c: 0.2165, h: 18.1 } }
205
209
  ];
206
210
  var PALETTE_STEPS = [
207
211
  { label: "100" },
@@ -253,50 +257,123 @@ var SCALES = [
253
257
  ]
254
258
  }
255
259
  ];
260
+ var BW_GUARD_MIN_L = 0.17;
261
+ var BW_GUARD_MAX_L = 0.93;
256
262
  var DEFAULT_PALETTE_LIGHTNESS = () => [makeAnchor(0, 95, 5), makeAnchor(100, 8, 5)];
257
263
  var DEFAULT_PALETTE_SATURATION = () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)];
258
- var defaultScaleCurves = {
259
- Surfaces: {
260
- lightness: () => [makeAnchor(0, 15, 5), makeAnchor(100, 47, 5)],
261
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
262
- },
263
- Borders: {
264
- lightness: () => [makeAnchor(0, 25, 5), makeAnchor(100, 80, 5)],
265
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
266
- },
267
- Text: {
268
- lightness: () => [makeAnchor(0, 120, 30), makeAnchor(100, 55, 30)],
269
- saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)]
264
+ function scaleCurveDefaults(scheme = "dark") {
265
+ if (scheme === "light") {
266
+ return {
267
+ Surfaces: {
268
+ lightness: () => [makeAnchor(0, 98, 5), makeAnchor(100, 82, 5)],
269
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
270
+ },
271
+ Borders: {
272
+ lightness: () => [makeAnchor(0, 92, 5), makeAnchor(100, 45, 5)],
273
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
274
+ },
275
+ Text: {
276
+ lightness: () => [makeAnchor(0, 30, 30), makeAnchor(100, 120, 30)],
277
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)]
278
+ }
279
+ };
270
280
  }
271
- };
281
+ return {
282
+ Surfaces: {
283
+ lightness: () => [makeAnchor(0, 15, 5), makeAnchor(100, 47, 5)],
284
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
285
+ },
286
+ Borders: {
287
+ lightness: () => [makeAnchor(0, 25, 5), makeAnchor(100, 80, 5)],
288
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 100, 30)]
289
+ },
290
+ Text: {
291
+ lightness: () => [makeAnchor(0, 120, 30), makeAnchor(100, 55, 30)],
292
+ saturation: () => [makeAnchor(0, 100, 30), makeAnchor(100, 15, 30)]
293
+ }
294
+ };
295
+ }
296
+ var defaultScaleCurves = scaleCurveDefaults("dark");
272
297
  function paletteStepKey(label) {
273
298
  return `Palette-${label}`;
274
299
  }
275
300
  function stepKey(scaleTitle, stepName) {
276
301
  return `${scaleTitle}-${stepName}`;
277
302
  }
278
- function stepIndexToX(index, total) {
279
- return total > 1 ? index / (total - 1) * 100 : 50;
303
+ function stepIndexToX(index) {
304
+ return index / (PALETTE_STEPS.length - 1) * 100;
305
+ }
306
+ function scaleStepToX(step, scale) {
307
+ const idx = scale.steps.indexOf(step);
308
+ return scale.steps.length > 1 ? idx / (scale.steps.length - 1) * 100 : 50;
309
+ }
310
+ function serializeDerivedValue(value) {
311
+ return value.kind === "raw" ? value.css : oklchToHexClamped(value.l, value.c, value.h);
280
312
  }
281
- function computePaletteColor(index, base, lightnessCurve, saturationCurve, curveOffset) {
282
- const { c: baseC, h } = hexToOklch(base);
283
- const xPos = stepIndexToX(index, PALETTE_STEPS.length);
313
+ function computePaletteOklch(index, base, lightnessCurve, saturationCurve, curveOffset) {
314
+ const { c: baseC, h } = base;
315
+ const xPos = stepIndexToX(index);
284
316
  const targetL = Math.max(0, Math.min(100, sampleCurve(lightnessCurve, xPos) + (curveOffset.lightness ?? 0))) / 100;
285
317
  const satMul = Math.max(0, Math.min(2, (sampleCurve(saturationCurve, xPos) + (curveOffset.saturation ?? 0)) / 100));
286
318
  const targetC = baseC * satMul;
287
- const clamped = gamutClamp(targetL, targetC, h);
288
- return oklchToHex(clamped.l, clamped.c, clamped.h);
319
+ return gamutClamp(targetL, targetC, h);
320
+ }
321
+ function nearestPaletteStep(lightnessCurve, baseL) {
322
+ let best = 0;
323
+ let bestD = Infinity;
324
+ for (let i = 0; i < PALETTE_STEPS.length; i++) {
325
+ const d = Math.abs(sampleCurve(lightnessCurve, stepIndexToX(i)) - baseL * 100);
326
+ if (d < bestD) {
327
+ bestD = d;
328
+ best = i;
329
+ }
330
+ }
331
+ return best;
332
+ }
333
+ function setCurveAnchor(curve, x, y) {
334
+ const existing = curve.findIndex((a) => Math.abs(a.x - x) < 0.5);
335
+ if (existing >= 0) {
336
+ const displacedY = curve[existing].y;
337
+ return { curve: curve.map((a, i) => i === existing ? { ...a, x, y } : a), displacedY };
338
+ }
339
+ let insertAt = curve.findIndex((a) => a.x > x);
340
+ if (insertAt < 0) insertAt = curve.length;
341
+ return { curve: [...curve.slice(0, insertAt), makeAnchor(x, y, 15), ...curve.slice(insertAt)] };
289
342
  }
290
- function computeDerivedColor(step, base, scaleTitle, scaleCurves, curveOffset) {
343
+ function liftCurveAnchor(curve, x, displacedY) {
344
+ const idx = curve.findIndex((a) => Math.abs(a.x - x) < 0.5);
345
+ if (idx < 0) return curve;
346
+ if (displacedY !== void 0) return curve.map((a, i) => i === idx ? { ...a, y: displacedY } : a);
347
+ if (idx === 0 || idx === curve.length - 1) return curve;
348
+ return curve.filter((_, i) => i !== idx);
349
+ }
350
+ function syncBaseAnchor(cfg) {
351
+ if (cfg.anchorToBase === false) return;
352
+ const prev = cfg.anchorPlacement;
353
+ let lCurve = cfg.lightnessCurve;
354
+ let sCurve = cfg.saturationCurve;
355
+ if (prev) {
356
+ const prevX = stepIndexToX(prev.step);
357
+ lCurve = liftCurveAnchor(lCurve, prevX, prev.displacedL);
358
+ sCurve = liftCurveAnchor(sCurve, prevX, prev.displacedS);
359
+ }
360
+ const step = nearestPaletteStep(lCurve, cfg.baseColor.l);
361
+ const x = stepIndexToX(step);
362
+ const l = setCurveAnchor(lCurve, x, cfg.baseColor.l * 100);
363
+ const s = setCurveAnchor(sCurve, x, 100);
364
+ cfg.lightnessCurve = l.curve;
365
+ cfg.saturationCurve = s.curve;
366
+ cfg.anchorPlacement = { step, displacedL: l.displacedY, displacedS: s.displacedY };
367
+ }
368
+ function computeDerivedOklch(step, base, scaleTitle, scaleCurves, curveOffset) {
291
369
  const scale = SCALES.find((s) => s.title === scaleTitle);
292
- const idx = scale.steps.indexOf(step);
293
- const xPos = stepIndexToX(idx, scale.steps.length);
370
+ const xPos = scaleStepToX(step, scale);
294
371
  const defs = defaultScaleCurves[scaleTitle];
295
372
  const lCurve = scaleCurves[scaleTitle]?.lightness ?? defs.lightness();
296
373
  const sCurve = scaleCurves[scaleTitle]?.saturation ?? defs.saturation();
297
374
  const lOff = curveOffset[`${scaleTitle}-lightness`] ?? 0;
298
375
  const sOff = curveOffset[`${scaleTitle}-saturation`] ?? 0;
299
- const { l: baseL, c: baseC, h: baseH } = hexToOklch(base);
376
+ const { l: baseL, c: baseC, h: baseH } = base;
300
377
  let targetL;
301
378
  if (scale.isText) {
302
379
  const lMul = Math.max(0, Math.min(2, (sampleCurve(lCurve, xPos) + lOff) / 100));
@@ -306,8 +383,7 @@ function computeDerivedColor(step, base, scaleTitle, scaleCurves, curveOffset) {
306
383
  }
307
384
  const satMul = Math.max(0, Math.min(2, (sampleCurve(sCurve, xPos) + sOff) / 100));
308
385
  const targetC = baseC * satMul;
309
- const clamped = gamutClamp(targetL, targetC, baseH);
310
- return oklchToHex(clamped.l, clamped.c, clamped.h);
386
+ return gamutClamp(targetL, targetC, baseH);
311
387
  }
312
388
  function scaleToCssVar(scaleTitle, stepName, cssNamespace) {
313
389
  if (cssNamespace === null) return null;
@@ -325,7 +401,7 @@ function scaleToCssVar(scaleTitle, stepName, cssNamespace) {
325
401
  }
326
402
  return null;
327
403
  }
328
- function derivePaletteVars(spec, config) {
404
+ function derivePaletteValues(spec, config) {
329
405
  const out = {};
330
406
  if (!config) return out;
331
407
  const baseColor = config.baseColor ?? spec.initialColor;
@@ -336,21 +412,24 @@ function derivePaletteVars(spec, config) {
336
412
  const saturationCurve = config.saturationCurve ?? DEFAULT_PALETTE_SATURATION();
337
413
  PALETTE_STEPS.forEach((ps, index) => {
338
414
  const k = paletteStepKey(ps.label);
339
- const hex = computePaletteColor(index, baseColor, lightnessCurve, saturationCurve, curveOffset);
340
- const effective = k in overrides ? overrides[k] : hex;
341
- out[`--color-${spec.cssNamespace}-${ps.label}`] = effective;
415
+ const value = k in overrides ? { kind: "color", ...overrides[k] } : { kind: "color", ...computePaletteOklch(index, baseColor, lightnessCurve, saturationCurve, curveOffset) };
416
+ out[`--color-${spec.cssNamespace}-${ps.label}`] = value;
342
417
  });
343
418
  for (const scale of SCALES) {
344
419
  for (const step of scale.steps) {
345
420
  const k = stepKey(scale.title, step.name);
346
- const hex = k in overrides ? overrides[k] : computeDerivedColor(step, baseColor, scale.title, scaleCurves, curveOffset);
421
+ const value = k in overrides ? { kind: "color", ...overrides[k] } : { kind: "color", ...computeDerivedOklch(step, baseColor, scale.title, scaleCurves, curveOffset) };
347
422
  const varName = scaleToCssVar(scale.title, step.name, spec.cssNamespace);
348
- if (varName) out[varName] = hex;
423
+ if (varName) out[varName] = value;
349
424
  }
350
425
  }
426
+ const textPrimary = out["--text-primary"];
427
+ if (textPrimary?.kind === "color") {
428
+ const mirroredL = Math.max(BW_GUARD_MIN_L, Math.min(BW_GUARD_MAX_L, 1 - textPrimary.l));
429
+ out["--text-inverted"] = { kind: "color", ...gamutClamp(mirroredL, textPrimary.c, textPrimary.h) };
430
+ }
351
431
  if (spec.emptySelector) {
352
432
  const emptyMode = config.emptyMode ?? "solid";
353
- const emptyStep = config.emptyStep ?? "850";
354
433
  const gradientStyle = config.gradientStyle ?? "linear";
355
434
  const gradientAngle = config.gradientAngle ?? 180;
356
435
  const gradientReverse = config.gradientReverse ?? false;
@@ -360,14 +439,17 @@ function derivePaletteVars(spec, config) {
360
439
  { position: 100, paletteLabel: "950" }
361
440
  ];
362
441
  if (emptyMode === "solid") {
363
- const stepHex = out[`--color-${spec.cssNamespace}-${emptyStep}`];
364
- if (stepHex) out["--page-bg"] = stepHex;
365
- out["--page-bg-attachment"] = "scroll";
442
+ out["--page-bg"] = { kind: "color", ...baseColor };
443
+ out["--page-bg-attachment"] = { kind: "raw", css: "scroll" };
366
444
  } else {
367
445
  const sortedStops = [...gradientStops].sort(
368
446
  (a, b) => gradientReverse ? b.position - a.position : a.position - b.position
369
447
  );
370
- const stopsCss = sortedStops.map((s) => `${out[`--color-${spec.cssNamespace}-${s.paletteLabel}`] ?? "#000000"} ${s.position}%`).join(", ");
448
+ const stopsCss = sortedStops.map((s) => {
449
+ const stopValue = out[`--color-${spec.cssNamespace}-${s.paletteLabel}`];
450
+ const hex = stopValue ? serializeDerivedValue(stopValue) : "#000000";
451
+ return `${hex} ${s.position}%`;
452
+ }).join(", ");
371
453
  let gradient;
372
454
  switch (gradientStyle) {
373
455
  case "radial":
@@ -379,16 +461,30 @@ function derivePaletteVars(spec, config) {
379
461
  default:
380
462
  gradient = `linear-gradient(${gradientAngle}deg, ${stopsCss})`;
381
463
  }
382
- out["--page-bg"] = gradient;
383
- out["--page-bg-attachment"] = gradientSize === "window" ? "fixed" : "scroll";
464
+ out["--page-bg"] = { kind: "raw", css: gradient };
465
+ out["--page-bg-attachment"] = { kind: "raw", css: gradientSize === "window" ? "fixed" : "scroll" };
384
466
  }
385
467
  }
386
468
  return out;
387
469
  }
388
- function palettesToVars(palettes) {
470
+ function derivePaletteVars(spec, config) {
471
+ const out = {};
472
+ for (const [k, v] of Object.entries(derivePaletteValues(spec, config))) {
473
+ out[k] = serializeDerivedValue(v);
474
+ }
475
+ return out;
476
+ }
477
+ function palettesToValues(palettes) {
389
478
  const out = {};
390
479
  for (const spec of PALETTE_SPECS) {
391
- Object.assign(out, derivePaletteVars(spec, palettes[spec.label]));
480
+ Object.assign(out, derivePaletteValues(spec, palettes[spec.label]));
481
+ }
482
+ return out;
483
+ }
484
+ function palettesToVars(palettes) {
485
+ const out = {};
486
+ for (const [k, v] of Object.entries(palettesToValues(palettes))) {
487
+ out[k] = serializeDerivedValue(v);
392
488
  }
393
489
  return out;
394
490
  }
@@ -403,8 +499,8 @@ function reconcilePalettesFromCssVars(palettes, cssVars) {
403
499
  if (current._imported === true) {
404
500
  const anchorHex = cssVars[`--color-${spec.cssNamespace}-500`];
405
501
  if (anchorHex && HEX_RE.test(anchorHex.trim())) {
406
- const hex = anchorHex.trim();
407
- next[spec.label] = { ...current, baseColor: hex, _imported: false };
502
+ next[spec.label] = { ...current, baseColor: hexToOklch(anchorHex.trim()), _imported: false };
503
+ syncBaseAnchor(next[spec.label]);
408
504
  snapped.add(spec.label);
409
505
  } else {
410
506
  next[spec.label] = { ...current, _imported: false };
@@ -417,6 +513,26 @@ function reconcilePalettesFromCssVars(palettes, cssVars) {
417
513
  return { palettes: next, consumed, snapped };
418
514
  }
419
515
 
516
+ // src/editor/core/themes/migrations/2026-07-21-palette-oklch-basis.ts
517
+ var round = (v, dp) => {
518
+ const f = 10 ** dp;
519
+ return Math.round(v * f) / f;
520
+ };
521
+ function toStoredOklch(value) {
522
+ if (typeof value !== "string") return value;
523
+ const { l, c, h } = hexToOklch(value);
524
+ return { l: round(l, 4), c: round(c, 4), h: round(h, 2) };
525
+ }
526
+ function migratePaletteColorsToOklch(editorConfigs) {
527
+ const out = {};
528
+ for (const [label, cfg] of Object.entries(editorConfigs)) {
529
+ const overrides = {};
530
+ for (const [k, v] of Object.entries(cfg.overrides ?? {})) overrides[k] = toStoredOklch(v);
531
+ out[label] = { ...cfg, baseColor: toStoredOklch(cfg.baseColor), overrides };
532
+ }
533
+ return out;
534
+ }
535
+
420
536
  // vite-plugin/files/versionedFileResourceServer.ts
421
537
  var import_fs = __toESM(require("fs"), 1);
422
538
  var import_path = __toESM(require("path"), 1);
@@ -622,6 +738,19 @@ function ensureScale(css, opts) {
622
738
  lines.splice(insertAfter + 1, 0, ...block);
623
739
  return lines.join("\n");
624
740
  }
741
+ function renameToken(css, oldName, newName) {
742
+ const defined = collectDefinedTokens(css);
743
+ if (!defined.has(oldName) || defined.has(newName)) return css;
744
+ const re = new RegExp(escapeRe(oldName) + "(?![a-z0-9-])", "gi");
745
+ return css.replace(re, newName);
746
+ }
747
+ function removeToken(css, name) {
748
+ const lines = css.split("\n");
749
+ const declRe = new RegExp("(^|[\\s;{])" + escapeRe(name) + "\\s*:");
750
+ const kept = lines.filter((line) => !declRe.test(line));
751
+ if (kept.length === lines.length) return css;
752
+ return kept.join("\n");
753
+ }
625
754
  function removeTokensMatching(css, predicate) {
626
755
  const lines = css.split("\n");
627
756
  const declRe = /^\s*(--[a-z0-9-]+)\s*:/;
@@ -674,25 +803,17 @@ function findTopLevelRoot(lines) {
674
803
  }
675
804
  return null;
676
805
  }
806
+ function escapeRe(s) {
807
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
808
+ }
677
809
 
678
810
  // vite-plugin/tokensCssMigrations/migrations/2026-05-29-typography-scale-additions.ts
679
811
  var tokensCssMigration_2026_05_29_typographyScaleAdditions = {
680
812
  id: "2026-05-29-typography-scale-additions",
681
813
  kind: "additive",
682
- description: "Add --line-height-{xs..xl}, --letter-spacing-* and --ease-out-quart scales",
814
+ description: "Add --letter-spacing-* and --ease-out-quart scales",
683
815
  apply(css) {
684
816
  let out = css;
685
- out = ensureScale(out, {
686
- sectionComment: "Line height (t-shirt scale)",
687
- anchorPrefixes: ["--line-height-", "--font-size-", "--font-weight-", "--font-"],
688
- entries: [
689
- { name: "--line-height-xs", value: "1" },
690
- { name: "--line-height-sm", value: "1.25" },
691
- { name: "--line-height-md", value: "1.5" },
692
- { name: "--line-height-lg", value: "1.75" },
693
- { name: "--line-height-xl", value: "2" }
694
- ]
695
- });
696
817
  out = ensureScale(out, {
697
818
  sectionComment: "Letter spacing",
698
819
  anchorPrefixes: ["--letter-spacing-", "--line-height-", "--font-size-", "--font-"],
@@ -835,13 +956,113 @@ var tokensCssMigration_2026_06_04_easingColorAndTypescaleAdditions = {
835
956
  }
836
957
  };
837
958
 
959
+ // vite-plugin/tokensCssMigrations/migrations/2026-07-20-line-height-rename.ts
960
+ var RENAMES = [
961
+ ["--line-height-xs", "--line-height-none"],
962
+ ["--line-height-sm", "--line-height-tighter"],
963
+ ["--line-height-md", "--line-height-normal"],
964
+ ["--line-height-lg", "--line-height-relaxed"]
965
+ ];
966
+ var SCALE = [
967
+ { name: "--line-height-none", value: "1" },
968
+ { name: "--line-height-tightest", value: "1.1" },
969
+ { name: "--line-height-tighter", value: "1.25" },
970
+ { name: "--line-height-tight", value: "1.35" },
971
+ { name: "--line-height-normal", value: "1.5" },
972
+ { name: "--line-height-relaxed", value: "1.75" }
973
+ ];
974
+ var tokensCssMigration_2026_07_20_lineHeightRename = {
975
+ id: "2026-07-20-line-height-rename",
976
+ kind: "breaking",
977
+ description: "Reshape --line-height-{xs..xl} to leading vocabulary (none/tightest/tighter/tight/normal/relaxed); retire the 2.0 slot",
978
+ apply(css) {
979
+ let out = css;
980
+ for (const [oldName, newName] of RENAMES) out = renameToken(out, oldName, newName);
981
+ out = removeToken(out, "--line-height-xl");
982
+ out = ensureScale(out, {
983
+ anchorPrefixes: ["--line-height-", "--font-size-", "--font-weight-", "--font-"],
984
+ entries: SCALE
985
+ });
986
+ return out;
987
+ }
988
+ };
989
+
990
+ // vite-plugin/tokensCssMigrations/migrations/2026-07-20-semantic-text-styles.ts
991
+ var BUNDLE = [
992
+ { name: "--heading-xl-font-family", value: "var(--font-display)" },
993
+ { name: "--heading-xl-font-size", value: "var(--font-size-4xl)" },
994
+ { name: "--heading-xl-font-weight", value: "var(--font-weight-semibold)" },
995
+ { name: "--heading-xl-line-height", value: "var(--line-height-tightest)" },
996
+ { name: "--heading-xl-letter-spacing", value: "var(--letter-spacing-normal)" },
997
+ { name: "--heading-lg-font-family", value: "var(--font-display)" },
998
+ { name: "--heading-lg-font-size", value: "var(--font-size-2xl)" },
999
+ { name: "--heading-lg-font-weight", value: "var(--font-weight-semibold)" },
1000
+ { name: "--heading-lg-line-height", value: "var(--line-height-tightest)" },
1001
+ { name: "--heading-lg-letter-spacing", value: "var(--letter-spacing-normal)" },
1002
+ { name: "--heading-md-font-family", value: "var(--font-display)" },
1003
+ { name: "--heading-md-font-size", value: "var(--font-size-xl)" },
1004
+ { name: "--heading-md-font-weight", value: "var(--font-weight-semibold)" },
1005
+ { name: "--heading-md-line-height", value: "var(--line-height-tighter)" },
1006
+ { name: "--heading-md-letter-spacing", value: "var(--letter-spacing-normal)" },
1007
+ { name: "--heading-sm-font-family", value: "var(--font-sans)" },
1008
+ { name: "--heading-sm-font-size", value: "var(--font-size-lg)" },
1009
+ { name: "--heading-sm-font-weight", value: "var(--font-weight-semibold)" },
1010
+ { name: "--heading-sm-line-height", value: "var(--line-height-tighter)" },
1011
+ { name: "--heading-sm-letter-spacing", value: "var(--letter-spacing-normal)" },
1012
+ { name: "--body-md-font-family", value: "var(--font-sans)" },
1013
+ { name: "--body-md-font-size", value: "var(--font-size-md)" },
1014
+ { name: "--body-md-font-weight", value: "var(--font-weight-normal)" },
1015
+ { name: "--body-md-line-height", value: "var(--line-height-normal)" },
1016
+ { name: "--body-md-letter-spacing", value: "var(--letter-spacing-normal)" },
1017
+ { name: "--body-sm-font-family", value: "var(--font-sans)" },
1018
+ { name: "--body-sm-font-size", value: "var(--font-size-sm)" },
1019
+ { name: "--body-sm-font-weight", value: "var(--font-weight-normal)" },
1020
+ { name: "--body-sm-line-height", value: "var(--line-height-tight)" },
1021
+ { name: "--body-sm-letter-spacing", value: "var(--letter-spacing-normal)" },
1022
+ { name: "--code-font-family", value: "var(--font-mono)" },
1023
+ { name: "--code-font-size", value: "var(--font-size-sm)" },
1024
+ { name: "--code-font-weight", value: "var(--font-weight-normal)" },
1025
+ { name: "--code-line-height", value: "var(--line-height-normal)" },
1026
+ { name: "--code-letter-spacing", value: "var(--letter-spacing-normal)" },
1027
+ { name: "--eyebrow-font-family", value: "var(--font-sans)" },
1028
+ { name: "--eyebrow-font-size", value: "var(--font-size-sm)" },
1029
+ { name: "--eyebrow-font-weight", value: "var(--font-weight-normal)" },
1030
+ { name: "--eyebrow-line-height", value: "var(--line-height-normal)" },
1031
+ { name: "--eyebrow-letter-spacing", value: "var(--letter-spacing-normal)" },
1032
+ { name: "--eyebrow-text-transform", value: "none" }
1033
+ ];
1034
+ var tokensCssMigration_2026_07_20_semanticTextStyles = {
1035
+ id: "2026-07-20-semantic-text-styles",
1036
+ kind: "additive",
1037
+ description: "Add the semantic text-style bundles (--heading-*/--body-*/--code-*/--eyebrow-*)",
1038
+ apply(css) {
1039
+ return ensureScale(css, {
1040
+ sectionComment: "Text styles \u2014 semantic typography roles",
1041
+ anchorPrefixes: [
1042
+ "--eyebrow-",
1043
+ "--code-",
1044
+ "--body-",
1045
+ "--heading-",
1046
+ "--letter-spacing-",
1047
+ "--line-height-",
1048
+ "--font-weight-",
1049
+ "--font-size-",
1050
+ "--font-"
1051
+ ],
1052
+ entries: BUNDLE
1053
+ });
1054
+ }
1055
+ };
1056
+
838
1057
  // vite-plugin/tokensCssMigrations/index.ts
839
1058
  var TOKENS_CSS_MIGRATIONS = [
840
1059
  tokensCssMigration_2026_05_29_typographyScaleAdditions,
841
1060
  tokensCssMigration_2026_05_29_sectiondividerLegacyAxisCleanup,
842
1061
  tokensCssMigration_2026_06_03_transformScaleAdditions,
843
1062
  tokensCssMigration_2026_06_04_removeDeadSizeIconScale,
844
- tokensCssMigration_2026_06_04_easingColorAndTypescaleAdditions
1063
+ tokensCssMigration_2026_06_04_easingColorAndTypescaleAdditions,
1064
+ tokensCssMigration_2026_07_20_lineHeightRename,
1065
+ tokensCssMigration_2026_07_20_semanticTextStyles
845
1066
  ];
846
1067
  function runTokensCssMigrations(css) {
847
1068
  return foldMigrations(css, () => true);
@@ -1032,7 +1253,7 @@ function themeFileApi(opts) {
1032
1253
  let themeVarCount = 0;
1033
1254
  if (themeData) {
1034
1255
  const cssVars = { ...themeData.cssVariables || {} };
1035
- Object.assign(cssVars, palettesToVars(themeData.editorConfigs ?? {}));
1256
+ Object.assign(cssVars, palettesToVars(migratePaletteColorsToOklch(themeData.editorConfigs ?? {})));
1036
1257
  const resolvedFontVars = resolveFontStacks(themeData);
1037
1258
  for (const [name, value] of Object.entries(resolvedFontVars)) {
1038
1259
  cssVars[name] = value;