@motion-proto/live-tokens 0.62.0 → 0.63.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 (67) hide show
  1. package/.claude/skills/live-tokens-adjust-geometry/SKILL.md +4 -4
  2. package/.claude/skills/live-tokens-build-page/SKILL.md +21 -3
  3. package/.claude/skills/live-tokens-create-component/SKILL.md +16 -48
  4. package/.claude/skills/live-tokens-create-component/references/fixed-overlays.md +10 -1
  5. package/.claude/skills/live-tokens-create-component/references/intrinsics.md +7 -5
  6. package/.claude/skills/live-tokens-create-component/references/sketch-mode.md +1 -1
  7. package/.claude/skills/live-tokens-create-component/references/token-naming.md +50 -0
  8. package/.claude/skills/live-tokens-generate-theme/SKILL.md +16 -9
  9. package/.claude/skills/live-tokens-pair-fonts/SKILL.md +8 -6
  10. package/.claude/skills/live-tokens-pick-component/SKILL.md +19 -5
  11. package/CHANGELOG.md +41 -0
  12. package/bin/migrate.mjs +6 -2
  13. package/dist-plugin/adjust/index.cjs +1 -1
  14. package/dist-plugin/adjust/index.d.cts +1 -1
  15. package/dist-plugin/adjust/index.d.ts +1 -1
  16. package/dist-plugin/adjust/index.js +1 -1
  17. package/dist-plugin/{chunk-232GZGQU.js → chunk-NDJJORKJ.js} +342 -5
  18. package/dist-plugin/{chunk-Y5CNFSSV.js → chunk-RVE3MNKM.js} +1 -1
  19. package/dist-plugin/{chunk-OPYOK2CA.js → chunk-ZHPX7ZYQ.js} +39 -1
  20. package/dist-plugin/{dataPaths-CRfD1LdA.d.ts → dataPaths-DZUzVv8H.d.cts} +3 -3
  21. package/dist-plugin/{dataPaths-CRfD1LdA.d.cts → dataPaths-DZUzVv8H.d.ts} +3 -3
  22. package/dist-plugin/fontPairing/index.cjs +1 -1
  23. package/dist-plugin/fontPairing/index.d.cts +1 -1
  24. package/dist-plugin/fontPairing/index.d.ts +1 -1
  25. package/dist-plugin/fontPairing/index.js +1 -1
  26. package/dist-plugin/generateColorsAndType/index.cjs +1 -1
  27. package/dist-plugin/generateColorsAndType/index.d.cts +1 -1
  28. package/dist-plugin/generateColorsAndType/index.d.ts +1 -1
  29. package/dist-plugin/generateColorsAndType/index.js +1 -1
  30. package/dist-plugin/index.cjs +417 -34
  31. package/dist-plugin/index.d.cts +1 -1
  32. package/dist-plugin/index.d.ts +1 -1
  33. package/dist-plugin/index.js +31 -23
  34. package/dist-plugin/migrateData/index.cjs +350 -9
  35. package/dist-plugin/migrateData/index.d.cts +1 -1
  36. package/dist-plugin/migrateData/index.d.ts +1 -1
  37. package/dist-plugin/migrateData/index.js +9 -5
  38. package/dist-plugin/tokensCssMigrations/index.cjs +39 -1
  39. package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
  40. package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
  41. package/dist-plugin/tokensCssMigrations/index.js +2 -2
  42. package/package.json +4 -2
  43. package/src/app/site.css +16 -0
  44. package/src/editor/core/preview/lookPreview.ts +46 -5
  45. package/src/editor/core/productionPulse.ts +6 -2
  46. package/src/editor/core/sketch/maskField.ts +5 -5
  47. package/src/editor/core/sketch/sketchLayer.ts +8 -8
  48. package/src/editor/core/sketch/sketchStore.ts +233 -79
  49. package/src/editor/core/sketch/{sketchPresetService.ts → sketchStyleService.ts} +14 -14
  50. package/src/editor/core/sketch/{sketchPresets.ts → sketchStyles.ts} +17 -17
  51. package/src/editor/core/themes/themeDocumentSync.ts +2 -0
  52. package/src/editor/core/themes/themeInit.ts +19 -1
  53. package/src/editor/core/themes/themeService.ts +7 -2
  54. package/src/editor/core/themes/themeTypes.ts +5 -0
  55. package/src/editor/docs/content/editing-tokens.md +1 -1
  56. package/src/editor/docs/content/sketch-mode.md +27 -16
  57. package/src/editor/docs/content/themes-workflow.md +30 -21
  58. package/src/editor/docs/content/where-themes-live.md +9 -5
  59. package/src/editor/docs/content.generated.ts +4 -4
  60. package/src/editor/overlay/LiveTokensRouter.svelte +1 -1
  61. package/src/editor/ui/EditorViewSwitcher.svelte +3 -3
  62. package/src/editor/ui/ThemePanel.svelte +47 -1
  63. package/src/editor/ui/sections/textStyles.ts +15 -1
  64. package/src/editor/ui/sketch/SketchPreview.svelte +3 -3
  65. package/src/editor/ui/sketch/SketchTab.svelte +87 -53
  66. package/src/system/components/FloatingTokenTags.css +10 -8
  67. package/src/system/styles/tokens.css +17 -2
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: live-tokens-adjust-geometry
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. Also invoked by live-tokens-generate-theme for the geometry half of a whole look. 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).
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, softer, or harder corners; thicker or thinner borders; or density space it out, tighter, denser, more compact, airier, more breathing room. Also invoked by live-tokens-generate-theme for the geometry half of a whole look. 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
4
  ---
5
5
 
6
6
  # Adjusting geometry
@@ -12,7 +12,7 @@ You translate the request into a small ops file; the CLI resolves each matching
12
12
  1. Write the ops file to `scratch/adjust-ops.json`.
13
13
  2. Run `npx live-tokens adjust scratch/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
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. Read where the controls landed, not only that the run succeeded: a button, badge, input, or tab padding sitting at `--space-6` is on its floor, and one that also carries `--radius-full` wants a targeted lift.
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.
15
+ 4. Tell the user to reload the page before saving. The editor keeps the look in the browser and writes the buffers from that copy, so a Save in a tab that was open during the run puts the pre-run shape back and the report you just showed them becomes a lie. After the reload, offer the inverse op as the undo and say the edit is unsaved until they save the open theme.
16
16
 
17
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
18
 
@@ -31,7 +31,7 @@ Targeted, absolute:
31
31
  ```
32
32
 
33
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.
34
+ - `target` (optional): a component id (the folder names under `src/live-tokens/data/component-configs/`, which the Catalogue in **live-tokens-pick-component** also names in full). 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
35
  - `kind`: `radius | padding | gap | border-width`.
