@motion-proto/live-tokens 0.47.0 → 0.48.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 (124) hide show
  1. package/.claude/skills/live-tokens-adjust-shape-space/SKILL.md +68 -0
  2. package/.claude/skills/live-tokens-create-component/SKILL.md +2 -2
  3. package/.claude/skills/live-tokens-generate-theme/SKILL.md +152 -0
  4. package/CHANGELOG.md +292 -0
  5. package/README.md +37 -26
  6. package/bin/adjust.mjs +254 -0
  7. package/bin/cli.mjs +94 -7
  8. package/bin/generate-theme.mjs +251 -0
  9. package/bin/migrate.mjs +95 -6
  10. package/dist-plugin/adjust/index.cjs +259 -0
  11. package/dist-plugin/adjust/index.d.cts +45 -0
  12. package/dist-plugin/adjust/index.d.ts +45 -0
  13. package/dist-plugin/adjust/index.js +176 -0
  14. package/dist-plugin/{chunk-H4TRUINI.js → chunk-44RSTAII.js} +0 -48
  15. package/dist-plugin/chunk-6OZFXIQI.js +316 -0
  16. package/dist-plugin/chunk-76TFDTJO.js +556 -0
  17. package/dist-plugin/chunk-D3ZVKOR4.js +52 -0
  18. package/dist-plugin/dataPaths-DBN0RPuT.d.cts +54 -0
  19. package/dist-plugin/dataPaths-DBN0RPuT.d.ts +54 -0
  20. package/dist-plugin/generateColorsAndType/index.cjs +1781 -0
  21. package/dist-plugin/generateColorsAndType/index.d.cts +81 -0
  22. package/dist-plugin/generateColorsAndType/index.d.ts +81 -0
  23. package/dist-plugin/generateColorsAndType/index.js +1210 -0
  24. package/dist-plugin/index.cjs +901 -533
  25. package/dist-plugin/index.d.cts +3 -2
  26. package/dist-plugin/index.d.ts +3 -2
  27. package/dist-plugin/index.js +658 -1082
  28. package/dist-plugin/migrateData/index.cjs +728 -0
  29. package/dist-plugin/migrateData/index.d.cts +60 -0
  30. package/dist-plugin/migrateData/index.d.ts +60 -0
  31. package/dist-plugin/migrateData/index.js +348 -0
  32. package/dist-plugin/themeTypes-DMHZOnUn.d.cts +211 -0
  33. package/dist-plugin/themeTypes-DMHZOnUn.d.ts +211 -0
  34. package/dist-plugin/tokensCssMigrations/index.cjs +2 -1
  35. package/dist-plugin/tokensCssMigrations/index.d.cts +3 -15
  36. package/dist-plugin/tokensCssMigrations/index.d.ts +3 -15
  37. package/dist-plugin/tokensCssMigrations/index.js +4 -2
  38. package/package.json +18 -4
  39. package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +183 -158
  40. package/src/editor/component-editor/scaffolding/ComponentFileMenu.svelte +0 -4
  41. package/src/editor/component-editor/scaffolding/SaveAsDialog.svelte +4 -4
  42. package/src/editor/component-editor/scaffolding/TokenLayout.svelte +2 -59
  43. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +1 -1
  44. package/src/editor/core/components/adjustAliases.ts +180 -0
  45. package/src/editor/core/components/aliasKinds.ts +73 -0
  46. package/src/editor/core/components/componentConfigService.ts +26 -38
  47. package/src/editor/core/flashStatus.ts +1 -1
  48. package/src/editor/core/fonts/fontMigration.ts +13 -13
  49. package/src/editor/core/fonts/fontPairing.ts +35 -0
  50. package/src/editor/core/palettes/paletteDerivation.ts +128 -16
  51. package/src/editor/core/preview/lookPreview.ts +141 -0
  52. package/src/editor/core/productionPulse.ts +34 -21
  53. package/src/editor/core/storage/files/versionedFileResourceClient.ts +18 -45
  54. package/src/editor/core/store/editorConfigStore.ts +2 -2
  55. package/src/editor/core/store/editorPersistence.ts +0 -1
  56. package/src/editor/core/store/editorStore.ts +114 -51
  57. package/src/editor/core/store/editorTypes.ts +0 -1
  58. package/src/editor/core/store/gradientSource.ts +2 -2
  59. package/src/editor/core/themes/colorsAndTypeService.ts +95 -0
  60. package/src/editor/core/themes/generateColorsAndType.ts +433 -0
  61. package/src/editor/core/themes/loadRows.ts +64 -0
  62. package/src/editor/core/themes/lookSummary.ts +75 -0
  63. package/src/editor/core/themes/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts +3 -3
  64. package/src/editor/core/themes/migrations/2026-05-13-primary-to-brand.ts +2 -2
  65. package/src/editor/core/themes/migrations/2026-05-26-drop-overlay-extra-stops.ts +3 -3
  66. package/src/editor/core/themes/migrations/2026-08-13-drop-legacy-shape-space-keys.ts +38 -0
  67. package/src/editor/core/themes/migrations/2026-08-15-line-height-scale-rename.ts +57 -0
  68. package/src/editor/core/themes/migrations/index.ts +19 -11
  69. package/src/editor/core/themes/slices/components.ts +4 -4
  70. package/src/editor/core/themes/slices/fonts.ts +1 -1
  71. package/src/editor/core/themes/slices/gradients.ts +22 -0
  72. package/src/editor/core/themes/slices/palettes.ts +2 -2
  73. package/src/editor/core/themes/themeInit.ts +21 -29
  74. package/src/editor/core/themes/themeService.ts +218 -78
  75. package/src/editor/core/themes/themeTypes.ts +94 -51
  76. package/src/editor/docs/Docs.svelte +1 -0
  77. package/src/editor/docs/chapters.ts +1 -0
  78. package/src/editor/docs/content/01-overview.md +1 -1
  79. package/src/editor/docs/content/editing-tokens.md +6 -6
  80. package/src/editor/docs/content/getting-started.md +9 -7
  81. package/src/editor/docs/content/themes-workflow.md +74 -34
  82. package/src/editor/docs/content/where-themes-live.md +61 -0
  83. package/src/editor/docs/content.generated.ts +5 -4
  84. package/src/editor/index.ts +27 -27
  85. package/src/editor/pages/ComponentEditorPage.svelte +2 -2
  86. package/src/editor/pages/EditorShell.svelte +4 -30
  87. package/src/editor/ui/BezierCurveEditor.svelte +2 -2
  88. package/src/editor/ui/ColorEditPanel.svelte +1 -1
  89. package/src/editor/ui/FileLoadList.svelte +67 -7
  90. package/src/editor/ui/GradientEditor.svelte +3 -3
  91. package/src/editor/ui/PaletteEditor.svelte +26 -12
  92. package/src/editor/ui/ThemePanel.svelte +1067 -0
  93. package/src/editor/ui/UIDialog.svelte +11 -9
  94. package/src/editor/ui/UIPillButton.svelte +7 -2
  95. package/src/editor/ui/colors/ColorWheel.svelte +34 -1
  96. package/src/editor/ui/curveEngine.ts +48 -0
  97. package/src/editor/ui/index.ts +4 -2
  98. package/src/editor/ui/palette/PaletteBase.svelte +75 -51
  99. package/src/editor/ui/palette/PaletteJumpButton.svelte +3 -8
  100. package/src/live-tokens/data/colors-and-type/autumn.json +2490 -0
  101. package/src/live-tokens/data/{themes → colors-and-type}/default.json +816 -293
  102. package/src/live-tokens/data/colors-and-type/halloween.json +2529 -0
  103. package/src/live-tokens/data/colors-and-type/midnight-study.json +2536 -0
  104. package/src/live-tokens/data/colors-and-type/ocean.json +2489 -0
  105. package/src/live-tokens/data/colors-and-type/royal-velvet.json +2520 -0
  106. package/src/live-tokens/data/colors-and-type/spring-meadow.json +2476 -0
  107. package/src/live-tokens/data/colors-and-type/sunset.json +2528 -0
  108. package/src/live-tokens/data/themes/autumn.json +3918 -0
  109. package/src/live-tokens/data/themes/halloween.json +3992 -0
  110. package/src/live-tokens/data/themes/midnight-study.json +3932 -0
  111. package/src/live-tokens/data/themes/ocean.json +3917 -0
  112. package/src/live-tokens/data/themes/royal-velvet.json +3983 -0
  113. package/src/live-tokens/data/themes/spring-meadow.json +3904 -0
  114. package/src/live-tokens/data/themes/sunset.json +3931 -0
  115. package/src/live-tokens/data/tokens.generated.css +106 -117
  116. package/src/system/styles/CONVENTIONS.md +1 -1
  117. package/src/system/styles/fonts.css +1 -1
  118. package/template/README.md +7 -5
  119. package/template/_gitignore +0 -6
  120. package/src/editor/core/components/componentPersist.ts +0 -62
  121. package/src/editor/core/manifests/manifestService.ts +0 -172
  122. package/src/editor/ui/ManifestFileManager.svelte +0 -446
  123. package/src/editor/ui/ThemeFileManager.svelte +0 -785
  124. package/src/live-tokens/data/manifests/default.json +0 -35
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: live-tokens-adjust-shape-space
3
+ description: Adjust corner radius, padding, gap, and border width across live-tokens components by moving each token alias along the shipped scales. Use when the user asks for pill or capsule buttons, rounded, rounder, sharp, sharper, square corners, softer, harder, a bigger or smaller corner radius, thicker or thinner borders, or talks about spacing and padding. Make the buttons pill shaped, give the cards sharp corners, make the windows sharper, make the UI softer, space it out, tighter, denser, more compact, airier, more breathing room. Changes shape and space aliases per component, never color, fonts, or tokens.css. Not for editing a single token (use the editor) or for color (see live-tokens-generate-theme).
4
+ ---
5
+
6
+ # Adjusting shape and space
7
+
8
+ You translate the request into a small ops file; the CLI resolves each matching alias on its token ladder, writes the result into each component's unsaved buffer, and prints a report card. Never hand-edit the data tree.
9
+
10
+ ## Workflow
11
+
12
+ 1. Write the ops file to a temp path (not the project tree), e.g. `/tmp/adjust-ops.json`.
13
+ 2. Run `npx live-tokens adjust /tmp/adjust-ops.json`. It writes `component-configs/<id>/_working.json` for every component the ops change, which is the buffer the page already runs. `--dry-run` prints the report without writing.
14
+ 3. Read the report card: every changed alias old → new, plus skips (raw value, off the ladder, already at the ladder end, pill preserved). Exit 1 means the run was rejected; the message names the offending op or the missing input, so fix it and re-run.
15
+ 4. Tell the user to reload the app and look. Offer the inverse op as the undo, and say the edit is unsaved until they save the open theme.
16
+
17
+ Each run reads the LIVE config (buffer, else the open theme, else the shipped default), so "a bit more" and "back one" compound naturally.
18
+
19
+ ## The ops file
20
+
21
+ Global, relative:
22
+
23
+ ```json
24
+ { "ops": [{ "kind": "radius", "shift": 1 }, { "kind": "padding", "shift": 1 }, { "kind": "gap", "shift": 1 }] }
25
+ ```
26
+
27
+ Targeted, absolute:
28
+
29
+ ```json
30
+ { "ops": [{ "target": "button", "kind": "radius", "set": "--radius-full" }] }
31
+ ```
32
+
33
+ - `name`: ignored. Buffers are fixed slots, so a name names no file, and the CLI says it dropped one. Leave it out.
34
+ - `target` (optional): a component id (the folder names under `src/live-tokens/data/component-configs/`). A named component targets its id: "windows" or "modals" is `dialog`, "cards" is `card`, "tabs" is `tabbar`; an unknown target is a hard error. "The UI", "everything", or no noun at all means global, so omit it.
35
+ - `kind`: `radius | padding | gap | border-width`.
36
+ - `set` or `shift`, exactly one of the two. `set` takes an existing token on that kind's ladder. `shift` is a whole number of steps, clamped at the ladder ends.
37
+ - `full` (radius shifts only): admits `--radius-full` as the ladder's top rung. `set` plus `full` is an error, so a pill request is `set: "--radius-full"` with no `full` flag.
38
+
39
+ ## Idioms
40
+
41
+ | Request | Ops |
42
+ |---|---|
43
+ | pill, capsule | radius `set: "--radius-full"` |
44
+ | sharp, square corners | radius `set: "--radius-none"`, or `--radius-sm` for "mostly sharp" |
45
+ | rounded (a named component) | radius `shift: 2` |
46
+ | softer, rounder (global) | radius `shift: 1` to `2`, no `full` |
47
+ | harder, sharper | radius `shift: -1` to `-2` |
48
+ | increase the radius, less round, more round | radius `shift: 1` or `-1` with `"full": true`, so repeated pushes reach pill and a pill can come back down |
49
+ | space it out, airier, breathing room | padding and gap `shift: 1` |
50
+ | tighter, denser, more compact | padding and gap `shift: -1` |
51
+ | thicker, thinner borders | border-width `shift: 1` or `-1` |
52
+
53
+ Magnitude words: "slightly" or "a bit" is 1 step, unqualified is 1 to 2, "much", "way", or "really" is 2 to 3. Mood words often mean both axes: "softer" is rounder plus airier, "compact" is tighter padding plus smaller gaps.
54
+
55
+ ## Ladders
56
+
57
+ Radius runs `none, sm, md, lg, xl, 2xl, 3xl, 4xl`, with `full` as the gated ninth rung. Space (padding and gap) is the editor picker's subset: `0, 2, 4, 6, 8, 10, 12, 16, 20, 24, 32, 48`, so every written value stays re-editable by hand. Border width is the full `--border-width-*` scale. `set` values must be on the ladder (`--space-64` is rejected); an alias sitting off the subset snaps to the nearest rung when shifted, and the report marks a snap that lands against the requested direction.
58
+
59
+ ## Scope
60
+
61
+ Every value written is an existing token; nothing new is minted. `tokens.css`, saved themes, colors, and fonts are never touched, so any theme composes with any shape state. An adjustment is an unsaved edit: Save the open theme in the editor to keep it, and Adopt to ship it. Both stay human actions.
62
+
63
+ ## Verify
64
+
65
+ - The CLI exits 0 and the report card lists the changes you expected, with no surprising skips.
66
+ - The app (dev server running) shows the new shape on each changed component after a reload.
67
+ - `component-configs/<id>/_working.json` exists for every component the report listed. That buffer is the whole change: it stays until the open theme is saved or another theme is loaded.
68
+ - To revert, run the inverse ops, or load a theme in the Theme panel to discard every unsaved edit.
@@ -552,7 +552,7 @@ It enforces the file layout, the `:global(:root)` block, token-suffix vocabulary
552
552
  1. Registration resolves to a real `sourceFile` and a non-empty schema.
