@motion-proto/live-tokens 0.56.1 → 0.57.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 (90) hide show
  1. package/.claude/skills/live-tokens-pair-fonts/SKILL.md +2 -2
  2. package/CHANGELOG.md +124 -0
  3. package/README.md +1 -1
  4. package/bin/cli.mjs +2 -1
  5. package/dist-plugin/adjust/index.cjs +1 -0
  6. package/dist-plugin/adjust/index.d.cts +2 -2
  7. package/dist-plugin/adjust/index.d.ts +2 -2
  8. package/dist-plugin/adjust/index.js +1 -1
  9. package/dist-plugin/{chunk-TKZBVIW5.js → chunk-E5QYON4L.js} +59 -2
  10. package/dist-plugin/{chunk-PB2JTK2H.js → chunk-LW4SR7AZ.js} +39 -6
  11. package/dist-plugin/{chunk-YR6GPXW2.js → chunk-XWXIMTWZ.js} +0 -5
  12. package/dist-plugin/{chunk-D3ZVKOR4.js → chunk-Y5CNFSSV.js} +1 -0
  13. package/dist-plugin/{dataPaths-DBN0RPuT.d.cts → dataPaths-CRfD1LdA.d.cts} +3 -0
  14. package/dist-plugin/{dataPaths-DBN0RPuT.d.ts → dataPaths-CRfD1LdA.d.ts} +3 -0
  15. package/dist-plugin/fontPairing/index.cjs +4 -2
  16. package/dist-plugin/fontPairing/index.d.cts +3 -3
  17. package/dist-plugin/fontPairing/index.d.ts +3 -3
  18. package/dist-plugin/fontPairing/index.js +4 -3
  19. package/dist-plugin/generateColorsAndType/index.cjs +22 -9
  20. package/dist-plugin/generateColorsAndType/index.d.cts +2 -2
  21. package/dist-plugin/generateColorsAndType/index.d.ts +2 -2
  22. package/dist-plugin/generateColorsAndType/index.js +8 -5
  23. package/dist-plugin/index.cjs +158 -9
  24. package/dist-plugin/index.d.cts +1 -1
  25. package/dist-plugin/index.d.ts +1 -1
  26. package/dist-plugin/index.js +71 -4
  27. package/dist-plugin/migrateData/index.cjs +1 -0
  28. package/dist-plugin/migrateData/index.d.cts +1 -1
  29. package/dist-plugin/migrateData/index.d.ts +1 -1
  30. package/dist-plugin/migrateData/index.js +1 -1
  31. package/dist-plugin/{themeTypes-BAqtv4XO.d.cts → themeTypes-DSV3Zisf.d.cts} +12 -1
  32. package/dist-plugin/{themeTypes-BAqtv4XO.d.ts → themeTypes-DSV3Zisf.d.ts} +12 -1
  33. package/dist-plugin/tokensCssMigrations/index.cjs +58 -1
  34. package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
  35. package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
  36. package/dist-plugin/tokensCssMigrations/index.js +3 -3
  37. package/package.json +3 -1
  38. package/src/editor/core/fonts/applyFontPairing.ts +3 -2
  39. package/src/editor/core/fonts/fontLoader.ts +1 -0
  40. package/src/editor/core/fonts/fontMigration.ts +31 -1
  41. package/src/editor/core/palettes/paletteDerivation.ts +9 -2
  42. package/src/editor/core/sketch/maskField.ts +381 -0
  43. package/src/editor/core/sketch/sketchLayer.ts +1028 -0
  44. package/src/editor/core/sketch/sketchPresetService.ts +65 -0
  45. package/src/editor/core/sketch/sketchPresets.ts +328 -0
  46. package/src/editor/core/sketch/sketchStore.ts +190 -0
  47. package/src/editor/core/store/editorPersistence.ts +28 -12
  48. package/src/editor/core/store/editorTypes.ts +4 -1
  49. package/src/editor/core/store/editorViewStore.ts +2 -2
  50. package/src/editor/core/store/gradientSource.ts +15 -1
  51. package/src/editor/core/themes/parsers/gradient.ts +62 -4
  52. package/src/editor/core/themes/slices/gradients.ts +75 -14
  53. package/src/editor/core/themes/themeTypes.ts +6 -1
  54. package/src/editor/docs/Docs.svelte +4 -3
  55. package/src/editor/docs/chapters.ts +1 -0
  56. package/src/editor/docs/content/01-overview.md +2 -1
  57. package/src/editor/docs/content/editing-tokens.md +5 -1
  58. package/src/editor/docs/content/sketch-mode.md +89 -0
  59. package/src/editor/docs/content.generated.ts +3 -2
  60. package/src/editor/overlay/LiveEditorOverlay.svelte +15 -0
  61. package/src/editor/pages/EditorShell.svelte +43 -0
  62. package/src/editor/ui/EditorViewSwitcher.svelte +15 -2
  63. package/src/editor/ui/FontStackEditor.svelte +3 -0
  64. package/src/editor/ui/GradientEditor.svelte +38 -1
  65. package/src/editor/ui/UIReveal.svelte +7 -1
  66. package/src/editor/ui/UISegmentedControl.svelte +4 -8
  67. package/src/editor/ui/sections/GradientsSection.svelte +47 -1
  68. package/src/editor/ui/sketch/SketchDial.svelte +124 -0
  69. package/src/editor/ui/sketch/SketchPreview.svelte +119 -0
  70. package/src/editor/ui/sketch/SketchRange.svelte +185 -0
  71. package/src/editor/ui/sketch/SketchTab.svelte +1263 -0
  72. package/src/live-tokens/data/colors-and-type/autumn.json +17 -0
  73. package/src/live-tokens/data/colors-and-type/default.json +17 -0
  74. package/src/live-tokens/data/colors-and-type/halloween.json +17 -0
  75. package/src/live-tokens/data/colors-and-type/midnight-study.json +17 -0
  76. package/src/live-tokens/data/colors-and-type/ocean.json +17 -0
  77. package/src/live-tokens/data/colors-and-type/royal-velvet.json +17 -0
  78. package/src/live-tokens/data/colors-and-type/sketches.json +2520 -0
  79. package/src/live-tokens/data/colors-and-type/spring-meadow.json +17 -0
  80. package/src/live-tokens/data/colors-and-type/sunset.json +17 -0
  81. package/src/live-tokens/data/themes/autumn.json +17 -0
  82. package/src/live-tokens/data/themes/halloween.json +17 -0
  83. package/src/live-tokens/data/themes/midnight-study.json +17 -0
  84. package/src/live-tokens/data/themes/ocean.json +17 -0
  85. package/src/live-tokens/data/themes/royal-velvet.json +17 -0
  86. package/src/live-tokens/data/themes/sketches.json +3984 -0
  87. package/src/live-tokens/data/themes/spring-meadow.json +17 -0
  88. package/src/live-tokens/data/themes/sunset.json +17 -0
  89. package/src/live-tokens/data/tokens.generated.css +1 -0
  90. package/src/system/styles/tokens.css +15 -2
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: live-tokens-pair-fonts
3
- description: Choose and apply a Google Fonts pairing for a live-tokens theme by binding families to the --font-display, --font-sans, --font-serif and --font-mono stacks. Use whenever the user asks to pair fonts, pick a typeface, change or set the fonts, or describes type by voice: give me a font pairing, what font should the headings use, make the type more editorial, friendlier, more technical, more elegant, a serif for headings, a display font for this theme, less generic type, match the fonts to the theme. Also invoked by live-tokens-generate-theme for the type half of a whole look. Changes type only, never color. Not for a single token (use the editor) or for color (see live-tokens-generate-theme).
3
+ description: Choose and apply a Google Fonts pairing for a live-tokens theme by binding families to the --font-display, --font-sans, --font-serif, --font-mono and --font-editorial stacks. Use whenever the user asks to pair fonts, pick a typeface, change or set the fonts, or describes type by voice: give me a font pairing, what font should the headings use, make the type more editorial, friendlier, more technical, more elegant, a serif for headings, a display font for this theme, less generic type, match the fonts to the theme. Also invoked by live-tokens-generate-theme for the type half of a whole look. Changes type only, never color. Not for a single token (use the editor) or for color (see live-tokens-generate-theme).
4
4
  ---