36
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
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.
@@ -56,7 +56,7 @@ Magnitude words: "slightly" or "a bit" is 1 step, unqualified is 1 to 2, "much",
56
56
 
57
57
  A global op spends the same number of steps everywhere, but a step costs a control far more than a container. `padding shift: -2` takes a card from a 16px inset to 10px and it is still a card. It takes a button from 8 to 4, doubled to 8px at each end, around an 18px line. The button stops reading as a button.
58
58
 
59
- So a global compaction is `shift: -1`. When the brief wants more, spend the extra steps on the containers by name (`card`, `dialog`, `panel`, `sidenavigation`, `table`, `codesnippet`) and leave the controls alone. Loosening is not symmetric: airier is safe globally, because nothing breaks by growing.
59
+ So a global compaction is `shift: -1`. When the brief wants more, spend the extra steps on the containers by name (`card`, `dialog`, `panel`, `collapsiblesection`, `sidenavigation`, `table`, `codesnippet`) and leave the controls alone. Loosening is not symmetric: airier is safe globally, because nothing breaks by growing.
60
60
 
61
61
  A pill needs the room most. `--radius-full` bends the corner in over the first and last glyph, so a capsule wants more horizontal inset than a square-cornered control, never less. `--space-8` is the floor for a large-text pill, which is where compact Midnight Study sits; the roomier pill presets (Ocean, Sunset, Royal Velvet) run `--space-10` to `--space-12`. Pair the radius op with a padding `set` on the same target, placed after any global compaction so it wins outright:
62
62
 
@@ -1,16 +1,18 @@
1
1
  ---
2
2
  name: live-tokens-build-page
3
- description: Apply the @motion-proto/live-tokens project conventions when building a page: use shipped components from the catalogue, reference theme tokens (never hex/pixel literals), mount routes dynamically, register each route's page source, and import site.css per-page. Use when the user asks to build / create / lay out a page, route, hero, marketing page, landing page, dashboard, settings screen, or pricing page; add a route; place / drop / use an existing component on a page; or assemble a screen from the live-tokens catalogue. For component-choice decisions, see live-tokens-pick-component. For authoring a brand-new component, see live-tokens-create-component.
3
+ description: Apply the @motion-proto/live-tokens project conventions when building a page: shipped components, theme tokens over hex/pixel literals, dynamic route mounting, per-page site.css. Use when the user asks to build, create, or lay out a page, route, hero, landing page, dashboard, settings screen, or pricing page; add a route; place an existing component on a page; or assemble a screen from the catalogue. For component choice see live-tokens-pick-component; for a brand-new component, live-tokens-create-component; for look and feel mid-build, live-tokens-generate-theme or live-tokens-adjust-geometry.
4
4
  ---
5
5
 
6
6
  # Building pages in a live-tokens project
7
7
 
8
8
  Two rules above all else:
9
9
 
10
- 1. **Use a shipped component if one fits.** Import from `@motion-proto/live-tokens/components/<Name>.svelte`. See [[live-tokens-pick-component]] for the catalogue and the confusing-pair decisions. Author custom markup only when nothing fits, and then consider [[live-tokens-create-component]] so the new piece is editable too.
10
+ 1. **Use a shipped component if one fits.** Import from `@motion-proto/live-tokens/components/<Name>.svelte`. See **live-tokens-pick-component** for the catalogue and the confusing-pair decisions. Author custom markup only when nothing fits, and then consider **live-tokens-create-component** so the new piece is editable too.
11
11
  2. **Use theme tokens for every value.** Every color, spacing, radius, font-size, and font-family in page CSS is a `var(--token-*)`. No hex literals. No pixel literals. A change in `/live-tokens/editor` should repaint your page.
12
12
 
13
- For text, reach for a whole text style rather than assembling one: `--heading-xl` through `--heading-sm`, `--body-md`, `--body-sm`, `--editorial-md`, `--editorial-sm`, `--eyebrow`, and `--code` each carry a `-font-family`, `-font-size`, `-font-weight`, `-line-height`, and `-letter-spacing`. A heading set from `--heading-lg-*` retypes when the theme's fonts change; one set from a raw `font-size` does not.
13
+ For text, reach for a whole text style rather than assembling one: `--heading-xl` through `--heading-sm`, `--body-md`, `--body-sm`, `--editorial-xl` through `--editorial-sm`, `--eyebrow`, and `--code` each carry a `-font-family`, `-font-size`, `-font-weight`, `-line-height`, and `-letter-spacing`. A heading set from `--heading-lg-*` retypes when the theme's fonts change; one set from a raw `font-size` does not.
14
+
15
+ Text inside a `Card` or a `CollapsibleSection` is typed by that container, not by the page: the slot pins the axes the container owns onto nested `p`, `ul`, `ol`, and `li`, so a consumer's global element rules cannot break a card's body. Pass `prose={false}` when the page should own the type instead, which is also what full-bleed media wants.
14
16
 
15
17
  ## Layout
16
18
 
@@ -26,6 +28,21 @@ To place children at specific page-column positions, span the parent grid (`grid
26
28
  Either way use `lazy`, not a static top-level import: static imports evaluate every page module at boot and leak page CSS into the editor routes.
27
29
  - Import `site.css` from each page's `<script>` block, never from `main.ts` (would leak into editor routes).
28
30
 
31
+ The entry shape, for a project whose `App.svelte` has moved on from the template:
32
+
33
+ ```svelte
34
+ const pages = {
35
+ '/pricing': {
36
+ lazy: () => import('./pages/Pricing.svelte'),
37
+ source: 'src/pages/Pricing.svelte',
38
+ label: 'Pricing',
39
+ icon: 'fa-tag',
40
+ },
41
+ };
42
+ ```
43
+
44
+ `source` is what makes Page Source work; drop `label` to keep a route reachable by URL but off the nav rail.
45
+
29
46
  ## Avoid
30
47
 
31
48
  - Hex or pixel literals in page CSS.
@@ -33,6 +50,7 @@ To place children at specific page-column positions, span the parent grid (`grid
33
50
  - Utility classes overriding shipped components. Extend via the `/live-tokens/components` editor instead.
34
51
  - Deep imports from `node_modules/@motion-proto/live-tokens/src/...`. Use public entry points only.
35
52
  - Mounting `Editor` or `ComponentEditorPage` outside their dedicated routes.
53
+ - A page route under `/live-tokens/*`. That namespace is reserved for the package's own dev surfaces so they can never shadow your routes; the rest of the URL space is yours.
36
54
 
37
55
  ## Verify
38
56
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: live-tokens-create-component
3
- description: Author a brand-new editable component for a @motion-proto/live-tokens project when nothing in the shipped catalogue fits. Covers the runtime Svelte file with :global(:root) tokens, the editor Svelte file with allTokens + VariantGroup, the registerComponent() call, naming conventions, state model, public-imports rule, and verification. Use when the user asks to author / create / build / extend a new tokenized component, make an existing Svelte component editable in the live-tokens editor, add a new component to the catalogue, register a custom component with the editor, or build a [Thing] component that does not exist in the shipped set. Not for placing an existing shipped component (Button, Card, etc.) on a page (see live-tokens-build-page); read live-tokens-pick-component first to confirm nothing in the catalogue fits.
3
+ description: Author a brand-new editable component for a @motion-proto/live-tokens project when nothing in the shipped catalogue fits runtime and editor Svelte files, registration, naming, state model, and verification. Use when the user asks to author, create, or build a new tokenized component; make an existing Svelte component editable in the live-tokens editor; add a component to the catalogue; register a custom component with the editor; or build a [Thing] component that does not exist in the shipped set. Not for placing an existing shipped component on a page (see live-tokens-build-page); read live-tokens-pick-component first to confirm nothing in the catalogue fits.
4
4
  ---
5
5
 
6
6
  # Authoring a component for a live-tokens project
@@ -43,80 +43,48 @@ For pattern reference, read any shipped component's source directly from the con
43
43
  });
