@motion-proto/live-tokens 0.56.1 → 0.58.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 (95) hide show
  1. package/.claude/skills/live-tokens-create-component/SKILL.md +13 -2
  2. package/.claude/skills/live-tokens-create-component/references/sketch-mode.md +202 -0
  3. package/.claude/skills/live-tokens-pair-fonts/SKILL.md +2 -2
  4. package/.claude/skills/live-tokens-pick-component/SKILL.md +1 -1
  5. package/CHANGELOG.md +192 -0
  6. package/README.md +1 -1
  7. package/bin/cli.mjs +2 -1
  8. package/dist-plugin/adjust/index.cjs +1 -0
  9. package/dist-plugin/adjust/index.d.cts +2 -2
  10. package/dist-plugin/adjust/index.d.ts +2 -2
  11. package/dist-plugin/adjust/index.js +1 -1
  12. package/dist-plugin/{chunk-TKZBVIW5.js → chunk-E5QYON4L.js} +59 -2
  13. package/dist-plugin/{chunk-PB2JTK2H.js → chunk-LW4SR7AZ.js} +39 -6
  14. package/dist-plugin/{chunk-YR6GPXW2.js → chunk-XWXIMTWZ.js} +0 -5
  15. package/dist-plugin/{chunk-D3ZVKOR4.js → chunk-Y5CNFSSV.js} +1 -0
  16. package/dist-plugin/{dataPaths-DBN0RPuT.d.cts → dataPaths-CRfD1LdA.d.cts} +3 -0
  17. package/dist-plugin/{dataPaths-DBN0RPuT.d.ts → dataPaths-CRfD1LdA.d.ts} +3 -0
  18. package/dist-plugin/fontPairing/index.cjs +4 -2
  19. package/dist-plugin/fontPairing/index.d.cts +3 -3
  20. package/dist-plugin/fontPairing/index.d.ts +3 -3
  21. package/dist-plugin/fontPairing/index.js +4 -3
  22. package/dist-plugin/generateColorsAndType/index.cjs +22 -9
  23. package/dist-plugin/generateColorsAndType/index.d.cts +2 -2
  24. package/dist-plugin/generateColorsAndType/index.d.ts +2 -2
  25. package/dist-plugin/generateColorsAndType/index.js +8 -5
  26. package/dist-plugin/index.cjs +158 -9
  27. package/dist-plugin/index.d.cts +1 -1
  28. package/dist-plugin/index.d.ts +1 -1
  29. package/dist-plugin/index.js +71 -4
  30. package/dist-plugin/migrateData/index.cjs +1 -0
  31. package/dist-plugin/migrateData/index.d.cts +1 -1
  32. package/dist-plugin/migrateData/index.d.ts +1 -1
  33. package/dist-plugin/migrateData/index.js +1 -1
  34. package/dist-plugin/{themeTypes-BAqtv4XO.d.cts → themeTypes-DSV3Zisf.d.cts} +12 -1
  35. package/dist-plugin/{themeTypes-BAqtv4XO.d.ts → themeTypes-DSV3Zisf.d.ts} +12 -1
  36. package/dist-plugin/tokensCssMigrations/index.cjs +58 -1
  37. package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
  38. package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
  39. package/dist-plugin/tokensCssMigrations/index.js +3 -3
  40. package/package.json +3 -1
  41. package/src/editor/core/fonts/applyFontPairing.ts +3 -2
  42. package/src/editor/core/fonts/fontLoader.ts +1 -0
  43. package/src/editor/core/fonts/fontMigration.ts +31 -1
  44. package/src/editor/core/palettes/paletteDerivation.ts +9 -2
  45. package/src/editor/core/sketch/maskField.ts +385 -0
  46. package/src/editor/core/sketch/sketchLayer.ts +1106 -0
  47. package/src/editor/core/sketch/sketchPresetService.ts +65 -0
  48. package/src/editor/core/sketch/sketchPresets.ts +345 -0
  49. package/src/editor/core/sketch/sketchStore.ts +190 -0
  50. package/src/editor/core/store/editorPersistence.ts +28 -12
  51. package/src/editor/core/store/editorTypes.ts +4 -1
  52. package/src/editor/core/store/editorViewStore.ts +2 -2
  53. package/src/editor/core/store/gradientSource.ts +15 -1
  54. package/src/editor/core/themes/parsers/gradient.ts +62 -4
  55. package/src/editor/core/themes/slices/gradients.ts +75 -14
  56. package/src/editor/core/themes/themeTypes.ts +6 -1
  57. package/src/editor/docs/Docs.svelte +4 -3
  58. package/src/editor/docs/chapters.ts +1 -0
  59. package/src/editor/docs/content/01-overview.md +2 -1
  60. package/src/editor/docs/content/editing-tokens.md +5 -1
  61. package/src/editor/docs/content/sketch-mode.md +183 -0
  62. package/src/editor/docs/content.generated.ts +3 -2
  63. package/src/editor/overlay/LiveEditorOverlay.svelte +15 -0
  64. package/src/editor/pages/EditorShell.svelte +43 -0
  65. package/src/editor/ui/EditorViewSwitcher.svelte +30 -17
  66. package/src/editor/ui/FontStackEditor.svelte +3 -0
  67. package/src/editor/ui/GradientEditor.svelte +38 -1
  68. package/src/editor/ui/UIReveal.svelte +7 -1
  69. package/src/editor/ui/UISegmentedControl.svelte +4 -8
  70. package/src/editor/ui/sections/GradientsSection.svelte +47 -1
  71. package/src/editor/ui/sketch/SketchDial.svelte +124 -0
  72. package/src/editor/ui/sketch/SketchPreview.svelte +119 -0
  73. package/src/editor/ui/sketch/SketchRange.svelte +185 -0
  74. package/src/editor/ui/sketch/SketchTab.svelte +1264 -0
  75. package/src/live-tokens/data/colors-and-type/autumn.json +17 -0
  76. package/src/live-tokens/data/colors-and-type/default.json +17 -0
  77. package/src/live-tokens/data/colors-and-type/halloween.json +17 -0
  78. package/src/live-tokens/data/colors-and-type/midnight-study.json +17 -0
  79. package/src/live-tokens/data/colors-and-type/ocean.json +17 -0
  80. package/src/live-tokens/data/colors-and-type/royal-velvet.json +17 -0
  81. package/src/live-tokens/data/colors-and-type/sketchy.json +2520 -0
  82. package/src/live-tokens/data/colors-and-type/spring-meadow.json +17 -0
  83. package/src/live-tokens/data/colors-and-type/sunset.json +17 -0
  84. package/src/live-tokens/data/themes/autumn.json +17 -0
  85. package/src/live-tokens/data/themes/halloween.json +17 -0
  86. package/src/live-tokens/data/themes/midnight-study.json +17 -0
  87. package/src/live-tokens/data/themes/ocean.json +17 -0
  88. package/src/live-tokens/data/themes/royal-velvet.json +17 -0
  89. package/src/live-tokens/data/themes/sketchy.json +3984 -0
  90. package/src/live-tokens/data/themes/spring-meadow.json +17 -0
  91. package/src/live-tokens/data/themes/sunset.json +17 -0
  92. package/src/live-tokens/data/tokens.generated.css +1 -0
  93. package/src/system/components/Card.svelte +12 -1
  94. package/src/system/components/SectionDivider.svelte +4 -0
  95. package/src/system/styles/tokens.css +15 -2