553
553
  2. Schema variables are unique within the component.
554
554
  3. Every editable token (excluding `hidden: true`, `kind: 'gradient'`, and padding-side suffixes) is declared in the runtime `<style>` block.
555
- 4. Every editable token is seeded in the production-pointed config under `src/live-tokens/data/component-configs/<id>/`.
555
+ 4. Every editable token is seeded in `src/live-tokens/data/component-configs/<id>/default.json`.
556
556
  5. `setComponentAlias` round-trips the alias through the slice.
557
557
 
558
558
  A new first-party component is auto-covered the moment it lands in `builtInRegistry` — `npm test` will fail if any of the five checks miss. For a consumer-authored component, mirror this pattern in your own test suite if you want the same drift protection (the same test logic works against any `registerComponent` registration; iterate `getComponentRegistryEntries()` after your `main.ts` has run).
@@ -564,6 +564,6 @@ Finally navigate to `/live-tokens/components` and confirm the runtime behaviours
564
564
  - [ ] The new component appears in the nav rail under the **CUSTOM** group (system entries above, custom below the labeled divider).
565
565
  - [ ] Token rows render. Color pickers, radius selectors, font selectors all work.
566
566
  - [ ] Linked-block (if your component has linked siblings): shared rows appear with the link toggle. Changing the linked value broadcasts across every variant.