44
44
  ```
45
45
  The schema side-effect happens inside `registerComponent` (which `bootLiveTokens` calls for you), so you don't call `registerComponentSchema` separately. **Do not place a standalone `registerComponent(...)` *before* `bootLiveTokens`** — that registers before the editor's init hooks run, which is the wrong window and can leave editor changes disconnected from the live page. Only call `registerComponent` directly if your app mounts manually (no `bootLiveTokens`), in which case call it before `mount(App, ...)`.
46
- 4. **Tell the picker** — open `.claude/skills/live-tokens-pick-component/SKILL.md` and add your new component to the **Catalogue** line under the family it belongs to (Action / Input / Selection / Containers / Messaging / Display). If it's confusable with an existing component (a second selection control, a competing container), add a row to that family's decision table explaining the use-case it owns. Without this step, the component exists but [[live-tokens-pick-component]] can't recommend it when a user asks "which component should I use?" — the same rule applies whether the component is first-party (update the picker shipped in this package) or consumer-authored (update the local copy at `.claude/skills/live-tokens-pick-component/SKILL.md` that `setup-claude` placed in your project).
46
+ 4. **Tell the picker** — open `.claude/skills/live-tokens-pick-component/SKILL.md` and add your new component to the **Catalogue** line under the family it belongs to (Action / Input / Selection / Containers / Messaging / Display). If it's confusable with an existing component (a second selection control, a competing container), add a row to that family's decision table explaining the use-case it owns. Without this step, the component exists but **live-tokens-pick-component** can't recommend it when a user asks "which component should I use?" — the same rule applies whether the component is first-party (update the picker shipped in this package) or consumer-authored (update the local copy at `.claude/skills/live-tokens-pick-component/SKILL.md` that `setup-claude` placed in your project).
47
47
  5. **Join the sketch layer** — the effect draws a fixed set of parts, so a new
48
48
  component stays crisp while the page around it goes hand-drawn until it opts
49
49
  in. A consumer component carries one of four reserved classes on its root and
50
- names the five `--sketch-*` colours it is drawn with; a first-party component
50
+ names the five `--sketch-*` values it is drawn with; a first-party component
51
51
  adds a `PartSpec` row instead. The layer also takes `background`,
52
52
  `border-color`, `box-shadow`, `overflow`, `position` and both pseudo-elements
53
53
  away from the element it draws, which constrains where the class can go. Read
54
54
  `references/sketch-mode.md`.
55
- 6. **Verify** with the checklist at the bottom of this file.
55
+ 6. **Verify** with the checklist at the bottom of this file, then place the component on a page with **live-tokens-build-page**.
56
56
 
57
57
  ## Token discipline
58
58
 
59
59
  ### Naming scheme
60
60
 
61
61
  ```
62
- --<componentId>-<part>[-<state>][-<element>]-<property>
62
+ --<componentId>-<part|variant>[-<state>][-<element>]-<property>
63
63
  ```
64
64
 
65
65
  - `componentId` — the literal id passed to `registerComponent()`. Lowercase, no dashes, no abbreviations (`segmentedcontrol` not `sc`). The file id matches: `MyWidget.svelte` → id `mywidget`.
66
- - `part` — sub-region (`bar`, `option`, `track`, `header`, `body`, `footer`, `overlay`, `value`, `label`).
66
+ - `part` or `variant` the sub-region (`bar`, `option`, `track`, `header`, `body`, `footer`, `overlay`, `value`, `label`), or, on a component whose variants differ in more than one property, the variant name: `--badge-accent-surface`, `--callout-danger-border`. A component with both stacks them outer to inner, so the bar in its small size is `--segmentedcontrol-bar-small-padding`.
67
67
  - `state` (optional) — interaction or component state (`hover`, `disabled`, `selected`, `focus`). **Always before the property.**
68
68
  - `element` (optional) — sub-element inside the part (`dot`, `icon`, `label`, `text`).
69
69
  - `property` — theme role or CSS property. Always last.
70
70
 
71
71
  ### Suffix vocabulary
72
72
 
73
- The editor picker is chosen by suffix. There is no per-token override; if a token renders with the wrong picker, rename it to one of these suffixes.
74
-
75
- **Color and surface**
76
-
77
- | Suffix | Meaning |
78
- |-------------|---------------------------------------------------------------|
79
- | `-surface` | Fill / background color |
80
- | `-border` | Border color |
81
- | `-text` | Text color |
82
- | `-icon` | Icon color |
83
- | `-label` | Label text color |
84
- | `-fill` | Inner fill (distinct from outer surface) |
85
- | `-divider` | Divider / separator color |
86
- | `-color` | Generic color, when none of the above name the role |
87
- | `-shadow` | Box-shadow |
88
- | `-opacity` | Opacity (0–1) |
89
- | `-blur` | Backdrop or filter blur radius |
90
-
91
- **Geometry**
92
-
93
- | Suffix | Meaning |
94
- |-----------------|---------------------------------------------------------------|
95
- | `-radius` | Corner radius |
96
- | `-border-width` | Stroke thickness (used even when CSS uses `outline:`) |
97
- | `-thickness` | Alternative to `-width` when fallback siblings would collide |
98
- | `-width` | Width dimension |
99
- | `-size` | Square / uniform dimension |
100
- | `-padding` | Internal spacing |
101
- | `-gap` | Spacing between sibling elements |
102
-
103
- **Typography**
104
-
105
- | Suffix | Meaning |
106
- |--------------------|--------------------------|
107
- | `-font-family` | Font family reference |
108
- | `-font-weight` | Font weight reference |
109
- | `-font-size` | Font size reference |
110
- | `-line-height` | Line height |
111
- | `-letter-spacing` | Letter spacing |
112
-
113
- The authoritative recognised list lives in `bin/check-component.mjs` (`KNOWN_SUFFIXES`). If you need a suffix that isn't listed, either rename to one that is, or open an issue against `@motion-proto/live-tokens` to add it. Don't invent suffixes; the editor falls back to a plain text input and your token won't get a real picker.
73
+ The editor picker is chosen by the token's suffix, so the suffix is the naming
74
+ decision that matters. Color and surface: `-surface`, `-border`, `-text`,
75
+ `-icon`, `-label`, `-fill`, `-divider`, `-color`, `-shadow`, `-opacity`,
76
+ `-blur`. Geometry: `-radius`, `-border-width`, `-thickness`, `-width`, `-size`,
77
+ `-padding`, `-gap`. Typography: `-font-family`, `-font-weight`, `-font-size`,
78
+ `-line-height`, `-letter-spacing`.
79
+
80
+ Read `references/token-naming.md` for what each one means and when two of them
81
+ compete. A suffix outside that list fails `check-component`.
114
82
 
115
83
  ### Rules that bite
116
84
 
117
85
  - **Fixed overlays must portal to `<body>`.** Any `position: fixed` layer is trapped by a transformed or `contain`ed ancestor, which real pages and the editor's preview pane both have. `check:overlay-portal` fails the build without it. Read `references/fixed-overlays.md` before authoring a modal, lightbox, or backdrop.
118
86
  - **State before property.** `--mywidget-button-hover-surface` ✓ — `--mywidget-button-surface-hover` ✗ (breaks sibling matching).
119
- - **Defaults reference theme tokens, never raw values.** `var(--surface-primary)` ✓ — `#6a4ce8` ✗.
87
+ - **Defaults reference theme tokens, never raw colours.** `var(--surface-primary)` ✓ — `#6a4ce8` ✗, and `check-component` fails the build on one. Composing a token counts and is common: `color-mix(in srgb, var(--surface-neutral-lower) 70%, transparent)`. A property with no theme token behind it takes a bare keyword (`contain`, `start`, `none`). Dimensions are the gap: a raw `16rem` passes the check, so reach for a token anyway unless the component genuinely owns that measurement.
120
88
  - **No abbreviations.** `bg` → `surface`; `fg` → `text`; component ids are never abbreviated.