@@ -5,7 +5,7 @@ import {
5
5
  parseGradientValue,
6
6
  reconcilePalettesFromCssVars,
7
7
  sanitizeFileName
8
- } from "./chunk-PB2JTK2H.js";
8
+ } from "./chunk-LW4SR7AZ.js";
9
9
  import {
10
10
  THEME_SCHEMA_VERSION,
11
11
  detectLegacyLayout,
@@ -23,13 +23,13 @@ import {
23
23
  runAdditiveTokensCssMigrations,
24
24
  runTokensCssMigrations,
25
25
  validateTokensCss
26
- } from "./chunk-TKZBVIW5.js";
26
+ } from "./chunk-E5QYON4L.js";
27
27
  import {
28
28
  hexToOklch
29
- } from "./chunk-YR6GPXW2.js";
29
+ } from "./chunk-XWXIMTWZ.js";
30
30
  import {
31
31
  resolveDataDirs
32
- } from "./chunk-D3ZVKOR4.js";
32
+ } from "./chunk-Y5CNFSSV.js";
33
33
 
34
34
  // vite-plugin/themeFileApi.ts
35
35
  import fs from "fs";
@@ -137,6 +137,7 @@ function themeFileApi(opts) {
137
137
  const COLORS_AND_TYPE_DIR = dataDirs.colorsAndTypeDir;
138
138
  const COMPONENT_CONFIGS_DIR = dataDirs.componentConfigsDir;
139
139
  const THEMES_DIR = dataDirs.themesDir;
140
+ const SKETCH_PRESETS_DIR = dataDirs.sketchPresetsDir;
140
141
  const CSS_PATH = path.resolve(opts.tokensCssPath);
141
142
  const GENERATED_CSS_PATH = opts.tokensGeneratedCssPath ? path.resolve(opts.tokensGeneratedCssPath) : path.join(dataDirs.dataDir, "tokens.generated.css");
142
143
  const FONTS_CSS_PATH = opts.fontsCssPath ? path.resolve(opts.fontsCssPath) : path.join(path.dirname(CSS_PATH), "fonts.css");
@@ -194,6 +195,7 @@ function themeFileApi(opts) {
194
195
  dir: THEMES_DIR,
195
196
  packageDir: packageThemesDir
196
197
  });