5
5
 
6
6
  # Pairing fonts for a theme
@@ -24,7 +24,7 @@ Flags: `--dry-run` reports without writing. `--no-verify` skips the network and
24
24
  { "display": "Fraunces", "body": "Nunito Sans" }
25
25
  ```
26
26
 
27
- Every slot is optional and an omitted slot is left exactly as it is. `display` is `--font-display`, `body` is `--font-sans`; `serif` and `mono` exist when a theme needs them. A slot may be `{ "name": "...", "url": "..." }` to pin an exact URL. Spell families as Google does; the CLI reports the canonical spelling back.
27
+ Every slot is optional and an omitted slot is left exactly as it is. `display` is `--font-display`, `body` is `--font-sans`; `serif`, `mono` and `editorial` exist when a theme needs them. `editorial` is `--font-editorial`, the long-reading face behind the `--editorial-*` text style: it tracks the body face until a theme repoints it, so set it only when essays and articles should not carry the body face. A slot may be `{ "name": "...", "url": "..." }` to pin an exact URL. Spell families as Google does; the CLI reports the canonical spelling back.
28
28
 
29
29
  ## Choose the body face first
30
30
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,129 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.57.0 — A theme can be drawn by hand
4
+
5
+ ### Added
6
+
7
+ - **Sketch mode: a fourth editor view, beside Tokens, Colors and Components.**
8
+ It is an effect layer over the active theme, not a set of theme values. It
9
+ reads nothing from the theme and writes nothing back. Each component's fill
10
+ and outline are repainted onto `::before`/`::after` from the tokens that
11
+ component already owns (`--<component>-<variant>-surface`, `-border`,
12
+ `-radius`), the real background and border are hidden behind them, and both
13
+ are pushed around one shared noise field. Turning it off removes every trace.
14
+ While it is on it applies to the page behind the editor as well as to the
15
+ preview, because the layer is injected into every document `cssVarSync`
16
+ tracks. Scope is the `data-sketch` attribute, so it switches on for a whole
17
+ document root or for one preview container.
18
+ - **Seven shipped looks, and every dial behind them.** Pencil, Marker,
19
+ Whiteboard, Hatched, Dashed, Napkin and Dry marker, each a complete set of
20
+ settings with a blurb naming what it is. The dials sit under five headings:
21
+ **Border** (travel, wavelength, width, ink, pressure, pooling, dashes, a
22
+ second pass that either copies the first line or runs it through the pen again
23
+ on its own seed), **Fill** (solid or hatched, travel, per-instance offset,
24
+ rotation and scale), **Shape** (corner spread, and a corner travel that leans
25
+ the drawn box into a quadrilateral with no two sides parallel), **Icons and
26
+ SVG** (glyph travel and wavelength on their own scale, because a glyph is all
27
+ curves and needs more travel than a card's long straight edge), and **Noise**
28
+ (the shared displacement field's wavelength, roughness and waveform).
29
+ - **Ink coverage as a generated field.** The mask that thins a fill is a tiling
30
+ Perlin field rasterised to a greyscale PNG and applied with `mask-mode:
31
+ luminance`, rather than an `feTurbulence` primitive. Turbulence lands in a
32
+ narrow band around its own midpoint, so a dial walking a cut across 0 to 1
33
+ spends most of its travel outside the field and the rest reads as flat grey.
34
+ Generated, the tile is stretched onto its own measured range first, so Min and
35
+ Max always mean levels at every grain and octave count. The panel previews the
36
+ field at each stage: noise, output, blur.
37
+ - **Saved sketch presets.** Any set of dials saves under a name and reloads
38
+ later, as files under `<dataDir>/sketch-presets/` served by a new
39
+ `/api/sketch-presets` route on the dev plugin. A sketch look is a draft
40
+ effect, never part of a theme: it has no active/production pointer, it is not
41
+ adopted, and it never reaches `tokens.generated.css`. The shipped seven stay
42
+ in code, so the directory holds nothing but your own files.
43
+ - **A page can hand the layer its own elements.** The effect displaces boxes, so
44
+ a rule drawn as a `border` is not one of them. `--sketch-fill`,
45
+ `--sketch-stroke`, `--sketch-hatch-color` and `--sketch-radius` name what an
46
+ element should be drawn with, and `--sketch-icon-off: none` opts a subtree
47
+ out. The demo app's kit section draws its rules this way; the editor's own
48
+ overlay bar opts out, because the effect is for the page being designed and
49
+ not for the tool looking at it.
50
+ - **A Sketch mode chapter in the user guide.** It sits after Editing tokens and
51
+ covers the presets, the dials, and the `--sketch-*` properties a page uses to
52
+ hand the layer its own elements. Editing tokens itself now names all four
53
+ views; it had been describing two since the Colors view landed.
54
+ - **Sketches ships as an eighth preset theme.** A whole look built on the
55
+ effect: Cabin Sketch over Shantell Sans, square corners, and component
56
+ aliases tuned for a page that is drawn rather than rendered. Load it from the
57
+ Theme panel like any other preset. It stands on its own with Sketch mode off,
58
+ and the effect is what it was designed under.
59
+ - **The gradient library is open-ended.** `--gradient-N` was a fixed four-slot
60
+ scale; the Gradients section now has Add and Remove, and any number of
61
+ numbered slots loads from a theme file. A new slot seeds from the last one, so
62
+ it lands as a gradient you edit rather than an invisible transparent one.
63
+ - **A gradient can point at a corner.** Alongside degrees, a linear gradient
64
+ takes CSS's `to top right` and its seven neighbours. A keyword angles the
65
+ gradient line off the box's own diagonal, so it tracks the element's aspect
66
+ where a fixed angle cannot: `to bottom right` reads as about 95deg across a
67
+ wide heading and about 111deg once that heading wraps. The degrees underneath
68
+ are kept, so clearing the keyword lands on the nearest angle rather than
69
+ snapping to 0deg.
70
+ - **`--gradient-N-stops`.** Each slot now also emits its stop list on its own,
71
+ so a consumer can keep the theme's colours and supply their own geometry:
72
+ `linear-gradient(to top, var(--gradient-5-stops))`. One set of stops then
73
+ serves every direction a design needs, instead of a token per angle.
74
+ - **The editorial type role.** A fifth font stack (`--font-editorial`) and a
75
+ ninth text-style bundle (`--editorial-*`), for the long-reading surfaces that
76
+ should not carry the body face under an expressive display face. Both default
77
+ to indirections rather than literals: the stack resolves to `var(--font-sans)`
78
+ and the bundle mirrors `--body-md-*`, so a project that never mentions
79
+ editorial renders byte-identically. `live-tokens set-fonts` gained a matching
80
+ `editorial` slot, and the stack is editable in the editor beside the other
81
+ four.
82
+
83
+ ### Changed
84
+
85
+ - **Derived palette values serialize unclamped.** A palette's basis is OKLCH,
86
+ and clamping every derived step into sRGB on the way out threw away chroma a
87
+ wide-gamut display can show. Values already inside sRGB serialize identically,
88
+ so nothing moves in an existing theme; intent authored beyond sRGB now
89
+ survives to the browser, which does its own gamut mapping at paint. The hex
90
+ readouts still show the clamped projection, which is what a hex readout is
91
+ for.
92
+ - **The product is spelled LiveTokens.** One word, in the README, the docs and
93
+ the demo app. The package name is unchanged.
94
+ - **A test fails on any engine load at module top.** `bin/engineLoadsLazily.test.ts`
95
+ follows every `.mjs` module a test suite can reach, transitively, and rejects
96
+ a top-level import of the compiled engine. CI runs the suite before it builds
97
+ the plugin, so such a load is a publish failure rather than a test failure.
98
+ This is the bug that took down 0.56.0.
99
+
100
+ ### Fixed
101
+
102
+ - **A session persisted before the OKLCH palette basis no longer throws on
103
+ hydrate.** Such a session holds hex strings where every palette color is now
104
+ `{ l, c, h }`. `loadFromFile` migrated those on the way in and `hydrate` did
105
+ not, so the session reached the renderer with an undefined hue and threw on
106
+ the first serialization.
107
+ - **The overlay panel's chrome keeps its own font.** `tokens.css` sets the theme
108
+ font on `:where(*)`, and a matching rule beats inheritance, so the panel's
109
+ font-family never reached its children. The editor page already restored
110
+ inheritance for its chrome; the overlay now does the same.
111
+
112
+ ### Migration
113
+
114
+ - Two **tokens.css migrations** ship here, both additive: `--font-editorial`
115
+ with the `--editorial-*` bundle, and a `--gradient-N-stops` companion per
116
+ gradient slot. Each companion is read out of the slot it belongs to rather
117
+ than hardcoded, so a retuned gradient does not get a stop list contradicting
118
+ it. Run `npx live-tokens migrate` to apply them to a vendored `tokens.css`, or
119
+ set the plugin's `autoMigrate` option and let it apply additive migrations on
120
+ boot. Until then the dev plugin warns on the gap and the new names simply
121
+ resolve to nothing.
122
+ - The **editorial font stack** is added to a theme file on load, cloned from
123
+ that theme's `--font-sans`. It is presence-based and idempotent, so there is
124
+ no schema step and a theme that never touches editorial keeps rendering as it
125
+ did.
126
+
3
127
  ## 0.56.1 — Release fix
4
128
 
5
129
  ### Fixed
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Live Tokens
1
+ # LiveTokens
2
2
 
3
3
  A design system for styling and building Svelte + Vite microsites. Edit tokens and components in a dev-only editor and watch the running site repaint on every input. Save the result as a theme file and carry it between projects.
4
4
 
package/bin/cli.mjs CHANGED
@@ -56,7 +56,8 @@ Commands:
56
56
  the report without writing.
57
57
  set-fonts <brief.json> [--dry-run] [--no-verify]
58
58
  Bind Google Fonts families to --font-display,
59
- --font-sans, --font-serif and --font-mono (see
59
+ --font-sans, --font-serif, --font-mono and
60
+ --font-editorial (see
60
61
  the live-tokens-pair-fonts skill). Each family is
61
62
  verified against the Google Fonts API and the URL
62
63
  is negotiated from the weights it actually has.
@@ -270,6 +270,7 @@ function resolveDataDirs(opts = {}) {
270
270
  colorsAndTypeDir: opts.colorsAndTypeDir ? import_path.default.resolve(opts.colorsAndTypeDir) : fileConfig.colorsAndTypeDir ? import_path.default.resolve(fileConfig.colorsAndTypeDir) : sub("colors-and-type"),
271
271
  componentConfigsDir: opts.componentConfigsDir ? import_path.default.resolve(opts.componentConfigsDir) : fileConfig.componentConfigsDir ? import_path.default.resolve(fileConfig.componentConfigsDir) : sub("component-configs"),
272
272
  themesDir: opts.themesDir ? import_path.default.resolve(opts.themesDir) : fileConfig.themesDir ? import_path.default.resolve(fileConfig.themesDir) : sub("themes"),
273
+ sketchPresetsDir: sub("sketch-presets"),
273
274
  legacyManifestsDir: fileConfig.manifestsDir ? import_path.default.resolve(fileConfig.manifestsDir) : void 0
274
275
  };
275
276
  }
@@ -1,5 +1,5 @@
1
- import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-BAqtv4XO.cjs';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.cjs';
1
+ import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-DSV3Zisf.cjs';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.cjs';
3
3
  export { C as CURRENT_COMPONENT_SCHEMA_VERSION } from '../index-4N-Orzzi.cjs';
4
4
 
5
5
  type AdjustKind = 'radius' | 'padding' | 'gap' | 'border-width';
@@ -1,5 +1,5 @@
1
- import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-BAqtv4XO.js';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.js';
1
+ import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-DSV3Zisf.js';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.js';
3
3
  export { C as CURRENT_COMPONENT_SCHEMA_VERSION } from '../index-4N-Orzzi.js';
4
4
 
5
5
  type AdjustKind = 'radius' | 'padding' | 'gap' | 'border-width';
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  readLiveTokensConfig,
6
6
  resolveDataDirs
7
- } from "../chunk-D3ZVKOR4.js";
7
+ } from "../chunk-Y5CNFSSV.js";
8
8
 
9
9
  // src/editor/core/components/aliasKinds.ts
10
10
  var KIND_PATTERNS = [
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  hexToOklch,
3
3
  oklchToCss
4
- } from "./chunk-YR6GPXW2.js";
4
+ } from "./chunk-XWXIMTWZ.js";
5
5
 
6
6
  // src/editor/core/themes/parsers/globalRootBlock.ts
7
7
  function extractGlobalRootBody(source) {
@@ -375,6 +375,61 @@ var tokensCssMigration_2026_08_19_oklchColorValues = {
375
375
  }
376
376
  };
377
377
 
378
+ // vite-plugin/tokensCssMigrations/migrations/2026-08-25-editorial-type-role.ts
379
+ var STACK = [{ name: "--font-editorial", value: "var(--font-sans)" }];
380
+ var BUNDLE2 = [
381
+ { name: "--editorial-font-family", value: "var(--font-editorial)" },
382
+ { name: "--editorial-font-size", value: "var(--font-size-md)" },
383
+ { name: "--editorial-font-weight", value: "var(--font-weight-normal)" },
384
+ { name: "--editorial-line-height", value: "var(--line-height-normal)" },
385
+ { name: "--editorial-letter-spacing", value: "var(--letter-spacing-normal)" }
386
+ ];
387
+ var tokensCssMigration_2026_08_25_editorialTypeRole = {
388
+ id: "2026-08-25-editorial-type-role",
389
+ kind: "additive",
390
+ description: "Add the editorial type role (--font-editorial and the --editorial-* bundle)",
391
+ apply(css) {
392
+ const withStack = ensureScale(css, {
393
+ anchorPrefixes: ["--font-mono", "--font-serif", "--font-sans", "--font-display"],
394
+ entries: STACK
395
+ });
396
+ return ensureScale(withStack, {
397
+ sectionComment: "Editorial \u2014 the long-reading role, body face until repointed",
398
+ anchorPrefixes: [
399
+ "--eyebrow-",
400
+ "--code-",
401
+ "--body-",
402
+ "--heading-",
403
+ "--letter-spacing-",
404
+ "--line-height-",
405
+ "--font-weight-",
406
+ "--font-size-",
407
+ "--font-"
408
+ ],
409
+ entries: BUNDLE2
410
+ });
411
+ }
412
+ };
413
+
414
+ // vite-plugin/tokensCssMigrations/migrations/2026-08-26-gradient-stops.ts
415
+ var STOPS_RE = /^(?:linear|radial)-gradient\(\s*[^,]+,\s*(.+)\)$/i;
416
+ var tokensCssMigration_2026_08_26_gradientStops = {
417
+ id: "2026-08-26-gradient-stops",
418
+ kind: "additive",
419
+ description: "Add --gradient-N-stops companions to the gradient slots",
420
+ apply(css) {
421
+ const values = collectTokenValues(css);
422
+ const entries = [];
423
+ for (const [name, value] of values) {
424
+ if (!/^--gradient-\d+$/.test(name)) continue;
425
+ const stops = value.match(STOPS_RE);
426
+ if (stops) entries.push({ name: `${name}-stops`, value: stops[1].trim() });
427
+ }
428
+ if (entries.length === 0) return css;
429
+ return ensureScale(css, { anchorPrefixes: ["--gradient-"], entries });
430
+ }
431
+ };
432
+
378
433
  // vite-plugin/tokensCssMigrations/index.ts
379
434
  var TOKENS_CSS_MIGRATIONS = [
380
435
  tokensCssMigration_2026_05_29_typographyScaleAdditions,
@@ -384,7 +439,9 @@ var TOKENS_CSS_MIGRATIONS = [
384
439
  tokensCssMigration_2026_06_04_easingColorAndTypescaleAdditions,
385
440
  tokensCssMigration_2026_07_20_lineHeightRename,
386
441
  tokensCssMigration_2026_07_20_semanticTextStyles,
387
- tokensCssMigration_2026_08_19_oklchColorValues
442
+ tokensCssMigration_2026_08_19_oklchColorValues,
443
+ tokensCssMigration_2026_08_25_editorialTypeRole,
444
+ tokensCssMigration_2026_08_26_gradientStops
388
445
  ];
389
446
  function runTokensCssMigrations(css) {
390
447
  return foldMigrations(css, () => true);
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  cssColorToOklch,
3
3
  gamutClamp,
4
- oklchToCssClamped
5
- } from "./chunk-YR6GPXW2.js";
4
+ oklchToCss
5
+ } from "./chunk-XWXIMTWZ.js";
6
6
 
7
7
  // src/editor/core/storage/files/versionedFileResourceClient.ts
8
8
  function sanitizeFileName(name) {
@@ -21,6 +21,30 @@ function formatColorOpacity(name, opacity) {
21
21
  }
22
22
 
23
23
  // src/editor/core/themes/parsers/gradient.ts
24
+ var LINEAR_DIRECTIONS = [
25
+ "to top",
26
+ "to top right",
27
+ "to right",
28
+ "to bottom right",
29
+ "to bottom",
30
+ "to bottom left",
31
+ "to left",
32
+ "to top left"
33
+ ];
34
+ var DIRECTION_ANGLES = {
35
+ "to top": 0,
36
+ "to top right": 45,
37
+ "to right": 90,
38
+ "to bottom right": 135,
39
+ "to bottom": 180,
40
+ "to bottom left": 225,
41
+ "to left": 270,
42
+ "to top left": 315
43
+ };
44
+ function parseDirection(raw) {
45
+ const norm = raw.trim().toLowerCase().replace(/\s+/g, " ");
46
+ return LINEAR_DIRECTIONS.includes(norm) ? norm : null;
47
+ }
24
48
  var RADIAL_BASE_PX = 100;
25
49
  function formatStopColor(s) {
26
50
  const opacity = s.opacity ?? 100;
@@ -48,11 +72,15 @@ function formatGradientValue(v) {
48
72
  return first ? formatStopColor(first) : "transparent";
49
73
  }
50
74
  const stops = formatGradientStops(v.stops);
51
- if (v.type === "linear") return `linear-gradient(${v.angle}deg, ${stops})`;
75
+ if (v.type === "linear") {
76
+ return `linear-gradient(${v.direction ?? `${v.angle}deg`}, ${stops})`;
77
+ }
52
78
  return `radial-gradient(${formatRadialShape(v)} at ${v.centerX ?? 50}% 50%, ${stops})`;
53
79
  }
54
80
  var NUM = String.raw`-?\d+(?:\.\d+)?`;
55
- var LINEAR_RE = new RegExp(String.raw`^linear-gradient\(\s*(${NUM})deg\s*,\s*(.+)\)$`, "i");
81
+ var HEADING = String.raw`${NUM}deg|to\s+\w+(?:\s+\w+)?`;
82
+ var LINEAR_RE = new RegExp(String.raw`^linear-gradient\(\s*(${HEADING})\s*,\s*(.+)\)$`, "i");
83
+ var DEG_RE = new RegExp(String.raw`^(${NUM})deg$`, "i");
56
84
  var RADIAL_RE = new RegExp(
57
85
  String.raw`^radial-gradient\(\s*(circle|circle\s+${NUM}px|ellipse\s+${NUM}px\s+${NUM}px)\s+at\s+(${NUM})%\s+50%\s*,\s*(.+)\)$`,
58
86
  "i"
@@ -111,8 +139,12 @@ function parseGradientValue(value) {
111
139
  const css = value.trim();
112
140
  const linear = css.match(LINEAR_RE);
113
141
  if (linear) {
142
+ const deg = linear[1].match(DEG_RE);
143
+ const direction = deg ? null : parseDirection(linear[1]);
144
+ if (!deg && !direction) return null;
114
145
  const stops2 = parseStops(linear[2]);
115
- return stops2 && { type: "linear", angle: parseFloat(linear[1]), stops: stops2 };
146
+ if (!stops2) return null;
147
+ return deg ? { type: "linear", angle: parseFloat(deg[1]), stops: stops2 } : { type: "linear", angle: 0, direction, stops: stops2 };
116
148
  }
117
149
  const radial = css.match(RADIAL_RE);
118
150
  if (!radial) return null;
@@ -307,7 +339,7 @@ function scaleStepToX(step, scale) {
307
339
  return scale.steps.length > 1 ? idx / (scale.steps.length - 1) * 100 : 50;
308
340
  }
309
341
  function serializeDerivedValue(value) {
310
- return value.kind === "raw" ? value.css : oklchToCssClamped(value.l, value.c, value.h);
342
+ return value.kind === "raw" ? value.css : oklchToCss(value.l, value.c, value.h);
311
343
  }
312
344
  function computePaletteOklch(index, base, lightnessCurve, saturationCurve, curveOffset, hueCurve) {
313
345
  const { c: baseC } = base;
@@ -587,6 +619,7 @@ function reconcilePalettesFromCssVars(palettes, cssVars) {
587
619
 
588
620
  export {
589
621
  parseColorOpacity,
622
+ DIRECTION_ANGLES,
590
623
  formatGradientValue,
591
624
  parseGradientValue,
592
625
  sanitizeFileName,
@@ -99,10 +99,6 @@ function oklchToCss(l, c, h) {
99
99
  const cs = trim(c, 4);
100
100
  return `oklch(${trim(l, 4)} ${cs} ${cs === "0" ? "0" : trim(h, 2)})`;
101
101
  }
102
- function oklchToCssClamped(l, c, h) {
103
- const g = gamutClamp(l, c, h);
104
- return oklchToCss(g.l, g.c, g.h);
105
- }
106
102
  var OKLCH_RE = /^oklch\(\s*([\d.]+)(%?)\s+([\d.]+)(%?)\s+([\d.]+)(?:deg)?\s*\)$/i;
107
103
  var HEX6_RE = /^#[0-9a-f]{6}$/i;
108
104
  function parseOklchCss(value) {
@@ -124,6 +120,5 @@ export {
124
120
  oklchToHexClamped,
125
121
  gamutClamp,
126
122
  oklchToCss,
127
- oklchToCssClamped,
128
123
  cssColorToOklch
129
124
  };
@@ -42,6 +42,7 @@ function resolveDataDirs(opts = {}) {
42
42
  colorsAndTypeDir: opts.colorsAndTypeDir ? path.resolve(opts.colorsAndTypeDir) : fileConfig.colorsAndTypeDir ? path.resolve(fileConfig.colorsAndTypeDir) : sub("colors-and-type"),
43
43
  componentConfigsDir: opts.componentConfigsDir ? path.resolve(opts.componentConfigsDir) : fileConfig.componentConfigsDir ? path.resolve(fileConfig.componentConfigsDir) : sub("component-configs"),
44
44
  themesDir: opts.themesDir ? path.resolve(opts.themesDir) : fileConfig.themesDir ? path.resolve(fileConfig.themesDir) : sub("themes"),
45
+ sketchPresetsDir: sub("sketch-presets"),
45
46
  legacyManifestsDir: fileConfig.manifestsDir ? path.resolve(fileConfig.manifestsDir) : void 0
46
47
  };
47
48
  }
@@ -28,6 +28,9 @@ interface ResolvedDataDirs {
28
28
  colorsAndTypeDir: string;
29
29
  componentConfigsDir: string;
30
30
  themesDir: string;
31
+ /** No per-folder config override: sketch presets are a draft-look artifact
32
+ * with no reason to live apart from the rest of the data tree. */
33
+ sketchPresetsDir: string;
31
34
  /** Absolute path of the retired `manifestsDir` key, when the config sets it.
32
35
  * Only the pre-0.48 layout detector and the migration read it. */
33
36
  legacyManifestsDir?: string;
@@ -28,6 +28,9 @@ interface ResolvedDataDirs {
28
28
  colorsAndTypeDir: string;
29
29
  componentConfigsDir: string;
30
30
  themesDir: string;
31
+ /** No per-folder config override: sketch presets are a draft-look artifact
32
+ * with no reason to live apart from the rest of the data tree. */
33
+ sketchPresetsDir: string;
31
34
  /** Absolute path of the retired `manifestsDir` key, when the config sets it.
32
35
  * Only the pre-0.48 layout detector and the migration read it. */
33
36
  legacyManifestsDir?: string;
@@ -143,12 +143,13 @@ function fontPairingLabel(stacks, sources) {
143
143
  }
144
144
 
145
145
  // src/editor/core/fonts/applyFontPairing.ts
146
- var SLOT_ORDER = ["display", "body", "serif", "mono"];
146
+ var SLOT_ORDER = ["display", "body", "serif", "mono", "editorial"];
147
147
  var SLOT_VARIABLES = {
148
148
  display: "--font-display",
149
149
  body: "--font-sans",
150
150
  serif: "--font-serif",
151
- mono: "--font-mono"
151
+ mono: "--font-mono",
152
+ editorial: "--font-editorial"
152
153
  };
153
154
  function slugifyFamily(name) {
154
155
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -391,6 +392,7 @@ function resolveDataDirs(opts = {}) {
391
392
  colorsAndTypeDir: opts.colorsAndTypeDir ? import_path.default.resolve(opts.colorsAndTypeDir) : fileConfig.colorsAndTypeDir ? import_path.default.resolve(fileConfig.colorsAndTypeDir) : sub("colors-and-type"),
392
393
  componentConfigsDir: opts.componentConfigsDir ? import_path.default.resolve(opts.componentConfigsDir) : fileConfig.componentConfigsDir ? import_path.default.resolve(fileConfig.componentConfigsDir) : sub("component-configs"),
393
394
  themesDir: opts.themesDir ? import_path.default.resolve(opts.themesDir) : fileConfig.themesDir ? import_path.default.resolve(fileConfig.themesDir) : sub("themes"),
395
+ sketchPresetsDir: sub("sketch-presets"),
394
396
  legacyManifestsDir: fileConfig.manifestsDir ? import_path.default.resolve(fileConfig.manifestsDir) : void 0
395
397
  };
396
398
  }
@@ -1,7 +1,7 @@
1
- import { c as FontStackVariable, C as ColorsAndType, b as FontStack, F as FontSource } from '../themeTypes-BAqtv4XO.cjs';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.cjs';
1
+ import { c as FontStackVariable, C as ColorsAndType, b as FontStack, F as FontSource } from '../themeTypes-DSV3Zisf.cjs';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.cjs';
3
3
 
4
- type PairingSlot = 'display' | 'body' | 'serif' | 'mono';
4
+ type PairingSlot = 'display' | 'body' | 'serif' | 'mono' | 'editorial';
5
5
  interface PairingFace {
6
6
  name: string;
7
7
  url: string;
@@ -1,7 +1,7 @@
1
- import { c as FontStackVariable, C as ColorsAndType, b as FontStack, F as FontSource } from '../themeTypes-BAqtv4XO.js';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.js';
1
+ import { c as FontStackVariable, C as ColorsAndType, b as FontStack, F as FontSource } from '../themeTypes-DSV3Zisf.js';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.js';
3
3
 
4
- type PairingSlot = 'display' | 'body' | 'serif' | 'mono';
4
+ type PairingSlot = 'display' | 'body' | 'serif' | 'mono' | 'editorial';
5
5
  interface PairingFace {
6
6
  name: string;
7
7
  url: string;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  readLiveTokensConfig,
3
3
  resolveDataDirs
4
- } from "../chunk-D3ZVKOR4.js";
4
+ } from "../chunk-Y5CNFSSV.js";
5
5
 
6
6
  // src/editor/core/fonts/fontParse.ts
7
7
  function parseGoogleFontsUrl(url2) {
@@ -101,12 +101,13 @@ function fontPairingLabel(stacks, sources) {
101
101
  }
102
102
 
103
103
  // src/editor/core/fonts/applyFontPairing.ts
104
- var SLOT_ORDER = ["display", "body", "serif", "mono"];
104
+ var SLOT_ORDER = ["display", "body", "serif", "mono", "editorial"];
105
105
  var SLOT_VARIABLES = {
106
106
  display: "--font-display",
107
107
  body: "--font-sans",
108
108
  serif: "--font-serif",
109
- mono: "--font-mono"
109
+ mono: "--font-mono",
110
+ editorial: "--font-editorial"
110
111
  };
111
112
  function slugifyFamily(name) {
112
113
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -139,10 +139,6 @@ function oklchToCss(l, c, h) {
139
139
  const cs = trim(c, 4);
140
140
  return `oklch(${trim(l, 4)} ${cs} ${cs === "0" ? "0" : trim(h, 2)})`;
141
141
  }
142
- function oklchToCssClamped(l, c, h) {
143
- const g = gamutClamp(l, c, h);
144
- return oklchToCss(g.l, g.c, g.h);
145
- }
146
142
  var OKLCH_RE = /^oklch\(\s*([\d.]+)(%?)\s+([\d.]+)(%?)\s+([\d.]+)(?:deg)?\s*\)$/i;
147
143
  var HEX6_RE = /^#[0-9a-f]{6}$/i;
148
144
  function parseOklchCss(value) {
@@ -336,7 +332,7 @@ function scaleStepToX(step, scale) {
336
332
  return scale.steps.length > 1 ? idx / (scale.steps.length - 1) * 100 : 50;
337
333
  }
338
334
  function serializeDerivedValue(value) {
339
- return value.kind === "raw" ? value.css : oklchToCssClamped(value.l, value.c, value.h);
335
+ return value.kind === "raw" ? value.css : oklchToCss(value.l, value.c, value.h);
340
336
  }
341
337
  function computePaletteOklch(index, base, lightnessCurve, saturationCurve, curveOffset, hueCurve) {
342
338
  const { c: baseC } = base;
@@ -609,6 +605,16 @@ function formatColorOpacity(name, opacity) {
609
605
  }
610
606
 
611
607
  // src/editor/core/themes/parsers/gradient.ts
608
+ var DIRECTION_ANGLES = {
609
+ "to top": 0,
610
+ "to top right": 45,
611
+ "to right": 90,
612
+ "to bottom right": 135,
613
+ "to bottom": 180,
614
+ "to bottom left": 225,
615
+ "to left": 270,
616
+ "to top left": 315
617
+ };
612
618
  var RADIAL_BASE_PX = 100;
613
619
  function formatStopColor(s) {
614
620
  const opacity = s.opacity ?? 100;
@@ -636,11 +642,15 @@ function formatGradientValue(v) {
636
642
  return first ? formatStopColor(first) : "transparent";
637
643
  }
638
644
  const stops = formatGradientStops(v.stops);
639
- if (v.type === "linear") return `linear-gradient(${v.angle}deg, ${stops})`;
645
+ if (v.type === "linear") {
646
+ return `linear-gradient(${v.direction ?? `${v.angle}deg`}, ${stops})`;
647
+ }
640
648
  return `radial-gradient(${formatRadialShape(v)} at ${v.centerX ?? 50}% 50%, ${stops})`;
641
649
  }
642
650
  var NUM = String.raw`-?\d+(?:\.\d+)?`;
643
- var LINEAR_RE = new RegExp(String.raw`^linear-gradient\(\s*(${NUM})deg\s*,\s*(.+)\)$`, "i");
651
+ var HEADING = String.raw`${NUM}deg|to\s+\w+(?:\s+\w+)?`;
652
+ var LINEAR_RE = new RegExp(String.raw`^linear-gradient\(\s*(${HEADING})\s*,\s*(.+)\)$`, "i");
653
+ var DEG_RE = new RegExp(String.raw`^(${NUM})deg$`, "i");
644
654
  var RADIAL_RE = new RegExp(
645
655
  String.raw`^radial-gradient\(\s*(circle|circle\s+${NUM}px|ellipse\s+${NUM}px\s+${NUM}px)\s+at\s+(${NUM})%\s+50%\s*,\s*(.+)\)$`,
646
656
  "i"
@@ -655,7 +665,8 @@ function makeDefaultGradients() {
655
665
  {
656
666
  variable: "--gradient-1",
657
667
  type: "linear",
658
- angle: 90,
668
+ angle: DIRECTION_ANGLES["to right"],
669
+ direction: "to right",
659
670
  stops: [
660
671
  { position: 0, color: "--color-brand-500" },
661
672
  { position: 100, color: "--color-accent-500" }
@@ -673,7 +684,8 @@ function makeDefaultGradients() {
673
684
  {
674
685
  variable: "--gradient-3",
675
686
  type: "linear",
676
- angle: 90,
687
+ angle: DIRECTION_ANGLES["to right"],
688
+ direction: "to right",
677
689
  stops: [
678
690
  { position: 0, color: "--color-success-500" },
679
691
  { position: 100, color: "--color-info-500" }
@@ -1914,6 +1926,7 @@ function resolveDataDirs(opts = {}) {
1914
1926
  colorsAndTypeDir: opts.colorsAndTypeDir ? import_path.default.resolve(opts.colorsAndTypeDir) : fileConfig.colorsAndTypeDir ? import_path.default.resolve(fileConfig.colorsAndTypeDir) : sub("colors-and-type"),
1915
1927
  componentConfigsDir: opts.componentConfigsDir ? import_path.default.resolve(opts.componentConfigsDir) : fileConfig.componentConfigsDir ? import_path.default.resolve(fileConfig.componentConfigsDir) : sub("component-configs"),
1916
1928
  themesDir: opts.themesDir ? import_path.default.resolve(opts.themesDir) : fileConfig.themesDir ? import_path.default.resolve(fileConfig.themesDir) : sub("themes"),
1929
+ sketchPresetsDir: sub("sketch-presets"),
1917
1930
  legacyManifestsDir: fileConfig.manifestsDir ? import_path.default.resolve(fileConfig.manifestsDir) : void 0
1918
1931
  };
1919
1932
  }
@@ -1,5 +1,5 @@
1
- import { F as FontSource, b as FontStack, G as GradientDiskToken, O as Oklch, H as HarmonyMode, C as ColorsAndType } from '../themeTypes-BAqtv4XO.cjs';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.cjs';
1
+ import { F as FontSource, b as FontStack, G as GradientDiskToken, O as Oklch, H as HarmonyMode, C as ColorsAndType } from '../themeTypes-DSV3Zisf.cjs';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.cjs';
3
3
  export { C as CURRENT_COMPONENT_SCHEMA_VERSION } from '../index-4N-Orzzi.cjs';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { F as FontSource, b as FontStack, G as GradientDiskToken, O as Oklch, H as HarmonyMode, C as ColorsAndType } from '../themeTypes-BAqtv4XO.js';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.js';
1
+ import { F as FontSource, b as FontStack, G as GradientDiskToken, O as Oklch, H as HarmonyMode, C as ColorsAndType } from '../themeTypes-DSV3Zisf.js';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.js';
3
3
  export { C as CURRENT_COMPONENT_SCHEMA_VERSION } from '../index-4N-Orzzi.js';
4
4
 
5
5
  /**