121
89
  - **Text aliases.** Neutral scale is `--text-primary` / `--text-secondary` / `--text-tertiary` / `--text-muted` / `--text-disabled`. Family-tinted is `--text-primary-color`, `--text-accent`, `--text-success`. There is no `--text-neutral`.
122
90
  - **Typography `groupKey` on multi-slot components must include the slot prefix.** `groupKey: 'value-font-family'` and `groupKey: 'label-font-family'` ✓ — bare `groupKey: 'font-family'` silently merges them into one link tree ✗. Single-slot components can use a bare typography `groupKey`; add the slot prefix the moment a second slot appears. The same trap applies to type-group **colors** (two slots ending in `-text` collapsing to one `text` key). Let the helpers handle both, below.
@@ -1,3 +1,12 @@
1
1
  # Fixed overlays must portal to body
2
2
 
3
- Any `position: fixed` layer (modal, lightbox, full-screen backdrop) is trapped clipped or painted under other chrome by a transformed / `isolation` / `contain` / `will-change` ancestor, which real consumer pages (and the editor's own preview pane) commonly have. Render the fixed layer with `use:portal` from `src/system/internal/portal.ts` so it escapes to `<body>`; pass `use:portal={enabled}` to keep an in-flow preview variant in place (see `Dialog`). `check:overlay-portal` fails the build if a component has `position: fixed` without it. Anchored popovers (`position: absolute` relative to a trigger, like `Tooltip`) are exempt. Two consequences of moving to `<body>`: DOM events from the layer no longer bubble to a consumer ancestor (use component callbacks, as `Dialog` does), and a subtree-scoped CSS-variable theme no longer reaches it (this library themes via `:root`, so fine here). If the layer is a modal, also give it `role="dialog"` + `aria-modal`, move focus in on open and restore it on close, and trap `Tab` (see `ImageLightbox`).
3
+ Any `position: fixed` layer (modal, lightbox, full-screen backdrop) is trapped, clipped or painted under other chrome, by a transformed / `isolation` / `contain` / `will-change` ancestor. Real consumer pages and the editor's own preview pane both have one, so this is the normal case rather than the edge.
4
+
5
+ Render the layer with `use:portal` from `src/system/internal/portal.ts` so it escapes to `<body>`. `use:portal={enabled}` keeps an in-flow preview variant where it is; `Dialog` is the worked example. `check:overlay-portal` fails the build when a component sets `position: fixed` without it. Anchored popovers are exempt: `Tooltip` is `position: absolute` against its trigger and belongs in the flow.
6
+
7
+ Moving to `<body>` costs two things:
8
+
9
+ - DOM events from the layer no longer bubble to a consumer ancestor, so pass component callbacks the way `Dialog` does.
10
+ - A subtree-scoped CSS-variable theme no longer reaches it. This library themes via `:root`, so nothing breaks here.
11
+
12
+ A modal also needs `role="dialog"` with `aria-modal`, focus moved in on open and restored on close, and `Tab` trapped inside. `ImageLightbox` is the worked example.
@@ -45,13 +45,15 @@ Declare intrinsics so the editor and the contract test stay honest:
45
45
 
46
46
  Writes go through `setComponentAlias(component, spec.variable(v), { kind: 'literal', value })` so the choice cascades to `:root` like any token.
47
47
 
48
- 4. **Pass `intrinsics` to `registerComponent`** so the contract test can see it:
48
+ 4. **Put `intrinsics` on the registry entry** so the contract test can see it. That is the same entry the recipe passes to `bootLiveTokens`, with one more field:
49
49
 
50
50
  ```ts
51
- registerComponent({
52
- id: 'mywidget',
53
- // ...label, icon, sourceFile, editorComponent, schema...
54
- intrinsics: myWidgetIntrinsics,
51
+ bootLiveTokens(App, '#app', {
52
+ components: [{
53
+ id: 'mywidget',
54
+ // ...label, icon, sourceFile, editorComponent, schema...
55
+ intrinsics: myWidgetIntrinsics,
56
+ }],
55
57
  });
56
58
  ```
57
59
 
@@ -190,7 +190,7 @@ against the compiled `<style>` block.
190
190
 
191
191
  ## Verify
192
192
 
193
- Switch Sketch mode on from the editor's **Sketch Style** view, then check the
193
+ Switch Sketch mode on from the editor's **Sketchstyle** view, then check the
194
194
  component in place:
195
195
 
196
196
  - [ ] Drawn, not crisp, in every variant.
@@ -0,0 +1,50 @@
1
+ # Suffix vocabulary
2
+
3
+ The editor picker is chosen by suffix. There is no per-token override; if a
4
+ token renders with the wrong picker, rename it to one of these.
5
+
6
+ `KNOWN_SUFFIXES` in `bin/check-component.mjs` is authoritative, and
7
+ `check-component` fails on a suffix outside it. `check:skills` holds this file
8
+ to that list, so the two cannot drift apart.
9
+
10
+ ## Color and surface
11
+
12
+ | Suffix | Meaning |
13
+ |-------------|---------------------------------------------------------------|
14
+ | `-surface` | Fill / background color |
15
+ | `-border` | Border color |
16
+ | `-text` | Text color |
17
+ | `-icon` | Icon color |
18
+ | `-label` | Label text color |
19
+ | `-fill` | Inner fill (distinct from outer surface) |
20
+ | `-divider` | Divider / separator color |
21
+ | `-color` | Generic color, when none of the above name the role |
22
+ | `-shadow` | Box-shadow |
23
+ | `-opacity` | Opacity (0–1) |
24
+ | `-blur` | Backdrop or filter blur radius |
25
+
26
+ ## Geometry
27
+
28
+ | Suffix | Meaning |
29
+ |-----------------|---------------------------------------------------------------|
30
+ | `-radius` | Corner radius |
31
+ | `-border-width` | Stroke thickness (used even when CSS uses `outline:`) |
32
+ | `-thickness` | Alternative to `-width` when fallback siblings would collide |
33
+ | `-width` | Width dimension |
34
+ | `-size` | Square / uniform dimension |
35
+ | `-padding` | Internal spacing |
36
+ | `-gap` | Spacing between sibling elements |
37
+
38
+ ## Typography
39
+
40
+ | Suffix | Meaning |
41
+ |--------------------|--------------------------|
42
+ | `-font-family` | Font family reference |
43
+ | `-font-weight` | Font weight reference |
44
+ | `-font-size` | Font size reference |
45
+ | `-line-height` | Line height |
46
+ | `-letter-spacing` | Letter spacing |
47
+
48
+ A suffix you need that is not here is either a rename away from one that is, or
49
+ an issue against `@motion-proto/live-tokens`. Inventing one costs the token its
50
+ picker: the editor falls back to a plain text input.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: live-tokens-generate-theme
3
- description: Generate a complete live-tokens theme (color, type, and geometry) from a natural-language brief. Chooses 10 OKLCH seeds and runs the packaged generator, which enforces AA contrast, then carries the same brief into a font pairing and a geometry through the sibling skills. Use whenever the user asks for a theme, a look, a vibe, a brand feel, a color scheme, or a palette, by mood, style, era, season, holiday, or hue, even if they only mention color: make me a bright and cheerful theme, a dark moody night theme, a brutalist look, mid-century modern, something Swiss and minimal, make it feel like a terminal, cyberpunk neon, an editorial magazine feel, a St. Patrick's Day theme in green and gold, something red-based, warmer, more contrast, calmer. Not for a single token (use the editor), for type alone (live-tokens-pair-fonts), or for geometry alone (live-tokens-adjust-geometry).
3
+ description: Generate a complete live-tokens theme color, type, and geometry from a natural-language brief, delegating the type and geometry halves to its sibling skills. Use whenever the user asks for a theme, look, vibe, brand feel, color scheme, or palette by mood, style, era, season, holiday, or hue; when they name only a color (something red-based, green and gold for St. Patrick's Day); or when they refine an existing theme (warmer, more contrast, calmer). Examples: bright and cheerful, dark and moody, brutalist, mid-century modern, Swiss and minimal, cyberpunk neon, editorial magazine, make it feel like a terminal. Not for a single token (use the editor), type alone (live-tokens-pair-fonts), or geometry alone (live-tokens-adjust-geometry).
4
4
  ---
5
5
 
6
6
  # Generating a theme from a brief
@@ -9,16 +9,17 @@ A theme is three decisions made from one brief: color, type, and geometry. This
9
9
 
10
10
  ## Workflow
11
11
 
12
- 1. Read the brief once and name its voice in a sentence: the mood, the hue family, the scheme, and the type and geometry that mood implies. Everything below keys off that sentence. Then read the anchor reference that matches the brief (feeling, idiom, or occasion; see below) before seeding: each entry fixes all three decisions together and overrides the generic defaults here.
13
- 2. Translate the brief into a seed file using the framework below. Write it to `scratch/theme-brief.json`.
14
- 3. Run `npx live-tokens generate-theme scratch/theme-brief.json`. It writes `themes/<slug>.json`, opens that theme, and prints a contrast report. Auto-corrections are fine. Unmet floors (exit 1) mean the seeds themselves are unworkable; each failure line names the seed to change, usually by raising its lightness or cutting its chroma. Fix the brief and re-run; the same name overwrites. Regeneration replaces that theme's whole color state, including palette edits made in the editor since the last run, so say so once when iterating.
15
- 4. Invoke **live-tokens-pair-fonts** with the same voice. Skip only when the user asked for colors specifically and said to leave the type alone.
16
- 5. Invoke **live-tokens-adjust-geometry** with the geometry the voice implies (table below). Skip when the voice implies nothing about geometry.
17
- 6. Tell the user to look at the running app, and that type and geometry sit in the unsaved buffer until they save the open theme. Offer refinements as edits to the same brief.
12
+ 1. Read the brief once and name its voice in a sentence: the mood, the hue family, the scheme, and the type and geometry that mood implies. Everything below keys off that sentence.
13
+ 2. Read the anchor reference that matches the voice (feeling, idiom, or occasion; see Anchor references) before seeding. Each entry fixes all three decisions together and overrides the generic defaults here.
14
+ 3. Translate the brief into a seed file using the framework below. Write it to `scratch/<slug>-brief.json`. Nothing else records the seeds, so this file is the only copy; one per slug is what makes the refinement pass below cheap.
15
+ 4. Run `npx live-tokens generate-theme scratch/<slug>-brief.json`. It writes `themes/<slug>.json`, opens that theme, and prints a contrast report. Auto-corrections are fine. Unmet floors (exit 1) mean the seeds themselves are unworkable; each failure line names the seed to change, usually by raising its lightness or cutting its chroma. Fix the brief and re-run; the same name overwrites. Regeneration replaces that theme's whole color state, including palette edits made in the editor since the last run, so say so once when iterating.
16
+ 5. Invoke **live-tokens-pair-fonts** with the same voice. Skip only when the user asked for colors specifically and said to leave the type alone.
17
+ 6. Invoke **live-tokens-adjust-geometry** with the geometry the voice implies (table below). Skip when the voice implies nothing about geometry.
18
+ 7. Tell the user to look at the running app, and that type and geometry sit in the unsaved buffer until they save the open theme. Offer refinements as edits to the same brief (see Refining a theme that exists).
18
19
 
19
20
  Order matters only for safety, and the order above is safe: the color generator carries the live buffers forward into the new theme file, so a color re-roll after fonts and geometry keeps both.
20
21
 
21
- Flags: `--dry-run` prints the report without writing; `--no-activate` writes without opening. Opening a theme never changes what the site ships. Only Adopt, in the editor, does that.
22
+ Flags: `--dry-run` prints the report without writing; `--no-activate` writes without opening; `--carry-from <theme>` takes the non-color content (gradients, fonts, component aliases) from a named theme rather than from the live look. Generating a set needs it: the first run becomes the live look, so a second run without it carries the first theme's fonts and geometry into the second. Opening a theme never changes what the site ships. Only Adopt, in the editor, does that.
22
23
 
23
24
  ## The brief
24
25
 
@@ -123,7 +124,7 @@ Read the matching reference before seeding, and apply the bands above on top of
123
124
  - `references/style-vocabulary.md` covers named idioms, eras, and genres: Swiss, Bauhaus, mid-century, art deco, terminal, cyberpunk, vaporwave, Y2K, blueprint, Scandinavian, Japandi, cottagecore, editorial, newsprint, riso, corporate, brutalist, Memphis, industrial. Each entry fixes color, type, and geometry as one set, so hand its Type and Geometry columns to the sibling skills verbatim.
124
125
  - `references/named-themes.md` covers holidays, seasons, and natural scenes: Christmas, Halloween, St. Patrick's, Ocean, Sunset, Autumn, Spring. A holiday or season brief is a statement brief: commitment level 2 or 3, with the named color on the ground rather than only on the buttons.
125
126
 
126
- Most briefs hit the first file. A brief that names no feeling, idiom, or occasion at all takes the bands above and the geometry table below.
127
+ Most briefs hit the first file. A brief that matches two ("cozy brutalist", "clinical Swiss") reads the idiom first and lets the feeling move the dials inside it: an idiom sets constraints, and dials move within constraints. A brief that names no feeling, idiom, or occasion at all takes the bands above and the geometry table below.
127
128
 
128
129
  ## Geometry from the voice
129
130
 
@@ -138,6 +139,12 @@ The geometry lives in radius, padding, gap, and border width, and `live-tokens-a
138
139
 
139
140
  This table is the fallback. When the brief matched an entry in the mood or style reference, take the geometry from that entry instead: it is tuned to the same reading the color came from, and a style's geometry is often targeted rather than global.
140
141
 
142
+ ## Refining a theme that exists
143
+
144
+ "Warmer", "calmer", "more contrast" arrive against a theme that is already open, and the answer is a new brief rather than hand-edits. Edit `scratch/<slug>-brief.json` when it is still there. When it is not, recover the seeds: `src/live-tokens/data/themes/<slug>.json` holds each one verbatim at `colorsAndType.editorConfigs.<Palette>.baseColor` as `{l, c, h}`, and the Canvas seed's lightness tells you the scheme. Rebuild the brief from those ten values, move the dial the user named, re-run under the same name.
145
+
146
+ One adjective moves one dial. Warmer and cooler rotate hue; calmer and louder move chroma; lighter, darker, and moodier move Canvas L and the scheme; more contrast widens the L gap between Canvas and Brand and takes chroma out of the ground rather than adding it to the garnish. Leave every seed the user did not name alone, because a refinement that re-rolls the whole palette reads as a different theme and loses the thing they liked.
147
+
141
148
  ## What each step writes
142
149
 
143
150
  Color writes `themes/<slug>.json` and opens it. Type and geometry write the unsaved buffers, which the page already runs. One Save in the editor keeps all three; Adopt ships them. Component aliases and gradients carry forward from the live look into a generated theme; user-tuned gradients survive, stock ones rebuild from the new families.
@@ -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, --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).
3
+ description: Choose and apply a Google Fonts pairing for a live-tokens theme, binding families to the shipped --font-* stacks. Use whenever the user asks to pair fonts, pick a typeface, change or set the fonts, or describes type by voice: 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
@@ -11,8 +11,8 @@ You choose the families; the CLI verifies each against Google Fonts, builds the
11
11
 
12
12
  1. Choose the pairing with the framework below and write a brief to `scratch/font-brief.json`.
13
13
  2. Run `npx live-tokens set-fonts scratch/font-brief.json`. It prints each stack that moved, each family's real weights and URL, and the weights your typography tokens ask for that the family lacks.
14
- 3. Read the report. A weight gap is a quality note: name it and offer an alternative only if it matters (a body face without 400 or 700 matters; a display face without 300 does not). A family not on Google Fonts fails the run; fix the spelling and re-run.
15
- 4. Tell the user to reload and look, and that the edit is unsaved until they save the open theme.
14
+ 3. Read the report. A weight gap is a quality note: name it and offer an alternative only if it matters (a body face without 400, 700, or italic matters; a display face without 300 does not). A family not on Google Fonts fails the run; fix the spelling and re-run.
15
+ 4. Tell the user to reload the editor page before saving. A running editor holds its own copy of the buffer this CLI just wrote and never re-reads it, so a Save without a reload writes the stale copy back and the pairing vanishes with a success report still on screen. After the reload the type is on the page, and unsaved until they save the open theme.
16
16
 
17
17
  State your reasoning when you propose the pairing: each face's form model and the matrix verdict, in one sentence, so the user can argue with the argument rather than only the result.
18
18
 
@@ -24,12 +24,14 @@ 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`, `mono` and `editorial` exist when a theme needs them. `editorial` is `--font-editorial`, the long-reading face behind the `--editorial-md-*` and `--editorial-sm-*` text styles: 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.
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 styles: 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
 
31
31
  The body face is the anchor. It carries most of the words, and text faces survive small sizes where display faces do not. Pick it against the brief, then pick the display face against it. A body face must have regular, bold, and italic; low to moderate stroke contrast; open apertures; and a large x-height. A face failing any of these is a display face whatever its name says. Single-weight families are fine for `display` and disqualifying for `body`.
32
32
 
33
+ The shipped text styles ask the display face for 600, across all four heading levels, and the body face for 400; prose markup adds 700 and italic for `strong` and `em`. Screen candidates against those four before running, so the report confirms a decision instead of reporting a surprise.
34
+
33
35
  ## The font matrix: the decision rule
34
36
 
35
37
  Classify each candidate on two layers. The **skeleton** is its form model; the **flesh** is its stroke contrast and serif treatment.
@@ -64,7 +66,7 @@ Match the type to the same brief the color came from. A warm autumn palette unde
64
66
 
65
67
  These find an adequate pairing fast and skip the reasoning; use them when the brief is vague or the type should stay quiet.
66
68
 
67
- - **A superfamily.** Google Fonts families with both sans and serif siblings: Alegreya, Ancizar, IBM Plex, Inria, Merriweather, Noto, PT, Roboto, Source.
69
+ - **A superfamily.** Google Fonts families with both sans and serif siblings, among them Alegreya, Ancizar, IBM Plex, Inria, Merriweather, Noto, PT, Roboto, Source. The catalogue moves and this list does not, so treat it as a starting set: `set-fonts` verifies every family against the API and fails loudly on one that is gone.
68
70
  - **One family across weights.**
69
71
  - **Same designer or foundry.**
70
72
  - **Serif display over sans body** when nothing else decides it.
@@ -78,7 +80,7 @@ These find an adequate pairing fast and skip the reasoning; use them when the br
78
80
 
79
81
  ## Scope
80
82
 
81
- Type only. Color, component aliases, shape, and the type scale are untouched: `set-fonts` moves families between stacks and nothing else, writing only the unsaved colors-and-type buffer. Save the theme to keep it, Adopt to ship it.
83
+ Type only. Color, component aliases, shape, and the type scale are untouched: `set-fonts` moves families between stacks and nothing else, writing only the unsaved colors-and-type buffer. Save the theme to keep it, Adopt to ship it. Adopt is also what rewrites `fonts.css`, which is how a build with no editor in it loads the family at all.
82
84
 
83
85
  ## Verify
84
86
 
@@ -1,20 +1,18 @@
1
1
  ---
2
2
  name: live-tokens-pick-component
3
- description: Recommend which shipped @motion-proto/live-tokens component fits a UX need, with decision trees for the confusable pairs (SegmentedControl vs TabBar vs RadioButton vs MenuSelect; Card vs CollapsibleSection vs Dialog; Callout vs Notification vs Tooltip; Badge vs CornerBadge; Toggle vs SegmentedControl vs RadioButton). Use when the user asks which / what component to use, should I use X or Y, what is the difference between two components, how do I show / let the user / capture / display some UX outcome, or starts to author a custom component before checking the catalogue. Read this before reaching for live-tokens-create-component. Not for actually placing the chosen component on a page (see live-tokens-build-page).
3
+ description: Recommend which shipped @motion-proto/live-tokens component fits a UX need, with decision trees for the confusable pairs (SegmentedControl / TabBar / RadioButton / MenuSelect, Card / CollapsibleSection / Dialog, Callout / Notification / Tooltip, and others). Use when the user asks which component to use, should I use X or Y, what is the difference between two components, how do I show / let the user / capture some UX outcome, or starts authoring a custom component before checking the catalogue. Read this before live-tokens-create-component. Not for placing the chosen component on a page (see live-tokens-build-page).
4
4
  ---
5
5
 
6
6
  # Picking the right live-tokens component
7
7
 
8
8
  This skill helps you choose between shipped components when several could plausibly fit. The catalogue is small; the hard part is semantic intent. A `RadioButton` set and a `SegmentedControl` can render identical-looking UIs but communicate different things.
9
9
 
10
- For composing a page once you've picked components, see [[live-tokens-build-page]]. For authoring a brand-new component when nothing fits, see [[live-tokens-create-component]] (but read this skill first to confirm nothing in the catalogue fits).
10
+ For composing a page once you've picked components, see **live-tokens-build-page**. For authoring a brand-new component when nothing fits, see **live-tokens-create-component** (but read this skill first to confirm nothing in the catalogue fits).
11
11
 
12
12
  ## Catalogue
13
13
 
14
14
  Action: `Button`, `IconButton`, `InlineEditActions`. Input: `Input`. Selection: `SegmentedControl`, `TabBar`, `RadioButton`, `MenuSelect`, `Toggle`. Containers: `Card`, `CollapsibleSection`, `Dialog`, `Panel`. Messaging: `Callout`, `Notification`, `Tooltip`, `Badge`, `CornerBadge`. Display: `Table`, `Image`, `ImageLightbox`, `ProgressBar`, `SectionDivider`, `SideNavigation`, `CodeSnippet`.
15
15
 
16
- `CodeSnippet` is for a single-line command or value the user is meant to copy and paste back into a terminal (install commands, generated keys, ids). Click-to-copy with a brief "Copied" popover. Use it whenever your page asks the reader to *run* something, rather than just *read* it.
17
-
18
16
  ## Action family: Button vs IconButton
19
17
 
20
18
  Both trigger an action and share the same six variants (primary, secondary, outline, success, danger, warning), three states (default, hover, disabled) and two sizes (default, small). They differ only in content.
@@ -40,6 +38,13 @@ All four pick one option from a set. The right one depends on **option count**,
40
38
  - Use `MenuSelect` when options would overflow horizontally or there are too many to display at once.
41
39
  - **Don't pick `SegmentedControl` when option labels are long enough to wrap.** It loses its compactness; use `RadioButton` rows instead.
42
40
 
41
+ ## Text entry: Input vs the selection family
42
+
43
+ - `Input` takes an answer the page cannot enumerate: a name, an email, a search string, an amount. It ships the label, the hint line, and the error state as parts (`--input-label-*`, `--input-hint-*`, `--input-error-*`), so style those rather than stacking your own text under a bare field.
44
+ - The boundary is whether you can list the answers. A short fixed set is the single-selection family above; a long fixed set is `MenuSelect`; anything you cannot write down is `Input`.
45
+ - **Don't use it for on/off.** That is `Toggle`, and a one-field form asking for yes or no is the usual way this goes wrong.
46
+ - Its four variants are `default`, `focused`, `disabled`, and `error`. A validation message belongs in the `error` variant, not in a `Callout` next to the field.
47
+
43
48
  ## Container family: Card vs CollapsibleSection vs Dialog
44
49
 
45
50
  | Component | Modality | Use for |
@@ -69,6 +74,15 @@ All four pick one option from a set. The right one depends on **option count**,
69
74
  - `Tooltip` is for *what an element means*. **Don't use `Tooltip` as the primary location of important content;** it auto-dismisses and isn't accessible for must-read content.
70
75
  - `Badge` and `CornerBadge` differ only in positioning. `CornerBadge` lives at a `top-right` / `bottom-left` anchor on a parent (notification counts, "NEW" stickers).
71
76
 
77
+ ## Display family: what the page shows rather than what it asks
78
+
79
+ - `Image` frames a picture in the flow at one of four sizes, with an optional hover zoom. It is the default for any picture the page simply shows.
80
+ - `ImageLightbox` adds click-to-open at full size and takes an array for a gallery. Use it when the detail is the point (screenshots, artwork, charts that need reading), and not for decoration: it puts a modal behind every picture it wraps.
81
+ - `Table` themes your own rows and cells without owning the data. Records go here; a set of *things the user acts on* is a stack of `Card`s instead.
82
+ - `ProgressBar` reports progress against a labelled track. It is a read-out, never a control.
83
+ - `CodeSnippet` is for a single-line command or value the reader is meant to copy back into a terminal (install commands, generated keys, ids), with click-to-copy and a brief "Copied" popover. Use it whenever the page asks the reader to *run* something rather than just *read* it.
84
+ - `SectionDivider` separates sections of one page. `SideNavigation` moves between pages, driven by the current path. **Don't use `SideNavigation` to switch panels inside one page;** that is `TabBar`, and the difference is whether the URL changes.
85
+
72
86
  ## Toggle vs SegmentedControl vs RadioButton (for on/off)
73
87
 
74
88
  All three can express a binary choice. The right one depends on what the choice *is*.
@@ -85,4 +99,4 @@ All three can express a binary choice. The right one depends on what the choice
85
99
 
86
100
  ---
87
101
 
88
- If nothing in the catalogue fits (a `Slider`, a `DatePicker`, a `Stepper`, a custom widget), author it via [[live-tokens-create-component]]. **Don't reach for a custom component before checking the catalogue;** a custom component is a maintenance commitment.
102
+ If nothing in the catalogue fits (a `Slider`, a `DatePicker`, a `Stepper`, a custom widget), author it via **live-tokens-create-component**. **Don't reach for a custom component before checking the catalogue;** a custom component is a maintenance commitment.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.63.0 — A theme carries its sketchstyle
4
+
5
+ ### Added
6
+
7
+ - **The editorial role reaches above the reading size.** It shipped as a pair,
8
+ so a lede, a standfirst or a pull quote had nowhere to go but a heading style:
9
+ the display face, at heading leading. `--editorial-lg-*` and `--editorial-xl-*`
10
+ are the two steps above `md`, set in the same editorial face, editable in the
11
+ editor's Text Styles section and carried by an `.editorial-lg` /
12
+ `.editorial-xl` class in `site.css`. Leading follows the role's own rule: `md`
13
+ is the reading step and takes the most open leading, and every step away from
14
+ it tightens by one. Sizes stay inside the band the responsive scale leaves
15
+ constant, so an article does not resize itself between breakpoints. An
16
+ `additive` tokens.css migration (`2026-08-27-editorial-large-steps`) adds both
17
+ steps to a vendored `tokens.css`.
18
+
19
+ ### Changed
20
+
21
+ - **A theme carries its sketchstyle.** Save folds the live dials into the open
22
+ theme's `sketchStyle` field; Load applies whatever the theme carries, or
23
+ turns the effect off for a theme with none. A sketchstyle keeps its old job:
24
+ a reusable look you pick from, saved and loaded independently of the open
25
+ theme. Production is unchanged: Adopt still does not bake the layer, and a
26
+ built site still ships no sketch. No migration runs for the theme field
27
+ itself: a browser that already had sketch dials set reads them as off the
28
+ theme on its first boot after upgrading, and Save folds them into the open
29
+ theme like any other unsaved change.
30
+ - **The Theme Picker previews the sketch layer.** Picking a theme showed its
31
+ colors and type over whatever drawing the previously applied theme had left
32
+ painted, so two themes' looks were on screen at once, which is the one thing a
33
+ preview exists to prevent. A previewed theme now paints its own sketchstyle,
34
+ and one carrying none previews crisp whatever is live. Leaving the picker puts
35
+ back exactly what was there before, an unsaved dial included: the preview
36
+ paints without writing the live buffer, so browsing costs nothing and Cancel
37
+ reverts to the buffer rather than to a snapshot taken before it.
38
+ - **Sketchstyle, named as one thing.** "Sketch preset" is retired: the code,
39
+ the Theme panel row, the editor's fourth view and the on-disk directory all
40
+ say "sketchstyle" now. The saved-look directory moves from
41
+ `data/sketch-presets/` to `data/sketch-styles/`; `npx live-tokens migrate`
42
+ renames it (boot only warns on the old name, it does not refuse the tree).
43
+
3
44
  ## 0.62.0 — Editorial type reads as a pair
4
45
 
5
46
  ### Added
package/bin/migrate.mjs CHANGED
@@ -12,7 +12,7 @@
12
12
  // resolution, file IO, and reporting.
13
13
 
14
14
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
15
- import { dirname, join, relative, resolve } from 'node:path';
15
+ import { basename, dirname, join, relative, resolve } from 'node:path';
16
16
  import { fileURLToPath } from 'node:url';
17
17
 
18
18
  const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
@@ -123,7 +123,11 @@ export function formatMigrateDataResult(result) {
123
123
  const lines = [planned ? 'Data tree heal (planned):' : 'Data tree healed:'];
124
124
 
125
125
  for (const { from, to } of result.renames) {
126
- lines.push(` ${planned ? 'would move' : 'moved'} ${rel(from)} ${rel(to)} (0.48 layout)`);
126
+ // Two unrelated renames share this list: the pre-0.48 layout swap, and
127
+ // `sketch-presets/` → `sketch-styles/`. Told apart by the destination
128
+ // name, since `LegacyRename` carries no reason of its own.
129
+ const reason = basename(to) === 'sketch-styles' ? 'sketchstyle rename' : '0.48 layout';
130
+ lines.push(` ${planned ? 'would move' : 'moved'} ${rel(from)} → ${rel(to)} (${reason})`);
127
131
  }
128
132
  for (const p of result.upgradedThemes) {
129
133
  lines.push(` ${planned ? 'would carry' : 'carried'} ${rel(p)} by value (was a pre-v3 theme naming files)`);
@@ -270,7 +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
+ sketchStylesDir: sub("sketch-styles"),
274
274
  legacyManifestsDir: fileConfig.manifestsDir ? import_path.default.resolve(fileConfig.manifestsDir) : void 0
275
275
  };
276
276
  }
@@ -1,5 +1,5 @@
1
1
  import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-DSV3Zisf.cjs';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.cjs';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DZUzVv8H.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
1
  import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-DSV3Zisf.js';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.js';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DZUzVv8H.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-Y5CNFSSV.js";
7
+ } from "../chunk-RVE3MNKM.js";
8
8
 
9
9
  // src/editor/core/components/aliasKinds.ts
10
10
  var KIND_PATTERNS = [