198
+ const sketchPresetsResource = versionedFileResourceServer({ dir: SKETCH_PRESETS_DIR });
197
199
  function ensureColorsAndTypeDir() {
198
200
  colorsAndTypeResource.ensureDir();
199
201
  }
@@ -759,6 +761,8 @@ ${lines.join("\n")}
759
761
  const THEME_EXPORT_REGEX = new RegExp(`^${escapedBase}/themes/([a-z0-9\\-_]+)/export$`);
760
762
  const THEME_IMPORT_ROUTE = `${API_BASE}/themes/import`;
761
763
  const THEME_BY_NAME_REGEX = new RegExp(`^${escapedBase}/themes/([a-z0-9\\-_]+)$`);
764
+ const SKETCH_PRESETS_ROUTE = `${API_BASE}/sketch-presets`;
765
+ const SKETCH_PRESET_BY_NAME_REGEX = new RegExp(`^${escapedBase}/sketch-presets/([a-z0-9\\-_]+)$`);
762
766
  const PRODUCTION_ROUTE = `${API_BASE}/production`;
763
767
  async function handleListColorsAndType(_ctx) {
764
768
  const files = colorsAndTypeResource.listNames().map((fileName) => {
@@ -1290,6 +1294,61 @@ ${lines.join("\n")}
1290
1294
  }
1291
1295
  });
1292
1296
  }
1297
+ async function handleListSketchPresets({ res }) {
1298
+ const files = sketchPresetsResource.listNames().map((fileName) => {
1299
+ let data = null;
1300
+ try {
1301
+ data = sketchPresetsResource.readJson(fileName);
1302
+ } catch {
1303
+ }
1304
+ return {
1305
+ name: data?.name || fileName,
1306
+ fileName,
1307
+ updatedAt: data?.updatedAt || ""
1308
+ };
1309
+ });
1310
+ jsonResponse(res, 200, { files });
1311
+ }
1312
+ async function handleSketchPresetByName({ params, req, res }) {
1313
+ const [fileName] = params;
1314
+ if (rejectReservedName(res, fileName)) return;
1315
+ const filePath = sketchPresetsResource.filePath(fileName);
1316
+ if (req.method === "GET") {
1317
+ const raw = sketchPresetsResource.readJson(fileName);
1318
+ if (!raw) {
1319
+ jsonResponse(res, 404, { error: "Not found" });
1320
+ return;
1321
+ }
1322
+ jsonResponse(res, 200, { ...raw, _fileName: fileName });
1323
+ return;
1324
+ }
1325
+ if (req.method === "PUT") {
1326
+ const body = JSON.parse(await readBody(req));
1327
+ if (!body || typeof body !== "object" || !body.settings || typeof body.settings !== "object") {
1328
+ jsonResponse(res, 400, { error: "A sketch preset needs a settings object" });
1329
+ return;
1330
+ }
1331
+ body.name = String(body.name || fileName);
1332
+ body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1333
+ if (fs.existsSync(filePath)) {
1334
+ try {
1335
+ const existing = JSON.parse(fs.readFileSync(filePath, "utf-8"));
1336
+ if (existing.createdAt) body.createdAt = existing.createdAt;
1337
+ } catch {
1338
+ }
1339
+ }
1340
+ if (!body.createdAt) body.createdAt = body.updatedAt;
1341
+ sketchPresetsResource.ensureDir();
1342
+ fs.writeFileSync(filePath, JSON.stringify(body, null, 2));
1343
+ jsonResponse(res, 200, { ok: true, fileName });
1344
+ return;
1345
+ }
1346
+ if (req.method === "DELETE") {
1347
+ if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
1348
+ jsonResponse(res, 200, { ok: true });
1349
+ return;
1350
+ }
1351
+ }
1293
1352
  function methodNotAllowed({ res }) {
1294
1353
  jsonResponse(res, 405, { error: "Method not allowed" });
1295
1354
  }