567
- - [ ] First save creates `component-configs/<id>/default.json`. Subsequent saves write `_active.json` plus any named files.
567
+ - [ ] `component-configs/<id>/default.json` is derived from the `:global(:root)` block at boot. Save writes `_working.json`, the unsaved buffer the open theme captures; Save As also writes a named preset.
568
568
  - [ ] Reset returns each variable to its `:global(:root)` default.
569
569
  - [ ] Boot validation is clean (no warnings about the component being missing from the server scan, or about disk-vs-registry drift).
@@ -0,0 +1,152 @@
1
+ ---
2
+ name: live-tokens-generate-theme
3
+ description: Generate a complete live-tokens color theme from a natural-language mood brief by choosing 10 OKLCH seeds and running the packaged generator, which enforces AA contrast automatically. Use when the user asks for a color theme, color scheme, or palette by mood, vibe, season, holiday, or hue — make me a bright and cheerful theme, give me a dark and moody night theme, a St. Patrick's Day theme with green and gold, a Christmas theme, a red-based theme, make it warmer, more contrast, a calmer palette. Changes color assignments only, never fonts. Not for editing a single token (use the editor) or building pages (see live-tokens-build-page).
4
+ ---
5
+
6
+ # Generating a theme from a mood brief
7
+
8
+ You translate the brief into 10 OKLCH seed colors plus a scheme; the CLI does everything else (curve assembly, AA contrast enforcement with auto-correction, writing the theme, opening it). Never hand-author theme JSON and never edit the data tree directly. Seeds in, valid theme out.
9
+
10
+ ## Workflow
11
+
12
+ 1. Translate the brief into a seed file using the framework below. Write it to a temp path (not the project tree), e.g. `/tmp/theme-brief.json`.
13
+ 2. Run `npx live-tokens generate-theme /tmp/theme-brief.json`. It writes `themes/<slug>.json`, opens that theme, and prints a contrast report card. Exit 1 means unmet floors.
14
+ 3. Read the report. Auto-corrections are fine (the engine adjusted text curves to hit the floors). Unmet floors mean the seeds themselves are unworkable; each failure line says which seed to adjust (usually raise the seed's lightness or cut chroma). Fix the brief and re-run — same name, same file, it overwrites.
15
+ 4. Tell the user to look at the running app. Offer refinements ("warmer", "more contrast", "less saturated") as seed adjustments to the same brief, re-run.
16
+
17
+ Flags: `--dry-run` prints the report without writing; `--no-activate` writes the theme without opening it. Opening a theme never changes what the site ships: only Adopt, in the editor, does that.
18
+
19
+ Warn once per session when iterating: regeneration replaces the whole color state of that theme, including any manual palette tweaks made in the editor after the last generation.
20
+
21
+ ## The brief
22
+
23
+ ```json
24
+ {
25
+ "name": "Spring Meadow",
26
+ "scheme": "light",
27
+ "seeds": {
28
+ "Brand": { "l": 0.62, "c": 0.17, "h": 145 },
29
+ "Accent": { "l": 0.80, "c": 0.15, "h": 95 },
30
+ "Special": { "l": 0.60, "c": 0.19, "h": 300 },
31
+ "Canvas": { "l": 0.97, "c": 0.01, "h": 120 },
32
+ "Neutral": { "l": 0.55, "c": 0.012, "h": 140 },
33
+ "Alternate": { "l": 0.58, "c": 0.009, "h": 60 },
34
+ "Info": { "l": 0.60, "c": 0.15, "h": 255 },
35
+ "Success": { "l": 0.60, "c": 0.16, "h": 150 },
36
+ "Warning": { "l": 0.75, "c": 0.15, "h": 85 },
37
+ "Danger": { "l": 0.58, "c": 0.20, "h": 25 }
38
+ },
39
+ "harmony": { "mode": "analogous" }
40
+ }
41
+ ```
42
+
43
+ All 10 seeds are required. A seed may also be a `"#rrggbb"` hex string (converted for you). OKLCH: `l` 0–1 perceptual lightness, `c` chroma (0 grey, ~0.37 max), `h` hue degrees. `name` becomes the theme file slug, tidied to lowercase with hyphens and stripped of any leading underscore (those names are reserved); `"default"` is refused (protected package theme). `harmony` is an optional record of your reasoning; the seeds are ground truth. `canvasGradient` is an optional boolean — see "The canvas sky" below before setting it.
44
+
45
+ Roles: **Brand** is the dominant chromatic identity; **Accent** the supporting color; **Special** the rare expressive tertiary; **Canvas** is the page background verbatim; **Neutral** drives all neutral surfaces and body text; **Alternate** is the second near-grey family; the four status colors are conventional signals.
46
+
47
+ ## Chroma budget: color is inversely proportional to area
48
+
49
+ | Tier | Palettes | Chroma |
50
+ |---|---|---|
51
+ | Ground (~60% of every screen) | Canvas, Neutral, Alternate | C 0.005–0.03 |
52
+ | Dominant chromatic (~30%) | Brand | C 0.10–0.20 |
53
+ | Garnish (~10%) | Accent, Special | may exceed Brand; at most one at full saturation |
54
+ | Conditional | Info, Success, Warning, Danger | C 0.12–0.19 |
55
+
56
+ A good theme reads as 3–4 hue families on screen, never 10. Neutral and Alternate stay near-grey, tinted toward the theme (Neutral near Brand's hue; Alternate offset +15–60° or as a warm/cool counterpoint), never pure C = 0.
57
+
58
+ ## Per-role bands
59
+
60
+ | Seed | Light scheme | Dark scheme | Hue |
61
+ |---|---|---|---|
62
+ | Canvas | L 0.92–0.98, C 0.02–0.06 (see below) | L 0.15–0.28, C 0.01–0.05 | brand hue or its harmony slot |
63
+ | Neutral / Alternate | L ≈ 0.55, C 0.008–0.02 | same | see chroma budget |
64
+ | Brand | L 0.45–0.62, C 0.12–0.20 | L 0.70–0.83, C cut by ~⅓ | the brief's identity hue |
65
+ | Accent | harmony slot, or ΔL ≥ 0.25 from Brand when the mode collapses hue distance | lighten/desaturate like Brand | harmony slot |
66
+ | Special | most expressive; default = Brand hue +60° at ~65% of Brand's C | same transform | harmony slot |
67
+ | Info | shared status L (0.55–0.65 light) | lighten like Brand | H 230–260 |
68
+ | Success | shared status L | same | H 140–155 |
69
+ | Warning | L ≥ 0.75 (vivid yellow must be light) | same | H 70–90 |
70
+ | Danger | shared status L, C 0.15–0.20 | same | H 20–30 |
71
+
72
+ - **The canvas carries the theme's identity — commit to it.** The page background is the largest area on screen and the strongest differentiator between themes; a timid canvas makes every theme look the same. Below C ≈ 0.015 at L ≥ 0.95 a tint is imperceptible: that near-white is a deliberate choice for clean/minimal briefs, never the default. Three escalating levels of commitment, matched to the brief:
73
+ 1. *Tinted paper* (most UI briefs): a tint you can actually see — C 0.02–0.06, dropping L to 0.92–0.95 where the hue needs room.
74
+ 2. *Colored ground* (expressive briefs): L 0.85–0.92 at C 0.05–0.10 — the page is unmistakably mint, parchment, sky.
75
+ 3. *Full-color ground* (holiday and statement briefs): the canvas IS the theme color — a red Christmas page (0.40–0.48, C 0.12–0.16, H 25) with green and gold on it, an orange Halloween page. Keep canvas L ≤ ~0.48 or ≥ ~0.85 so text has somewhere to go; the contrast gate enforces legibility either way. A saturated background is a legitimate choice, not something to correct.
76
+ - **Gamut note for light canvases**: blue tints cap very low at high L (H 264 at L 0.95 barely reaches C 0.03) — for a blue-leaning canvas, lower L instead of fighting the ceiling; yellow/green/cream hues tint generously at high L.
77
+ - **When generating a set of themes, make the canvases pairwise distinct.** Any two Canvas seeds should differ noticeably in hue (at visible chroma) or in L. Two light themes both near (0.97, 0.01, anything) read as the same theme with different buttons. Dark canvases differentiate the same way: deep indigo, plum, and near-black violet are three different nights.
78
+ - **Dark scheme is a transform, not just a dark Canvas**: every chromatic seed lightens to L ≈ 0.75–0.85 and drops about a third of its chroma. Saturated color vibrates on dark grounds.
79
+ - **Equal lightness = equal weight**: give the four statuses one shared L; do the same for Brand vs Accent when they should balance.
80
+ - Status hues never rotate with the harmony; only their L/C adapt to the mood.
81
+
82
+ ## Mood dials
83
+
84
+ Empirically: pleasantness rises with lightness (strongly) and saturation (weakly); energy/arousal rises with saturation; drama/dominance rises with dark + saturated.
85
+
86
+ | Brief says | Dials |
87
+ |---|---|
88
+ | cheerful, bright, playful | light scheme; Brand/Accent L 0.7–0.9, C 0.15–0.22; warm hues 40–140 (yellow is the strongest joy hue) |
89
+ | calm, serene, soft | light scheme; C 0.03–0.08 on everything chromatic; cool hues 140–260 |
90
+ | energetic, bold | C ≥ 0.18 at L 0.55–0.65; red/orange/magenta |
91
+ | dark, moody, dramatic, luxurious | dark scheme; Canvas L 0.15–0.25; purple, deep blue, crimson; keep working accents light per the dark transform, reserve dark-saturated color for one or two moments |
92
+ | professional, trustworthy | blue 230–265, C 0.08–0.15; everything else muted |
93
+ | warm / cool | hues 20–110 (plus pink 290–360) / hues 140–290 |
94
+
95
+ Avoid mid-lightness yellow-green (H 100–120 at L 0.5–0.7, C ≈ 0.1) unless the brief asks for olive/toxic.
96
+
97
+ ## Gamut guardrails (don't request impossible seeds)
98
+
99
+ - Dark saturated yellow does not exist: H 90 at L 0.4 caps at C ≈ 0.08 and reads olive. Vivid yellow needs L ≥ 0.8. Brown = dark low-chroma orange.
100
+ - Vivid light blue does not exist: H 264 at L 0.9 caps at C ≈ 0.05. Rich blue lives at L 0.40–0.55.
101
+ - Teal/sky cap at C ≈ 0.15; a "vivid teal" is C 0.13–0.15.
102
+ - Peak chroma anchors: red H20 C 0.25 @ L 0.63; orange H60 C 0.18 @ L 0.76; yellow H90 C 0.18 @ L 0.86; green H140 C 0.28 @ L 0.88; blue H264 C 0.28 @ L 0.50; magenta H320 C 0.31 @ L 0.65.
103
+
104
+ The engine gamut-clamps regardless; these rules keep your *intent* achievable rather than silently muted.
105
+
106
+ ## Choosing the harmony mode
107
+
108
+ Modes and hue offsets from Brand: complementary +180; split-complementary +150/+210; triadic +120/+240; tetradic +60/+180/+240; square +90 steps; compound +30/+180/+210; analogous ±30; monochromatic same hue.
109
+
110
+ - Vague or single-adjective brief → monochromatic or analogous; differentiate Accent from Brand by L/C, not hue. Polished-UI default: Accent = Brand's hue at ~45% chroma, Special = +60° at ~65% chroma.
111
+ - Brief names two colors → measure their hue gap and pick the matching mode (green + gold ≈ 60–90° → analogous/compound).
112
+ - Drama or maximum contrast → complementary/triadic/tetradic; then never pair max-chroma text with a near-black ground; tone one side down.
113
+
114
+ ## The canvas sky (optional page-background gradient)
115
+
116
+ The brief may set `"canvasGradient": true` to render the page background as a
117
+ vertical gradient built from the Canvas ramp. The engine picks the stops itself
118
+ (two ramp steps from the Canvas anchor on the scheme's safe side), so the
119
+ choice you make is only *whether*, never *how*.
120
+
121
+ Default **off**. Turn it on only when the brief evokes atmosphere: sky, night,
122
+ dusk, dawn, sunset, glow, candlelight, underwater, depth — or when the user
123
+ asks for a gradient outright. Keep it off for crisp, clean, flat, minimal, or
124
+ corporate briefs, and whenever in doubt. Most themes should not have one; a sky
125
+ that appears on every generated theme stops meaning anything. When you enable
126
+ it, say why in one sentence when reporting back ("night brief → canvas sky").
127
+
128
+ A sky needs a committed canvas: when the Canvas seed anchors at the ramp edge
129
+ (near-white light canvas, near-black dark one) there is no room on the safe
130
+ side and the engine skips the sky, saying so in the report. An atmospheric
131
+ brief that wants one should already be at canvas commitment level 2–3.
132
+
133
+ ## Named themes (canonical OKLCH anchors)
134
+
135
+ Holiday briefs are statement briefs — default to commitment level 2–3 above, not cream. The named colors go on the *ground*, not just the buttons.
136
+
137
+ - **Christmas**: red (0.53, 0.21, 22) + green (0.46, 0.11, 155) + gold (0.77, 0.14, 91). Strongest form: a full red canvas (0.42, 0.14, 25) with green Brand and gold Accent on it (dark scheme). Softer form: evergreen canvas (0.35, 0.07, 160) or deep cream (0.95, 0.04, 85). Never a 50/50 red-green split — one owns the ground, the other highlights.
138
+ - **Halloween**: pumpkin (0.70, 0.20, 46) + purple (0.51, 0.21, 313) + poison green (0.73, 0.20, 137). Strongest form: an orange canvas (0.45, 0.13, 55) with violet and poison-green accents; alternative: near-black violet canvas with pumpkin Brand. Dark scheme either way.
139
+ - **St. Patrick's**: green (0.51, 0.13, 152) Brand, gold Accent, white/beige neutrals.
140
+ - **Ocean**: deep blue (0.35, 0.08, 237) vs aqua (0.78, 0.12, 214); H 180–240.
141
+ - **Sunset**: hues 90 → 320 through red, L falling 0.85 → 0.40.
142
+ - **Autumn / Fall**: leaves and dry grass — canvas warm tan/parchment (0.87, 0.06, 80), rust Brand (0.55, 0.15, 40), golden Accent (0.75, 0.15, 85), moss/olive Special (0.55, 0.10, 120), warm brown neutrals H 50–70. Deep red H 25 welcome. **Spring**: pastels L 0.85–0.95, C 0.04–0.10, greens 130–150 / pinks 0–20, mint canvas.
143
+
144
+ ## Scope
145
+
146
+ Fonts are never touched (they carry forward from the live look, as do shadows and component aliases). Swatch gradients (`--gradient-1..4`) carry forward when user-tuned; if they are absent or still stock, the engine rebuilds them from the new theme's families — you never author them. Radius is out of scope for generation. Shipping the theme stays a human action: Adopt, in the editor.
147
+
148
+ ## Verify
149
+
150
+ - The CLI exits 0 and the report card shows every check ✓ (auto-corrected is fine).
151
+ - The app (dev server running) shows the new theme after a reload; the editor's Theme panel names it.
152
+ - If the user wants the previous look back, the CLI output names the theme that was open; load it from the Theme panel.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,297 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.48.0 — Themes are documents
4
+
5
+ ### Added
6
+
7
+ - **Loading is preview first.** Picking a theme in the Load window paints
8
+ it on the page while the window stays open: switch between looks freely,
9
+ then Save to keep one or Cancel to return to exactly what you had,
10
+ unsaved edits included. The preview is paint only; nothing touches disk
11
+ until Save. A "Colors and type only. Keep my shapes" toggle previews and
12
+ loads just a theme's palette and fonts over your current component
13
+ shapes, and your own saved colors-and-type files appear in the same list
14
+ under a badge, where that mode is implied.
15
+
16
+ - **One Theme panel.** The editor's separate colors-and-type and theme
17
+ managers merge into a single panel: one identity, Save, Save As, Load,
18
+ Import, Export. Colors & Type and a Components drift count appear as
19
+ read-only parts inside it, the way a component shows its parts. The seven
20
+ presets live in this panel's Load list and nowhere else. Save captures the
21
+ look on screen, the colors and type you have been editing included, so
22
+ there is no separate save first.
23
+
24
+ - **Adopt ships the whole look.** Production state and the Adopt action
25
+ live on the Theme panel's root card: one action saves the open theme and
26
+ publishes it, colors and type plus every component it carries, with one
27
+ CSS regeneration. Component editors hold their own unsaved state, which
28
+ the panel cannot write, so Save and Adopt both say how many components are
29
+ waiting on their own editors before they run.
30
+
31
+ - **`generate-theme` turns a mood brief into a complete theme.** `npx
32
+ live-tokens generate-theme <brief.json>` takes ten OKLCH seeds plus a
33
+ scheme, assembles the full curve set, enforces AA contrast with automatic
34
+ correction rounds, writes `themes/<slug>.json`, and opens it. The bundled
35
+ `live-tokens-generate-theme` skill translates natural-language briefs
36
+ ("dark and moody night theme", "St. Patrick's Day with green and gold")
37
+ into seeds. Seven preset themes generated this way ship in the package:
38
+ Autumn, Halloween, Midnight Study, Ocean, Royal Velvet, Spring Meadow,
39
+ Sunset.
40
+
41
+ - **`adjust` moves shape and space along the token scales.** `npx
42
+ live-tokens adjust <ops.json>` shifts or sets every matching radius,
43
+ padding, gap, and border-width alias across component configs: relative
44
+ shifts preserve the cross-component hierarchy, `--radius-full` is a
45
+ gated rung so a global "softer" never silently turns the UI into
46
+ capsules, and spacing moves along the editor picker's 12-step subset so
47
+ every written value stays hand-editable. The result lands in each
48
+ component's unsaved buffer, so save the open theme to keep it. The bundled
49
+ `live-tokens-adjust-shape-space` skill maps "make the buttons pill
50
+ shaped", "make the UI softer", "space it out" onto ops files.
51
+
52
+ - **Gradients travel with the theme.** The `--gradient-N` swatch tokens
53
+ round-trip through colors-and-type files as a structured `gradients` field:
54
+ the editable type, angle, and stops, not just the rendered CSS strings, which
55
+ stay in `cssVariables` as a projection for production. Loading restores your
56
+ tuned gradients; files saved before the field existed keep the stock set they
57
+ rendered. `generate-theme` carries tuned gradients forward and rebuilds stock
58
+ ones from the new theme's color families.
59
+
60
+ - **Themes are encapsulated.** A theme now carries its whole look by value
61
+ (`schemaVersion: 3`): its full colors and type plus a copy of every
62
+ non-default component config. Deleting any colors-and-type or component
63
+ file never breaks a saved theme, and the import/export bundle is the same
64
+ format as the file on disk. Existing pointer themes migrate on first
65
+ dev-server start, resolving and embedding what they reference.
66
+
67
+ - **The seven presets ship as full example looks.** One theme per preset
68
+ embeds its colors and type plus a distinct shape personality and a Google
69
+ Fonts pairing: Halloween goes fully square with heavy borders and Mystery
70
+ Quest, Midnight Study pairs sharp windows and round buttons with EB
71
+ Garamond, and no two presets share a corner-radius and spacing profile or
72
+ a font family. Load one to try the complete look, load Motion Proto to
73
+ come back. `npm run generate:preset-themes` regenerates all seven from the
74
+ component defaults.
75
+
76
+ ### Changed
77
+
78
+ - **Themes are documents; the working set is a buffer. (breaking)** A theme file
79
+ is the whole look, `themes/_active.json` names the one the editor has open,
80
+ and `themes/_production.json` names the one your site ships. Anything not yet
81
+ saved into a theme lives in one reserved slot per layer, `_working.json`, and
82
+ a slot exists only where the look sits off the shipped default, so a new
83
+ project has none. The per-layer `_active.json` / `_production.json` pointer
84
+ files, about fifty in a full tree, are retired, and applying a theme no longer
85
+ writes a copy of it under its own slug. The files that piled up, one set per
86
+ theme sampled, go with the mechanism that made them.
87
+
88
+ - **Trying a theme no longer publishes it. (breaking)** Loading a theme used to
89
+ set production in the same step, so sampling the presets rewrote
90
+ `tokens.generated.css` behind your back. Loading now opens the theme and does
91
+ nothing else. Adopt is the only action that changes what your site ships.
92
+
93
+ - **A component ships with its theme. (breaking)** Production is one saved theme
94
+ rather than a mix of per-slice pointers, so a component editor's Adopt saves
95
+ and publishes the open theme, that component with it. The per-slice promote
96
+ door is gone.
97
+
98
+ - **Existing projects need one `npx live-tokens migrate`.** It starts with the
99
+ directories: a project last opened on 0.47.1 or earlier keeps its colors and
100
+ type in `data/themes/` and its whole looks in `data/manifests/`, so the
101
+ migration moves `themes/` to `colors-and-type/` and `manifests/` to `themes/`
102
+ before reading a single file. Then it heals what is inside. It reads what the
103
+ retired pointers resolved to and records it as the production theme, keeps
104
+ live state that had drifted from the open theme as a buffer, deletes the
105
+ copies a saved theme already carries, and clears the pointer files. A file
106
+ matching no theme is yours and is kept; the effective production output never
107
+ changes without being written down as `themes/recovered-production.json`, so
108
+ that file appearing is the normal outcome when your live look had drifted from
109
+ the production pointers, not an error. `--check` prints the whole plan,
110
+ renames included. Until you run it the dev server leaves the data directory
111
+ alone: it writes nothing, rebuilds no CSS, refuses the editor's save doors,
112
+ and prints what to run. Restart it afterwards.
113
+
114
+ Two things to finish by hand. Token references that the migration repaired
115
+ reach `tokens.generated.css` on the next save and adopt, not at migration
116
+ time, so re-save your theme (make any edit, or Save As over it) and adopt it
117
+ once. And a `.gitignore` entry for `data/themes/_backups/` or
118
+ `data/manifests/_backups/` now names the wrong directory: any old backups move
119
+ with their directory, so repath the entry to `data/colors-and-type/_backups/`
120
+ or drop it.
121
+
122
+ - **`npm update` will not bring you here.** Pre-1.0 a caret range pins the
123
+ minor, so `^0.47.1` never resolves to 0.48.0. Ask for it by name: `npm
124
+ install @motion-proto/live-tokens@0.48.0`.
125
+
126
+ - **Re-copy the bundled skills.** `npx live-tokens setup-claude` copies the
127
+ Claude Code skills into your repo's `.claude/skills/`. That copy is a copy: it
128
+ does not follow the package, and a stale one describes CLIs and files this
129
+ release changed. Run the command again after upgrading, with `--force` to
130
+ overwrite.
131
+
132
+ - **The REST surface, before and after.** For anyone who proxies, mocks or
133
+ scripts `/api/live-tokens/*`. A door retired in place answers 405, so a caller
134
+ that still holds one gets an answer it can read. The `/manifests/*` paths are
135
+ not retired in place: they are gone, and the middleware passes them through to
136
+ your app like any unclaimed URL.
137
+
138
+ | Before, through 0.47.1 | Now |
139
+ | --- | --- |
140
+ | `/themes`, `/themes/:name` (colors and type) | `/colors-and-type`, `/colors-and-type/:name` |
141
+ | `GET /themes/active`, `PUT /themes/active` | `GET /colors-and-type/active`; writes go to `PUT /colors-and-type/working` |
142
+ | `GET`/`PUT /themes/production` (colors and type) | `/colors-and-type/production`, 405 on every method |
143
+ | `PUT /component-configs/:comp/active` | `PUT /component-configs/:comp/working` |
144
+ | `GET`/`PUT /component-configs/:comp/production` | 405 on every method: a component ships with its theme |
145
+ | `/manifests`, `/manifests/:name`, `/manifests/:name/apply`, `/manifests/:name/export`, `/manifests/import` | the same shapes under `/themes` |
146
+ | `GET`/`PUT /manifests/active` | `GET`/`PUT /themes/active` |
147
+ | new | `PUT /production`: adopt the open theme, the only door that publishes |
148
+ | new | `GET /themes/production`: the whole theme your site ships |
149
+ | new | `GET`/`PUT`/`DELETE /colors-and-type/working` and `/component-configs/:comp/working` |
150
+
151
+ - **Public API. (breaking)** `setActiveFile`, `setProductionFile`,
152
+ `getProductionInfo`, the `ProductionInfo` type and the `activeFileName` store
153
+ leave with the per-layer pointers they drove. `getProductionTheme`,
154
+ `writeWorkingColorsAndType`, the `LiveSource` type and the `openThemeSlug`
155
+ store arrive.
156
+
157
+ - **CLIs follow the model.** `generate-theme` writes `themes/<slug>.json` and
158
+ opens it, instead of writing a colors-and-type file and flipping pointers at
159
+ it. `adjust` writes each touched component's buffer; its `--no-activate` flag
160
+ named files that no longer exist, so it is rejected with what to do instead.
161
+ `live-tokens migrate` runs the data heal alongside the tokens.css migrations.
162
+
163
+ - **One Theme panel, one save and load surface.** The editor sidebar holds a
164
+ single Theme panel: Save, Save As, Load with preview, Import, Export, the
165
+ production state and Adopt at the root, with Colors & Type and Components
166
+ as read-only parts under it. Colors and type stops being a file the user
167
+ manages: no list, no Save, no Save As, no lifecycle of its own. It names
168
+ the two faces the page is showing, and no working file name appears
169
+ anywhere in the panel.
170
+
171
+ - **Load can take colors and type alone.** "Colors and type only. Keep my
172
+ shapes." in the Load window previews and applies just the palette and the
173
+ fonts, leaving every component setting as it is. Older colors and type
174
+ files are listed there too, marked, and picking one is always that
175
+ narrower load. `GET /colors-and-type` marks each file `isPackage`, so a
176
+ local copy of a shipped preset stays reachable while the presets
177
+ themselves are offered once, as whole themes.
178
+
179
+ - **The Default theme is written and regenerated at boot.** It is a full set
180
+ derived from the package default colors and type and each component's
181
+ `:global(:root)` defaults, rewritten whenever the derived content drifts
182
+ and restored if deleted outside the file manager. The package no longer
183
+ ships the file; a consumer's boot always materializes a current local
184
+ copy.
185
+
186
+ - **Almost everything is deletable.** Colors-and-type and component files are
187
+ presets now, so nothing live points at one and any of them can go. Deleting
188
+ the theme you have open is legal too: the buffer survives its document, so
189
+ the look on screen stays, and open heals to the shipped version if one
190
+ shadows it, otherwise to Default. Three refusals remain, each about
191
+ something a delete would break: the protected `default` name, the theme in
192
+ production, and a shipped file with no local copy.
193
+
194
+ - **Load applies the complete look.** Loading a theme opens it: its embedded
195
+ copies fill the working buffer and components it does not carry go back to
196
+ their defaults. Components that are not installed are skipped and reported
197
+ instead of silently ignored.
198
+
199
+ - **"Manifest" retires; a theme is the whole look.** The file that carries a
200
+ complete look is a **theme**, and the colors-and-typography layer inside it
201
+ is **Colors & Type**. Every surface follows: `data/manifests/` →
202
+ `data/themes/` and the old `data/themes/` → `data/colors-and-type/`;
203
+ `/api/live-tokens/manifests/*` → `/api/live-tokens/themes/*` and the old
204
+ `/api/live-tokens/themes/*` → `/api/live-tokens/colors-and-type/*`; the
205
+ public exports `listManifests`, `applyManifest`, `saveAsManifest` and their
206
+ siblings become `listThemes`, `applyTheme`, `saveAsTheme`; `npm run
207
+ generate:preset-manifests` and `collapse:manifest` become
208
+ `generate:preset-themes` and `collapse:theme`. The manifest family never
209
+ shipped, and the routes ship as a matched client and server pair, so no
210
+ compatibility shim exists anywhere. The one key a consumer must act on is
211
+ below.
212
+
213
+ - **Config keys follow the vocabulary.** `themesDir` now names the whole-look
214
+ directory, the meaning `manifestsDir` used to carry; the new
215
+ `colorsAndTypeDir` names the colors-and-typography directory that
216
+ `themesDir` used to name. A consumer that set `themesDir` in
217
+ `live-tokens.config.json` before this release must rename it to
218
+ `colorsAndTypeDir`.
219
+
220
+ - **A theme file is `schemaVersion: 3`.** The embedded key `theme` becomes
221
+ `colorsAndType`. The boot migration carries a v1 pointer file or a v2
222
+ encapsulated file to v3 in one pass. Export writes `kind: "theme-bundle"`
223
+ at that version; the `manifest-bundle` kind every release through 0.47.1
224
+ wrote is still imported, and only at its own v1, so a crossed pair stays
225
+ rejected.
226
+
227
+ ### Fixed
228
+
229
+ - **Dead shape and space keys dropped from every colors-and-type file**
230
+ (colors-and-type schema version 4): the `--badge-trait-*`,
231
+ `--sectiondivider-padding`, and `--dialog-{primary,secondary}-*` shape keys
232
+ had no consumers since their components were restructured; a migration
233
+ removes them from `cssVariables`.
234
+
235
+ - **Line-height references follow the scale that was renamed under them**
236
+ (colors-and-type schema version 5, component-config schema version 21):
237
+ 0.41.0 reshaped `--line-height-{xs..xl}` into leading vocabulary in
238
+ `tokens.css`, but nothing carried a saved file's references across, so every
239
+ one of them pointed at a token that no longer existed. Both layers now migrate
240
+ on load, a colors-and-type file's `cssVariables` and a component config's
241
+ alias values alike: `xs` to `none`, `sm` to `tighter`, `md` to `normal`, `lg`
242
+ to `relaxed`. The retired 2.0 slot (`xl`) lands on `relaxed`, the nearest
243
+ surviving step, which is a visible change to any line that used it.
244
+
245
+ - **A colors-and-type file left among the themes is refused, not read as a
246
+ theme.** Both kinds carry a `schemaVersion` and the sequences overlap, so one
247
+ parsed as a current theme whose colors resolved to the package default: it
248
+ listed as a theme that painted the shipped look, and the boot migration
249
+ rewrote it, taking the palette with it. Every door that reads a theme off disk
250
+ now checks the shape, answers 422, and leaves the file alone.
251
+
252
+ - **Adopting while a shipped theme is active records the adoption.** The
253
+ adopt path used to return success while writing nothing when the active
254
+ theme resolved from the package; it now forks the theme locally,
255
+ shadow-and-restore style.
256
+
257
+ - **Orphan component-config directories removed** (`detailnav`, `stateditor`,
258
+ `slotprobe`, `floatingtokentags`): no shipped component reads them.
259
+
260
+ - **`collapse-theme-to-default` no longer drops `harmonyAxes`** or coerces a
261
+ missing `fontStacks` to an empty object when baking a theme into the
262
+ shipped defaults.
263
+
264
+ ### Known limitations
265
+
266
+ - **Production freshness resets on reload.** Nothing on disk records when the
267
+ last bake happened, so the editor tracks it for the session. Save a theme
268
+ after adopting it and the panel reads "out of sync" until you adopt again,
269
+ which is right; reload the page and it reads "in production" again while the
270
+ baked CSS is a version behind. Adopt when in doubt, it costs nothing to
271
+ repeat.
272
+
273
+ ## 0.47.1 — Straightened curves
274
+
275
+ ### Fixed
276
+
277
+ - **`setCurveAnchor` could insert a handle that overran its neighbour.**
278
+ A fresh anchor's tangent handles were sized from the gap alone, so an
279
+ interior anchor placed past roughly the curve's midpoint could leave the
280
+ new segment non-monotone in x — the one thing `sampleCurve`'s binary
281
+ search cannot survive. Insertion now scales the neighbours' facing
282
+ handles by the share of the gap each keeps, the same rule de Casteljau
283
+ uses, and gives the new anchor only the room left over; `liftCurveAnchor`
284
+ reverses it on removal.
285
+
286
+ - **The default theme's curves are regenerated to their endpoints.** The
287
+ shipped `default.json` carried curves hand-dragged in the editor: two
288
+ palette-lightness curves had handles overrunning an interior anchor
289
+ (the bug above, already on disk), and a text-saturation curve overshot
290
+ its own endpoint. All 80 curves are now straight interpolations between
291
+ their designed endpoint values, with the one base-color placement per
292
+ family per curve kept and pinned to its historical step. Full audit in
293
+ `docs/plans/default-theme-curve-audit.md`.
294
+
3
295
  ## 0.47.0 — Colors and Tokens hand palettes to each other
4
296
 
5
297
  ### Added