@@ -1366,6 +1425,14 @@ ${lines.join("\n")}
1366
1425
  { method: "PUT", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
1367
1426
  { method: "DELETE", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
1368
1427
  // Production — the whole-look adopt door
1428
+ { method: "GET", pattern: SKETCH_PRESETS_ROUTE, handler: handleListSketchPresets },
1429
+ { method: "PUT", pattern: SKETCH_PRESETS_ROUTE, handler: methodNotAllowed },
1430
+ { method: "POST", pattern: SKETCH_PRESETS_ROUTE, handler: methodNotAllowed },
1431
+ { method: "DELETE", pattern: SKETCH_PRESETS_ROUTE, handler: methodNotAllowed },
1432
+ { method: "GET", pattern: SKETCH_PRESET_BY_NAME_REGEX, handler: handleSketchPresetByName },
1433
+ { method: "PUT", pattern: SKETCH_PRESET_BY_NAME_REGEX, handler: handleSketchPresetByName },
1434
+ { method: "DELETE", pattern: SKETCH_PRESET_BY_NAME_REGEX, handler: handleSketchPresetByName },
1435
+ { method: "POST", pattern: SKETCH_PRESET_BY_NAME_REGEX, handler: methodNotAllowed },
1369
1436
  { method: "PUT", pattern: PRODUCTION_ROUTE, handler: handleAdoptLook },
1370
1437
  { method: "GET", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
1371
1438
  { method: "POST", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
@@ -1659,6 +1659,7 @@ function resolveDataDirs(opts = {}) {
1659
1659
  colorsAndTypeDir: opts.colorsAndTypeDir ? import_path4.default.resolve(opts.colorsAndTypeDir) : fileConfig.colorsAndTypeDir ? import_path4.default.resolve(fileConfig.colorsAndTypeDir) : sub("colors-and-type"),
1660
1660
  componentConfigsDir: opts.componentConfigsDir ? import_path4.default.resolve(opts.componentConfigsDir) : fileConfig.componentConfigsDir ? import_path4.default.resolve(fileConfig.componentConfigsDir) : sub("component-configs"),
1661
1661
  themesDir: opts.themesDir ? import_path4.default.resolve(opts.themesDir) : fileConfig.themesDir ? import_path4.default.resolve(fileConfig.themesDir) : sub("themes"),
1662
+ sketchPresetsDir: sub("sketch-presets"),
1662
1663
  legacyManifestsDir: fileConfig.manifestsDir ? import_path4.default.resolve(fileConfig.manifestsDir) : void 0
1663
1664
  };
1664
1665
  }
@@ -1,4 +1,4 @@
1
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.cjs';
1
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.cjs';
2
2
 
3
3
  interface LegacyRename {
4
4
  from: string;
@@ -1,4 +1,4 @@
1
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.js';
1
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.js';
2
2
 
3
3
  interface LegacyRename {
4
4
  from: string;
@@ -14,7 +14,7 @@ import {
14
14
  import {
15
15
  readLiveTokensConfig,
16
16
  resolveDataDirs
17
- } from "../chunk-D3ZVKOR4.js";
17
+ } from "../chunk-Y5CNFSSV.js";
18
18
 
19
19
  // vite-plugin/migrateData/migrateData.ts
20
20
  import fs from "fs";
@@ -7,6 +7,13 @@ interface CurveAnchor {
7
7
  outDy: number;
8
8
  }
9
9
 
10
+ /** The `to <side-or-corner>` keywords CSS accepts in place of an angle. A
11
+ * keyword angles the gradient line off the box's own diagonal, so it tracks
12
+ * the element's aspect ratio where a fixed angle cannot: `to bottom right`
13
+ * reads as ~95deg across a wide heading and ~111deg once that heading wraps.
14
+ * That is the whole reason to carry them — a degree cannot express it. */
15
+ declare const LINEAR_DIRECTIONS: readonly ["to top", "to top right", "to right", "to bottom right", "to bottom", "to bottom left", "to left", "to top left"];
16
+ type LinearDirection = (typeof LINEAR_DIRECTIONS)[number];
10
17
  interface GradientStop$1 {
11
18
  /** 0–100 percentage along the gradient axis. */
12
19
  position: number;
@@ -25,6 +32,10 @@ interface GradientStop$1 {
25
32
  interface GradientValue {
26
33
  type: 'linear' | 'radial' | 'solid' | 'none';
27
34
  angle: number;
35
+ /** Emitted in place of `angle` on a `linear` gradient. `angle` is retained
36
+ * underneath so clearing the direction restores the user's degrees, the
37
+ * same way a radial keeps its angle. */
38
+ direction?: LinearDirection;
28
39
  radius?: number;
29
40
  centerX?: number;
30
41
  aspectX?: number;
@@ -136,7 +147,7 @@ interface FontSource {
136
147
  }
137
148
  type SystemCascadePreset = 'system-ui-sans' | 'system-ui-serif' | 'system-ui-mono';
138
149
  type GenericFamily = 'sans-serif' | 'serif' | 'monospace' | 'cursive' | 'fantasy';
139
- type FontStackVariable = '--font-display' | '--font-sans' | '--font-serif' | '--font-mono';
150
+ type FontStackVariable = '--font-display' | '--font-sans' | '--font-serif' | '--font-mono' | '--font-editorial';
140
151
  type FontStackSlot = {
141
152
  kind: 'project';
142
153
  familyId: string;
@@ -7,6 +7,13 @@ interface CurveAnchor {
7
7
  outDy: number;
8
8
  }
9
9
 
10
+ /** The `to <side-or-corner>` keywords CSS accepts in place of an angle. A
11
+ * keyword angles the gradient line off the box's own diagonal, so it tracks
12
+ * the element's aspect ratio where a fixed angle cannot: `to bottom right`
13
+ * reads as ~95deg across a wide heading and ~111deg once that heading wraps.
14
+ * That is the whole reason to carry them — a degree cannot express it. */
15
+ declare const LINEAR_DIRECTIONS: readonly ["to top", "to top right", "to right", "to bottom right", "to bottom", "to bottom left", "to left", "to top left"];
16
+ type LinearDirection = (typeof LINEAR_DIRECTIONS)[number];
10
17
  interface GradientStop$1 {
11
18
  /** 0–100 percentage along the gradient axis. */
12
19
  position: number;
@@ -25,6 +32,10 @@ interface GradientStop$1 {
25
32
  interface GradientValue {
26
33
  type: 'linear' | 'radial' | 'solid' | 'none';
27
34
  angle: number;
35
+ /** Emitted in place of `angle` on a `linear` gradient. `angle` is retained
36
+ * underneath so clearing the direction restores the user's degrees, the
37
+ * same way a radial keeps its angle. */
38
+ direction?: LinearDirection;
28
39
  radius?: number;
29
40
  centerX?: number;
30
41
  aspectX?: number;
@@ -136,7 +147,7 @@ interface FontSource {
136
147
  }
137
148
  type SystemCascadePreset = 'system-ui-sans' | 'system-ui-serif' | 'system-ui-mono';
138
149
  type GenericFamily = 'sans-serif' | 'serif' | 'monospace' | 'cursive' | 'fantasy';
139
- type FontStackVariable = '--font-display' | '--font-sans' | '--font-serif' | '--font-mono';
150
+ type FontStackVariable = '--font-display' | '--font-sans' | '--font-serif' | '--font-mono' | '--font-editorial';
140
151
  type FontStackSlot = {
141
152
  kind: 'project';
142
153
  familyId: string;
@@ -462,6 +462,61 @@ var tokensCssMigration_2026_08_19_oklchColorValues = {
462
462
  }
463
463
  };
464
464
 
465
+ // vite-plugin/tokensCssMigrations/migrations/2026-08-25-editorial-type-role.ts
466
+ var STACK = [{ name: "--font-editorial", value: "var(--font-sans)" }];
467
+ var BUNDLE2 = [
468
+ { name: "--editorial-font-family", value: "var(--font-editorial)" },
469
+ { name: "--editorial-font-size", value: "var(--font-size-md)" },
470
+ { name: "--editorial-font-weight", value: "var(--font-weight-normal)" },
471
+ { name: "--editorial-line-height", value: "var(--line-height-normal)" },
472
+ { name: "--editorial-letter-spacing", value: "var(--letter-spacing-normal)" }
473
+ ];
474
+ var tokensCssMigration_2026_08_25_editorialTypeRole = {
475
+ id: "2026-08-25-editorial-type-role",
476
+ kind: "additive",
477
+ description: "Add the editorial type role (--font-editorial and the --editorial-* bundle)",
478
+ apply(css) {
479
+ const withStack = ensureScale(css, {
480
+ anchorPrefixes: ["--font-mono", "--font-serif", "--font-sans", "--font-display"],
481
+ entries: STACK
482
+ });
483
+ return ensureScale(withStack, {
484
+ sectionComment: "Editorial \u2014 the long-reading role, body face until repointed",
485
+ anchorPrefixes: [
486
+ "--eyebrow-",
487
+ "--code-",
488
+ "--body-",
489
+ "--heading-",
490
+ "--letter-spacing-",
491
+ "--line-height-",
492
+ "--font-weight-",
493
+ "--font-size-",
494
+ "--font-"
495
+ ],
496
+ entries: BUNDLE2
497
+ });
498
+ }
499
+ };
500
+
501
+ // vite-plugin/tokensCssMigrations/migrations/2026-08-26-gradient-stops.ts
502
+ var STOPS_RE = /^(?:linear|radial)-gradient\(\s*[^,]+,\s*(.+)\)$/i;
503
+ var tokensCssMigration_2026_08_26_gradientStops = {
504
+ id: "2026-08-26-gradient-stops",
505
+ kind: "additive",
506
+ description: "Add --gradient-N-stops companions to the gradient slots",
507
+ apply(css) {
508
+ const values = collectTokenValues(css);
509
+ const entries = [];
510
+ for (const [name, value] of values) {
511
+ if (!/^--gradient-\d+$/.test(name)) continue;
512
+ const stops = value.match(STOPS_RE);
513
+ if (stops) entries.push({ name: `${name}-stops`, value: stops[1].trim() });
514
+ }
515
+ if (entries.length === 0) return css;
516
+ return ensureScale(css, { anchorPrefixes: ["--gradient-"], entries });
517
+ }
518
+ };
519
+
465
520
  // vite-plugin/files/dataPaths.ts
466
521
  var import_fs = __toESM(require("fs"), 1);
467
522
  var import_path = __toESM(require("path"), 1);
@@ -505,7 +560,9 @@ var TOKENS_CSS_MIGRATIONS = [
505
560
  tokensCssMigration_2026_06_04_easingColorAndTypescaleAdditions,
506
561
  tokensCssMigration_2026_07_20_lineHeightRename,
507
562
  tokensCssMigration_2026_07_20_semanticTextStyles,
508
- tokensCssMigration_2026_08_19_oklchColorValues
563
+ tokensCssMigration_2026_08_19_oklchColorValues,
564
+ tokensCssMigration_2026_08_25_editorialTypeRole,
565
+ tokensCssMigration_2026_08_26_gradientStops
509
566
  ];
510
567
  function runTokensCssMigrations(css) {
511
568
  return foldMigrations(css, () => true);
@@ -1,4 +1,4 @@
1
- export { r as readLiveTokensConfig } from '../dataPaths-DBN0RPuT.cjs';
1
+ export { r as readLiveTokensConfig } from '../dataPaths-CRfD1LdA.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-DBN0RPuT.js';
1
+ export { r as readLiveTokensConfig } from '../dataPaths-CRfD1LdA.js';
2
2
 
3
3
  /**
4
4
  * A `tokens-css` migration: a pure, idempotent transform on the text of a
@@ -13,11 +13,11 @@ import {
13
13
  runTokensCssMigrations,
14
14
  semverBumpType,
15
15
  validateTokensCss
16
- } from "../chunk-TKZBVIW5.js";
17
- import "../chunk-YR6GPXW2.js";
16
+ } from "../chunk-E5QYON4L.js";
17
+ import "../chunk-XWXIMTWZ.js";
18
18
  import {
19
19
  readLiveTokensConfig
20
- } from "../chunk-D3ZVKOR4.js";
20
+ } from "../chunk-Y5CNFSSV.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.56.1",
3
+ "version": "0.58.0",
4
4
  "type": "module",
5
5
  "description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
6
6
  "keywords": [
@@ -30,6 +30,7 @@
30
30
  "src/live-tokens/data/colors-and-type/midnight-study.json",
31
31
  "src/live-tokens/data/colors-and-type/ocean.json",
32
32
  "src/live-tokens/data/colors-and-type/royal-velvet.json",
33
+ "src/live-tokens/data/colors-and-type/sketchy.json",
33
34
  "src/live-tokens/data/colors-and-type/spring-meadow.json",
34
35
  "src/live-tokens/data/colors-and-type/sunset.json",
35
36
  "src/live-tokens/data/themes/autumn.json",
@@ -37,6 +38,7 @@
37
38
  "src/live-tokens/data/themes/midnight-study.json",
38
39
  "src/live-tokens/data/themes/ocean.json",
39
40
  "src/live-tokens/data/themes/royal-velvet.json",
41
+ "src/live-tokens/data/themes/sketchy.json",
40
42
  "src/live-tokens/data/themes/spring-meadow.json",
41
43
  "src/live-tokens/data/themes/sunset.json",
42
44
  "dist-plugin",
@@ -8,7 +8,7 @@ import type {
8
8
  import { parseGoogleFontsUrl } from './fontParse';
9
9
  import { familyIndex, resolvedFaceName } from './fontPairing';
10
10
 
11
- export type PairingSlot = 'display' | 'body' | 'serif' | 'mono';
11
+ export type PairingSlot = 'display' | 'body' | 'serif' | 'mono' | 'editorial';
12
12
 
13
13
  export interface PairingFace {
14
14
  name: string;
@@ -44,13 +44,14 @@ export interface FontPairingResult {
44
44
 
45
45
  /** Slot order is also the order stamped sources land in `fontSources`;
46
46
  * `check:preset-themes` reads the preset pair back out positionally. */
47
- export const SLOT_ORDER: PairingSlot[] = ['display', 'body', 'serif', 'mono'];
47
+ export const SLOT_ORDER: PairingSlot[] = ['display', 'body', 'serif', 'mono', 'editorial'];
48
48
 
49
49
  export const SLOT_VARIABLES: Record<PairingSlot, FontStackVariable> = {
50
50
  display: '--font-display',
51
51
  body: '--font-sans',
52
52
  serif: '--font-serif',
53
53
  mono: '--font-mono',
54
+ editorial: '--font-editorial',
54
55
  };
55
56
 
56
57
  export function slugifyFamily(name: string): string {
@@ -20,6 +20,7 @@ const FONT_STACK_VARIABLES: FontStackVariable[] = [
20
20
  '--font-sans',
21
21
  '--font-serif',
22
22
  '--font-mono',
23
+ '--font-editorial',
23
24
  ];
24
25
 
25
26
  const LINK_ATTR = 'data-font-source-id';
@@ -115,9 +115,37 @@ export function defaultFontStacks(sources: FontSource[]): FontStack[] {
115
115
  { kind: 'generic', value: 'monospace' },
116
116
  ],
117
117
  },
118
+ {
119
+ variable: '--font-editorial',
120
+ slots: [
121
+ ...pick('Manrope'),
122
+ { kind: 'system', preset: 'system-ui-sans' },
123
+ { kind: 'generic', value: 'sans-serif' },
124
+ ],
125
+ },
118
126
  ];
119
127
  }
120
128
 
129
+ /**
130
+ * Seed `--font-editorial` from `--font-sans` on files written before the
131
+ * editorial role existed. The role ships as "the body face until a consumer
132
+ * repoints it", so cloning the body stack is the migration: a theme that never
133
+ * touches editorial keeps rendering exactly as it did.
134
+ *
135
+ * Presence-based like the rest of this module, so re-running is a no-op and no
136
+ * schemaVersion step is needed.
137
+ */
138
+ function ensureEditorialStack(stacks: FontStack[]): boolean {
139
+ if (stacks.some((s) => s.variable === '--font-editorial')) return false;
140
+ const sans = stacks.find((s) => s.variable === '--font-sans');
141
+ if (!sans) return false;
142
+ stacks.push({
143
+ variable: '--font-editorial',
144
+ slots: sans.slots.map((slot) => ({ ...slot })),
145
+ });
146
+ return true;
147
+ }
148
+
121
149
  // Older themes have `kind: 'font-face'` sources with embedded cssText that
122
150
  // references local woff2 files. The url() resolution for those embedded refs
123
151
  // is fragile in consumers, so swap any font-face source whose cssText is for
@@ -161,9 +189,11 @@ export function migrateColorsAndTypeFonts(colorsAndType: ColorsAndType): { migra
161
189
  if (!colorsAndType.fontStacks || colorsAndType.fontStacks.length === 0) {
162
190
  colorsAndType.fontStacks = defaultFontStacks(colorsAndType.fontSources);
163
191
  migrated = true;
192
+ } else if (ensureEditorialStack(colorsAndType.fontStacks)) {
193
+ migrated = true;
164
194
  }
165
195
  if (colorsAndType.cssVariables) {
166
- for (const key of ['--font-display', '--font-sans', '--font-serif', '--font-mono']) {
196
+ for (const key of ['--font-display', '--font-sans', '--font-serif', '--font-mono', '--font-editorial']) {
167
197
  if (key in colorsAndType.cssVariables) {
168
198
  delete colorsAndType.cssVariables[key];
169
199
  migrated = true;
@@ -13,7 +13,7 @@
13
13
  * derives the same CSS vars from the persisted config.
14
14
  */
15
15
 
16
- import { cssColorToOklch, oklchToCssClamped, gamutClamp, type Oklch } from './oklch';
16
+ import { cssColorToOklch, oklchToCss, gamutClamp, type Oklch } from './oklch';
17
17
  import { type CurveAnchor, sampleCurve, makeAnchor, tangentAnchor, resmoothAutoCurve } from '../../ui/curveEngine';
18
18
  import type { PaletteConfig } from '../themes/themeTypes';
19
19
 
@@ -204,7 +204,14 @@ export type DerivedValue =
204
204
  * then serialized as `oklch()`. Idempotent on the already-in-gamut per-step
205
205
  * derivation output. `raw` passes through untouched. */
206
206
  export function serializeDerivedValue(value: DerivedValue): string {
207
- return value.kind === 'raw' ? value.css : oklchToCssClamped(value.l, value.c, value.h);
207
+ // Emitted unclamped: the stored basis is OKLCH, and clamping into sRGB threw
208
+ // away chroma a wide-gamut display can actually show. Values already inside
209
+ // sRGB serialize identically (`gamutClamp` returns them untouched), so this
210
+ // moves nothing in an existing theme; only intent authored beyond sRGB — a
211
+ // Tailwind v4 ramp, say — now survives to the browser, which does its own
212
+ // gamut mapping at paint. `oklchToCssClamped` still backs the hex readouts,
213
+ // where a clamped projection is the whole point.
214
+ return value.kind === 'raw' ? value.css : oklchToCss(value.l, value.c, value.h);
208
215
  }
209
216
 
210
217
  export function computePaletteOklch(