@motion-proto/live-tokens 0.68.1 → 0.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.claude/skills/live-tokens-build-page/SKILL.md +16 -5
  2. package/.claude/skills/live-tokens-create-component/SKILL.md +55 -19
  3. package/.claude/skills/live-tokens-create-component/references/token-naming.md +30 -1
  4. package/.claude/skills/live-tokens-fix-findings/SKILL.md +133 -0
  5. package/.claude/skills/live-tokens-pick-component/SKILL.md +10 -1
  6. package/CHANGELOG.md +236 -0
  7. package/README.md +13 -4
  8. package/bin/check-component.mjs +367 -63
  9. package/bin/check-page.mjs +409 -0
  10. package/bin/cli.mjs +107 -9
  11. package/bin/lib/catalogue.mjs +123 -0
  12. package/bin/lib/cssValues.mjs +50 -0
  13. package/bin/lib/findings.mjs +106 -0
  14. package/bin/lib/tokenVocabulary.mjs +240 -0
  15. package/dist-plugin/adjust/index.cjs +174 -23
  16. package/dist-plugin/adjust/index.js +68 -23
  17. package/dist-plugin/{chunk-2UX6EVVA.js → chunk-2YNERPXY.js} +1 -1
  18. package/dist-plugin/{chunk-NE6N66EE.js → chunk-GPIBU44G.js} +107 -1
  19. package/dist-plugin/{chunk-ZHPX7ZYQ.js → chunk-RFVYPNRO.js} +39 -1
  20. package/dist-plugin/generateColorsAndType/index.cjs +107 -1
  21. package/dist-plugin/generateColorsAndType/index.js +1 -1
  22. package/dist-plugin/index.cjs +146 -2
  23. package/dist-plugin/index.js +3 -3
  24. package/dist-plugin/migrateData/index.cjs +107 -1
  25. package/dist-plugin/migrateData/index.js +2 -2
  26. package/dist-plugin/tokensCssMigrations/index.cjs +39 -1
  27. package/dist-plugin/tokensCssMigrations/index.js +1 -1
  28. package/package.json +3 -2
  29. package/src/app/site.css +4 -4
  30. package/src/editor/component-editor/ButtonEditor.svelte +71 -4
  31. package/src/editor/component-editor/CardEditor.svelte +6 -6
  32. package/src/editor/component-editor/DialogEditor.svelte +3 -3
  33. package/src/editor/component-editor/IconButtonEditor.svelte +68 -3
  34. package/src/editor/component-editor/ImageEditor.svelte +8 -8
  35. package/src/editor/component-editor/ImageLightboxEditor.svelte +12 -1
  36. package/src/editor/component-editor/MenuSelectEditor.svelte +63 -3
  37. package/src/editor/component-editor/SegmentedControlEditor.svelte +63 -3
  38. package/src/editor/component-editor/SideNavigationEditor.svelte +67 -3
  39. package/src/editor/component-editor/SliderEditor.svelte +186 -0
  40. package/src/editor/component-editor/TabBarEditor.svelte +63 -3
  41. package/src/editor/component-editor/registry.ts +10 -0
  42. package/src/editor/core/components/aliasKinds.ts +51 -28
  43. package/src/editor/core/sketch/sketchLayer.ts +16 -0
  44. package/src/editor/core/store/editorPersistence.ts +44 -1
  45. package/src/editor/core/store/editorRenderer.ts +2 -2
  46. package/src/editor/core/store/editorStore.ts +18 -18
  47. package/src/editor/core/store/editorTypes.ts +7 -6
  48. package/src/editor/core/themes/migrations/2026-09-01-gate-suffix-enabled.ts +31 -0
  49. package/src/editor/core/themes/migrations/2026-09-01-scrim-rename.ts +52 -0
  50. package/src/editor/core/themes/migrations/2026-09-01-tabbar-active-tint.ts +27 -0
  51. package/src/editor/core/themes/migrations/2026-09-01-tint-rename.ts +42 -0
  52. package/src/editor/core/themes/migrations/index.ts +16 -0
  53. package/src/editor/core/themes/slices/domainVars.ts +2 -2
  54. package/src/editor/core/themes/slices/washes.ts +107 -0
  55. package/src/editor/docs/content/editing-tokens.md +5 -3
  56. package/src/editor/docs/content.generated.ts +1 -1
  57. package/src/editor/index.ts +1 -1
  58. package/src/editor/pages/EditorShell.svelte +1 -1
  59. package/src/editor/ui/SurfacesTab.svelte +3 -3
  60. package/src/editor/ui/UITokenSelector.svelte +1 -0
  61. package/src/editor/ui/VariablesTab.svelte +2 -2
  62. package/src/editor/ui/sections/{OverlaysSection.svelte → WashesSection.svelte} +44 -43
  63. package/src/live-tokens/data/colors-and-type/autumn.json +6 -6
  64. package/src/live-tokens/data/colors-and-type/default.json +6 -6
  65. package/src/live-tokens/data/colors-and-type/halloween.json +6 -6
  66. package/src/live-tokens/data/colors-and-type/midnight-study.json +6 -6
  67. package/src/live-tokens/data/colors-and-type/ocean.json +6 -6
  68. package/src/live-tokens/data/colors-and-type/royal-velvet.json +6 -6
  69. package/src/live-tokens/data/colors-and-type/sketchy.json +6 -6
  70. package/src/live-tokens/data/colors-and-type/spring-meadow.json +6 -6
  71. package/src/live-tokens/data/colors-and-type/sunset.json +6 -6
  72. package/src/live-tokens/data/themes/autumn.json +81 -13
  73. package/src/live-tokens/data/themes/halloween.json +81 -13
  74. package/src/live-tokens/data/themes/midnight-study.json +81 -13
  75. package/src/live-tokens/data/themes/ocean.json +81 -13
  76. package/src/live-tokens/data/themes/royal-velvet.json +81 -13
  77. package/src/live-tokens/data/themes/sketchy.json +81 -13
  78. package/src/live-tokens/data/themes/spring-meadow.json +81 -13
  79. package/src/live-tokens/data/themes/sunset.json +81 -13
  80. package/src/live-tokens/data/tokens.generated.css +6 -6
  81. package/src/system/components/Button.svelte +28 -10
  82. package/src/system/components/Card.svelte +6 -6
  83. package/src/system/components/Dialog.svelte +3 -3
  84. package/src/system/components/IconButton.svelte +23 -7
  85. package/src/system/components/Image.svelte +6 -6
  86. package/src/system/components/MenuSelect.svelte +17 -1
  87. package/src/system/components/SegmentedControl.svelte +20 -2
  88. package/src/system/components/SideNavigation.svelte +22 -2
  89. package/src/system/components/Slider.svelte +348 -0
  90. package/src/system/components/TabBar.svelte +20 -2
  91. package/src/system/styles/CONVENTIONS.md +2 -2
  92. package/src/system/styles/tokens.css +12 -4
  93. package/template/package.json +3 -2
  94. package/template/src/pages/Home.svelte +1 -1
  95. package/src/editor/core/themes/slices/overlays.ts +0 -101
@@ -7,8 +7,8 @@ description: Apply the @motion-proto/live-tokens project conventions when buildi
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.
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.
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. Pass only the props it declares, with variant and size values from its union: `npx live-tokens components <id>` prints them (`--json` for data), and the list includes the project's own components beside the shipped ones. A prop a component does not declare is dropped silently at runtime, and the checker reports it. Author custom markup only when nothing fits, and then consider **live-tokens-create-component** so the new piece is editable too.
11
+ 2. **Use theme tokens for every value.** Every color, spacing, radius, font-size, and font-family in page CSS is a `var(--token-*)`, whether it sits in the `<style>` block, an inline `style=` attribute, or a `style:` directive. No colour literals in any notation, `white` and `rgb()` included. No px or rem in spacing, stroke, radius, or shadow: that is the geometry the theme owns and `adjust` moves. Sizing is layout, not theme: a hero's height, a max content width, or a column's minimum width stays a literal. A change in `/live-tokens/editor` should repaint your page.
12
12
 
13
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
14
 
@@ -45,8 +45,8 @@ const pages = {
45
45
 
46
46
  ## Avoid
47
47
 
48
- - Hex or pixel literals in page CSS.
49
- - Hardcoded column counts (`repeat(10, 1fr)`). Use `repeat(var(--columns-count), 1fr)`.
48
+ - Colour literals, and px or rem in spacing, stroke, radius, or shadow.
49
+ - Hardcoded page-grid counts (`repeat(10, 1fr)`). Use `repeat(var(--columns-count), 1fr)`, or `calc(var(--columns-count) - 2)` for a sub-grid that spans fewer page columns. A local two-up or three-up is a layout and is fine.
50
50
  - Utility classes overriding shipped components. Extend via the `/live-tokens/components` editor instead.
51
51
  - Deep imports from `node_modules/@motion-proto/live-tokens/src/...`. Use public entry points only.
52
52
  - Mounting `Editor` or `ComponentEditorPage` outside their dedicated routes.
@@ -54,4 +54,15 @@ const pages = {
54
54
 
55
55
  ## Verify
56
56
 
57
- In dev: change a colour in `/live-tokens/editor` and confirm your page repaints (proves token usage). The overlay's "Page Source" button on the new route opens the page in VS Code (proves the route's `source`). `ColumnsOverlay` (Cmd+G) shows content sitting inside `--columns-max-width`.
57
+ Run the checker and fix what it reports. Repeat until it exits 0:
58
+
59
+ ```sh
60
+ npx live-tokens check-page src/pages/YourPage.svelte
61
+ # or: npx @motion-proto/live-tokens check-page (every page under src/)
62
+ ```
63
+
64
+ It fails on a component outside the catalogue, a prop a component does not declare, a variant or size outside the prop's union, a deep import, a `var()` that resolves to nothing, a colour literal in any notation, a route under `/live-tokens/*`, and `site.css` imported from `main.ts`. It warns on a px or rem literal in spacing, stroke, radius, or shadow, a hardcoded column count of four or more, an absolute type value, and a route entry with no `source`. Inline `style=` attributes and `style:` directives are read the same way as the `<style>` block; a `var()` fallback is not the page's value and is never a finding.
65
+
66
+ Warnings do not fail the run. `--strict` makes them fail, which is the setting to use when the page is meant to be fully tokenized. `--json` prints findings with a stable `rule` id, so you can work through one rule at a time and re-run. `--off=<rule>` silences a rule for a run; `"checks": { "rules": { ... } }` in `live-tokens.config.json` sets it for the project. A project scaffolded by `create` runs the checker, with `check-component`, as `npm run check:design` before every `vite build`, so the page has to pass before it can ship.
67
+
68
+ Then in dev: change a colour in `/live-tokens/editor` and confirm your page repaints (proves token usage). The overlay's "Page Source" button on the new route opens the page in VS Code (proves the route's `source`). `ColumnsOverlay` (Cmd+G) shows content sitting inside `--columns-max-width`.
@@ -15,11 +15,10 @@ For pattern reference, read any shipped component's source directly from the con
15
15
  - Simplest reads (no state, no linked-block): `Card` (single variant with parts), `Badge` and `Callout` (multi-variant).
16
16
  - Multi-state (hover, disabled, focus): `Button`, `Input`.
17
17
  - Multi-part (overlay / header / body / footer): `Dialog`.
18
- - Multi-variant with linked siblings (`canBeLinked` + `groupKey`): `SegmentedControl`, `TabBar`.
19
- - Composes another shipped component: `CodeSnippet` (renders a `Tooltip` for the copy-confirmation popover).
18
+ - Multi-variant with linked siblings (`canBeLinked` + `groupKey`): `SegmentedControl`, `TabBar`. Composes another shipped component: `CodeSnippet`.
20
19
  - Editor files: `node_modules/@motion-proto/live-tokens/src/editor/component-editor/<Name>Editor.svelte`.
21
20
 
22
- **File-location note.** Shipped editors live in `src/editor/component-editor/` because they're library-internal. For *your* component, **co-locate** both files in `src/system/components/` per the recipe below. Read the shipped files for pattern, ignore their location.
21
+ **File-location note.** Shipped editors live in `src/editor/component-editor/` because they're library-internal. For *your* component, **co-locate** both files in `src/system/components/`. Read the shipped files for pattern, ignore their location.
23
22
 
24
23
  ## The recipe
25
24
 
@@ -43,7 +42,15 @@ For pattern reference, read any shipped component's source directly from the con
43
42
  });
44
43
  ```
45
44
  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).
45
+ 4. **Say what it is for.** The runtime file's leading HTML comment is the
46
+ component's description. `npx live-tokens components` prints it beside the
47
+ id with the variants and props read from `interface Props` (`--json` for
48
+ data); that is how **live-tokens-pick-component** weighs a project's own
49
+ component against the shipped set, so no skill file is edited and nothing
50
+ is lost when `setup-claude` refreshes the skills. Name the job it does and
51
+ what it is not for. A directory other than `src/system/components` goes in
52
+ `"componentDirs"` in `live-tokens.config.json`; a first-party component is
53
+ also added to the picker's **Catalogue** line, which `check:skills` holds.
47
54
  5. **Join the sketch layer** — the effect draws a fixed set of parts, so a new
48
55
  component stays crisp while the page around it goes hand-drawn until it opts
49
56
  in. A consumer component carries one of four reserved classes on its root and
@@ -52,7 +59,24 @@ For pattern reference, read any shipped component's source directly from the con
52
59
  `border-color`, `box-shadow`, `overflow`, `position` and both pseudo-elements
53
60
  away from the element it draws, which constrains where the class can go. Read
54
61
  `references/sketch-mode.md`.
55
- 6. **Verify** with the checklist at the bottom of this file, then place the component on a page with **live-tokens-build-page**.
62
+ 6. **Gate on the checker.** Run it, fix every error, and run it again. Do not
63
+ call the component done while it reports one:
64
+ ```bash
65
+ npx live-tokens check-component <id> --strict --json
66
+ ```
67
+ `--json` gives each finding a stable `rule` id and a line number, so work one
68
+ rule at a time and re-run rather than guessing. `--strict` fails on warnings
69
+ too, which is the right setting for a new component: every warning it raises
70
+ is a naming or token decision that is cheaper to make now than to migrate
71
+ later. Exit code 0 is the gate. With no id it checks every component under
72
+ `src/system/components`; a project scaffolded by `create` runs that as
73
+ `npm run check:design` before every `vite build`.
74
+
75
+ If it rejects a suffix, do not invent a new name for the role. Find a shipped
76
+ component that paints the same thing and use the name it uses: the catalogue
77
+ is the worked reference, and `bin/check-component.test.ts` holds all 26 of
78
+ them to this same contract.
79
+ 7. **Verify** with the checklist at the bottom of this file, then place the component on a page with **live-tokens-build-page**.
56
80
 
57
81
  ## Token discipline
58
82
 
@@ -73,18 +97,32 @@ For pattern reference, read any shipped component's source directly from the con
73
97
  The editor picker is chosen by the token's suffix, so the suffix is the naming
74
98
  decision that matters. Color and surface: `-surface`, `-border`, `-text`,
75
99
  `-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`.
100
+ `-tint`, `-background`, `-accent`, `-indicator`, `-thumb`, and the
101
+ element-named text roles `-title`, `-body`, `-eyebrow`, `-description`,
102
+ `-hint`, `-error`, `-placeholder`, `-value`. Geometry: `-radius`,
103
+ `-border-width`, `-accent-width`, `-hairline-thickness`, `-thickness`,
104
+ `-width`, `-height`, `-size`, `-padding`, `-margin`, `-gap`, `-inset`,
105
+ `-divider-width`, `-divider-thickness`, `-divider-height`, `-divider-inset`,
106
+ `-track-height`, `-dot-size`, `-thumb-size`, `-icon-size`, `-scale`, `-blur`.
107
+ Motion: `-duration`, `-easing`. Typography: `-font-family`, `-font-weight`,
108
+ `-font-size`, `-line-height`, `-letter-spacing`.
109
+
110
+ A token that carries a structural keyword rather than a value takes no suffix
111
+ from this list. Declare it in the editor's `intrinsics` instead, which is what
112
+ exempts it, and never end its name in a state word, which reads as
113
+ state-after-property and fails.
79
114
 
80
115
  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`.
116
+ compete. A suffix outside that list fails `check-component`. The list lives in
117
+ `KIND_RULES` in the editor's `aliasKinds.ts`, which the picker, the `adjust`
118
+ CLI, and `check-component` all read, so a name accepted here always has a
119
+ control behind it.
82
120
 
83
121
  ### Rules that bite
84
122
 
85
123
  - **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.
86
- - **State before property.** `--mywidget-button-hover-surface` ✓ — `--mywidget-button-surface-hover` ✗ (breaks sibling matching).
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.
124
+ - **State before property.** `--mywidget-button-hover-surface` ✓ — `--mywidget-button-surface-hover` ✗ (breaks sibling matching). Disabled is terminal in the name too: `-disabled-hover-` and `-selected-disabled-` describe states that never paint, and `check-component` rejects both.
125
+ - **Every default resolves to a theme token.** A component token names a semantic property; its default is the theme token that property reads. That is what makes the component repaint when the theme changes. `var(--surface-primary)` ✓, `#6a4ce8` ✗, `white` ✗, and `var(--surface-imaginary)` ✗: `check-component` fails on a colour literal in any notation, on a `var()` naming a token that does not exist, and on a default with no token behind it at all, so a raw `16rem` fails too. Composing tokens counts and is common: `color-mix(in srgb, var(--surface-neutral-lower) 70%, transparent)`, or `calc(var(--space-64) * 4)` for a width the spacing scale does not reach. The one value allowed without a token is a structural keyword (`contain`, `start`, `none`), and only when the editor declares it in `intrinsics`.
88
126
  - **No abbreviations.** `bg` → `surface`; `fg` → `text`; component ids are never abbreviated.
89
127
  - **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`.
90
128
  - **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.
@@ -186,17 +224,15 @@ npx live-tokens check-component <id>
186
224
  # or: npx @motion-proto/live-tokens check-component <id>
187
225
  ```
188
226
 
189
- It enforces the file layout, the `:global(:root)` block, token-suffix vocabulary, state-before-property rule, theme-token defaults (no raw colour literals), public-imports rule, and that the id is registered via either `bootLiveTokens({ components: [{ id }] })` or a direct `registerComponent({ id })` call. It also *warns* (non-fatal) when a type-group font helper is called bare across multiple slots, which would merge their fonts into one link tree. Exit code 0 means the static contract is met; resolve warnings before shipping.
227
+ It enforces the file layout, the `:global(:root)` block, token-suffix vocabulary, state-before-property rule, the terminal disabled state, public-imports rule, that every token an editor row names is declared in the runtime, and that the id is registered, via either `bootLiveTokens({ components: [{ id }] })` or a direct `registerComponent({ id })` call. On the value side it fails on a colour literal in any notation, on a default reading a token that does not exist, and on a default with no theme token behind it that the editor does not declare an intrinsic.
228
+
229
+ It *warns* (non-fatal) when a token-backed default still carries a px or rem term, and when a type-group font helper is called bare across multiple slots, which would merge their fonts into one link tree.
190
230
 
191
- **Then run the registry contract test.** If you're authoring inside the package itself, `src/editor/component-editor/registryContract.test.ts` runs `describe.each(getComponentRegistryEntries())` and verifies, per component:
231
+ Exit code 0 means the static contract is met. Resolve warnings before shipping, or run with `--strict` to make them fail. `--json` prints findings with a stable `rule` id, so you can work through one rule at a time and re-run.
192
232
 
193
- 1. Registration resolves to a real `sourceFile` and a non-empty schema.
194
- 2. Schema variables are unique within the component.
195
- 3. Every editable token (excluding `hidden: true`, `kind: 'gradient'`, and padding-side suffixes) is declared in the runtime `<style>` block.
196
- 4. Every editable token is seeded in `src/live-tokens/data/component-configs/<id>/default.json`.
197
- 5. `setComponentAlias` round-trips the alias through the slice.
233
+ **Then run the registry contract test.** If you're authoring inside the package itself, `src/editor/component-editor/registryContract.test.ts` runs `describe.each(getComponentRegistryEntries())` and verifies, per component, that the registration resolves to a real `sourceFile` and a non-empty schema, that schema variables are unique, that every editable token (excluding `hidden: true`, `kind: 'gradient'`, and padding-side suffixes) is declared in the runtime `<style>` block and seeded in `src/live-tokens/data/component-configs/<id>/default.json`, and that `setComponentAlias` round-trips the alias through the slice.
198
234
 
199
- 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).
235
+ 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: `getComponentRegistryEntries` is exported from `@motion-proto/live-tokens` and returns every registration, shipped and custom, once your `main.ts` has run.
200
236
 
201
237
  **If your component declares `intrinsics`, the intrinsics contract test covers it too.** `src/editor/component-editor/intrinsicsContract.test.ts` iterates every entry with an `intrinsics` array and asserts, per (intrinsic, variant), that the runtime `:global(:root)` declares a default, the default is one of the spec's `values`, and the editor's `default` equals the runtime default. This is what would have caught a getter defaulting to `center` while `:global(:root)` says `start`. Same auto-coverage rule: declare `intrinsics` on the registry entry and the test picks it up.
202
238
 
@@ -3,7 +3,7 @@
3
3
  The editor picker is chosen by suffix. There is no per-token override; if a
4
4
  token renders with the wrong picker, rename it to one of these.
5
5
 
6
- `KNOWN_SUFFIXES` in `bin/check-component.mjs` is authoritative, and
6
+ `KIND_RULES` in `src/editor/core/components/aliasKinds.ts` is authoritative, and
7
7
  `check-component` fails on a suffix outside it. `check:skills` holds this file
8
8
  to that list, so the two cannot drift apart.
9
9
 
@@ -22,6 +22,19 @@ to that list, so the two cannot drift apart.
22
22
  | `-shadow` | Box-shadow |
23
23
  | `-opacity` | Opacity (0–1) |
24
24
  | `-blur` | Backdrop or filter blur radius |
25
+ | `-tint` | A wash over the surface, aliasing a `--tint-*` stop |
26
+ | `-background` | Fill, where the component's own vocabulary says background |
27
+ | `-accent` | An accent bar or indicator's colour |
28
+ | `-indicator` | A selection indicator's colour |
29
+ | `-thumb` | A scrollbar or slider thumb's colour |
30
+ | `-title` | Title text colour |
31
+ | `-body` | Body text colour |
32
+ | `-eyebrow` | Eyebrow text colour |
33
+ | `-description` | Description text colour |
34
+ | `-hint` | Hint text colour |
35
+ | `-error` | Error text colour |
36
+ | `-placeholder` | Placeholder text colour |
37
+ | `-value` | A displayed value's colour |
25
38
 
26
39
  ## Geometry
27
40
 
@@ -30,6 +43,22 @@ to that list, so the two cannot drift apart.
30
43
  | `-radius` | Corner radius |
31
44
  | `-border-width` | Stroke thickness (used even when CSS uses `outline:`) |
32
45
  | `-thickness` | Alternative to `-width` when fallback siblings would collide |
46
+ | `-accent-width` | An accent bar's thickness |
47
+ | `-hairline-thickness` | A hairline rule's thickness |
48
+ | `-dot-size` | A dot indicator's diameter |
49
+ | `-divider-width` | A divider's thickness |
50
+ | `-divider-thickness` | Alternative to `-divider-width` |
51
+ | `-divider-height` | A divider's length |
52
+ | `-divider-inset` | Inset trimmed from a stretched divider |
53
+ | `-track-height` | A track's height (progress bar, slider) |
54
+ | `-icon-size` | An icon's rendered size |
55
+ | `-thumb-size` | A thumb's rendered size |
56
+ | `-height` | A measured height (a track, a panel) |
57
+ | `-margin` | Outer spacing, moved on the same scale as `-padding` |
58
+ | `-inset` | Inset trimmed from a stretched element |
59
+ | `-duration` | Motion duration |
60
+ | `-easing` | Motion easing curve |
61
+ | `-scale` | A transform scale factor |
33
62
  | `-width` | Width dimension |
34
63
  | `-size` | Square / uniform dimension |
35
64
  | `-padding` | Internal spacing |
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: live-tokens-fix-findings
3
+ description: Bring an existing @motion-proto/live-tokens project into line with its design system by running check-page and check-component, reading the findings, and fixing each by rule until both exit 0. Use when the user asks to make the build pass, fix the design-system errors or warnings, clean up the literals, replace hex or pixel values with tokens, make a page or component themeable, audit the site or a file against the design system, or asks why check:design fails. Not for building a new page (live-tokens-build-page) or a new component (live-tokens-create-component), which run the same gate as their last step, and not for a single token edit (use the editor).
4
+ ---
5
+
6
+ # Fixing what the checkers report
7
+
8
+ Two checkers hold a project to its design system. `check-page` holds pages:
9
+ every component comes from the catalogue and is passed only the props it
10
+ declares, and every value in page CSS is a theme token. `check-component`
11
+ holds authored components: every token names a semantic property and its
12
+ default is the theme token that property reads. A page or component that
13
+ passes repaints when the theme changes. One that does not has opted out of the
14
+ system silently, and these findings are where.
15
+
16
+ This skill is the loop for code that already exists. Run the checker, fix one
17
+ rule at a time, run it again, and stop only when both exit 0.
18
+
19
+ ## Reach the checkers
20
+
21
+ ```sh
22
+ npx live-tokens check-page --json # every page under src/
23
+ npx live-tokens check-component --json # every component authored under src/system/components
24
+ ```
25
+
26
+ - **Unknown command.** The installed package predates the checkers. Upgrade
27
+ `@motion-proto/live-tokens`, then run `npx live-tokens migrate --check` and
28
+ apply what it plans with `--write`; `--tokens <path>` names a tokens.css that
29
+ sits somewhere other than the four default locations.
30
+ - **No `check:design` script.** A project scaffolded by `create` has one. Add
31
+ it to any other project's `package.json`:
32
+ `"check:design": "live-tokens check-page && live-tokens check-component"`.
33
+ Once it passes, gate the build: `"build": "npm run check:design && vite build"`.
34
+ - **A file, not the project.** `check-page src/pages/Home.svelte` and
35
+ `check-component <id>` scope a run when the user names one thing.
36
+
37
+ ## The loop
38
+
39
+ 1. Run with `--json`. Each finding carries a stable `rule`, a file, and a line.
40
+ 2. Group by rule. Take errors before warnings, and the rule with the most
41
+ findings first, because one recipe clears the whole group.
42
+ 3. Apply that rule's recipe, below, to every finding in the group.
43
+ 4. Run again. New findings can appear as old ones clear: a token you reached
44
+ for may not exist, or a moved import may land somewhere the rule now sees.
45
+ 5. Stop at exit 0. Then run once with `--strict` and report what it adds, so
46
+ the user can decide whether warnings are worth clearing now.
47
+
48
+ Three things the loop never does:
49
+
50
+ - **Silence a rule to pass.** `--off=<rule>` is for a single run while
51
+ working. A severity the project wants changed goes in
52
+ `live-tokens.config.json` under `"checks": { "rules": { "<rule>": "warn" } }`,
53
+ with the reason in the commit, and only when the user has made that call.
54
+ - **Mint a token.** A literal with no token behind it is remapped to the
55
+ nearest existing token by role. No new `--surface-*`, `--text-*`, or
56
+ `--space-*` is added to `tokens.css` to match a value the page happened to
57
+ use. If nothing fits, say so and leave the finding.
58
+ - **Change what the page looks like without saying so.** Most remaps land on
59
+ the same value. When the nearest token differs, `14px` to `--space-16` or a
60
+ 55% black to `--scrim`, name the shift in the report.
61
+
62
+ ## Colour by role, never by hue
63
+
64
+ `color-literal` is the finding that takes judgement. The replacement is the
65
+ token for what the colour *does*, not the token that happens to be closest in
66
+ hue, because the theme will move every role together and the page must move
67
+ with it. `npx live-tokens tokens --family surface` prints a family's names and
68
+ values (`text`, `border`, `scrim`, `tint` likewise; `--json` for data); the
69
+ families are fixed.
70
+
71
+ | The literal is | Token family | Notes |
72
+ | --- | --- | --- |
73
+ | Text on a surface | `--text-primary`, `-secondary`, `-tertiary`, `-muted`, `-disabled` | The neutral scale. Family colour is `--text-accent`, `--text-success`, and so on. |
74
+ | Light text on a dark chip over the page | `--text-inverted` | The one flip; no AA guarantee. |
75
+ | A box's fill | `--surface-<family>-<level>` | `neutral` for chrome; `brand`, `accent`, `special` for emphasis; `info`, `success`, `warning`, `danger` for status. |
76
+ | A stroke | `--border-<family>-<level>` | `faint`, `subtle`, base, `medium`, `strong` in the neutral family. |
77
+ | A translucent layer that dims what is behind it | `--scrim-low`, `--scrim`, `--scrim-high` | Behind a modal, under a floating control. |
78
+ | A translucent wash on a surface | `--tint-low`, `--tint`, `--tint-high` | Hover, an active tab, a code chip's background. |
79
+ | Fully transparent | `--color-transparent` | Never `transparent` inside a component default. |
80
+ | A gradient | `--gradient-*` | Or compose one from surface tokens. |
81
+
82
+ A `var(--x, #fff)` fallback is not a finding. A named colour is: `white` and
83
+ `rebeccapurple` are literals like any hex.
84
+
85
+ ## Geometry by scale
86
+
87
+ `dimension-literal` fires only on the geometry the theme owns: padding,
88
+ margin, gap, border and outline widths, inset offsets, radius, and shadow.
89
+ Sizing (a hero's height, a max content width, a `minmax()` floor) is layout
90
+ and is never reported, so leave it.
91
+
92
+ | The literal is | Token | Notes |
93
+ | --- | --- | --- |
94
+ | Padding, margin, gap, an offset | `--space-<px>` | `npx live-tokens tokens --family space` prints the steps. Round to the nearest one and name the shift. |
95
+ | A stroke width | `--border-width-1`, `-2`, `-4` | Also for `outline`. |
96
+ | A corner | `--radius-sm` through `-4xl`, `--radius-full` | |
97
+ | A shadow | `--shadow-sm` through `-xl` | Replace the whole value, never one offset. |
98
+ | Part of a `calc()` | The token inside the calc | `calc(var(--space-64) * -2 + var(--space-8))` |
99
+
100
+ While in the file, motion values take `--duration-*` and `--ease-*` even
101
+ though no rule reports them, and a `blur()` takes `--blur-*`.
102
+
103
+ ## Every other rule
104
+
105
+ | Rule | Fix |
106
+ | --- | --- |
107
+ | `unknown-token` | A typo or a rename. Search `tokens.css` for the stem. A contract-family name (`--surface-…`, `--text-…`) that is gone was renamed: `npx live-tokens migrate --check` names the migration. |
108
+ | `raw-text-axis` | Set the whole axis set from one text style: `--heading-xl` through `-sm`, `--body-md`, `--body-sm`, `--editorial-*`, `--eyebrow`, `--code`, each carrying `-font-family`, `-font-size`, `-font-weight`, `-line-height`, `-letter-spacing`. A `font:` shorthand is rewritten the same way. `em`, `%`, and a unitless line-height are relative and fine. |
109
+ | `unknown-component` | Not in the catalogue. Read **live-tokens-pick-component** for the shipped one that fits, or author it with **live-tokens-create-component**. |
110
+ | `unknown-prop` | The component drops it at runtime. `npx live-tokens components <id>` prints the props it declares and the values each union accepts; map the prop to one of them or delete it. A `class` on a component that declares none does nothing. |
111
+ | `unknown-prop-value` | Pick a value from the union the message lists. |
112
+ | `hardcoded-columns` | `repeat(var(--columns-count), 1fr)` for the page grid; `calc(var(--columns-count) - 2)` for a sub-grid spanning fewer page columns. A two-up or three-up is a layout and is not reported. |
113
+ | `site-css-in-main` | Delete the import from `main.ts` and add it to each page's `<script>`, so page CSS never reaches the editor routes. |
114
+ | `missing-source` | Add `source: 'src/...'` to the route entry so Page Source can open it. |
115
+ | `reserved-route` | Move the route out of `/live-tokens/*`; the package owns that namespace. |
116
+ | `deep-import` | Import from `@motion-proto/live-tokens` or `/component-editor` or `/components/<Name>.svelte`, never from `/src/`. |
117
+ | `unknown-suffix`, `state-after-property`, `disabled-is-terminal` | Rename the token. Borrow the name a shipped component uses for the same role; the vocabulary and the state model are in **live-tokens-create-component**. |
118
+ | `color-literal`, `unknown-token-ref`, `default-not-token` (component) | The `:global(:root)` default reads a theme token, composed if needed. A structural keyword (`start`, `contain`) is declared in the editor's `intrinsics`. |
119
+ | `phantom-editor-token`, `phantom-link` | The editor names a token the runtime never declares, or a bare font helper spans slots. Both are editor fixes; see the same skill. |
120
+ | `missing-registration`, `missing-file`, `missing-root-block` | The component is not wired the way the recipe in **live-tokens-create-component** wires it. |
121
+
122
+ ## Report
123
+
124
+ Say what changed by rule, one line per rule with the count and any visible
125
+ shift. Say what was left and why, with the config entry if the user chose to
126
+ lower a severity. End with the two commands and their exit codes.
127
+
128
+ ## Verify
129
+
130
+ Open `/live-tokens/editor` in dev and change a surface colour and a spacing
131
+ step. Every file the loop touched should repaint. One that does not still
132
+ holds a literal the checker cannot see, which is worth reporting as a gap in
133
+ the checker rather than patching around.
@@ -11,7 +11,15 @@ For composing a page once you've picked components, see **live-tokens-build-page
11
11
 
12
12
  ## Catalogue
13
13
 
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`.
14
+ Action: `Button`, `IconButton`, `InlineEditActions`. Input: `Input`, `Slider`. 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
+
16
+ That line is the shipped set. A project can register components of its own,
17
+ and those never appear in this file: run `npx live-tokens components` before
18
+ choosing. It lists every component the project has, shipped and custom, with
19
+ the variants each takes and the purpose its header comment states, so a custom
20
+ component is weighed against the shipped set on the same footing.
21
+ `npx live-tokens components <id>` prints one component's props, the values each
22
+ union accepts, and its tokens with defaults; `--json` returns the same as data.
15
23
 
16
24
  ## Action family: Button vs IconButton
17
25
 
@@ -42,6 +50,7 @@ All four pick one option from a set. The right one depends on **option count**,
42
50
 
43
51
  - `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
52
  - 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`.
53
+ - `Slider` takes a number inside a known range where the position carries the meaning: a volume, a price band, a percentage. Its `range` variant takes a low and a high bound on one track. A number the user knows exactly and would rather type is `Input` with `type="number"`.
45
54
  - **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
55
  - 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
56
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,241 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.70.0 — The registry is a query
4
+
5
+ ### Added
6
+
7
+ - **The registry is a query.** `npx live-tokens components` lists every
8
+ component a project has, shipped and its own, with the variants and props
9
+ read from each `interface Props` and the purpose its header comment states;
10
+ `components <id>` prints one component's props, unions, tokens, and
11
+ defaults. `npx live-tokens tokens` lists every theme token the project's
12
+ `tokens.css` declares by family with its value, `--family <name>` for one
13
+ scale. Both take `--json`. A project's components in a directory other than
14
+ `src/system/components` are found through `"componentDirs"` in
15
+ `live-tokens.config.json`. The same vocabulary the checkers read answers the
16
+ query, so a skill sees exactly what the checkers will hold it to.
17
+
18
+ - **`getComponentRegistryEntries` is exported from the package**, so a
19
+ project's own test suite can run the registry contract over every
20
+ registration, shipped and custom. The create-component skill pointed at it
21
+ before it was public.
22
+
23
+ ### Changed
24
+
25
+ - **The skills read the registry instead of carrying it.** The picker's
26
+ catalogue line is the shipped set only; a project's own component is found
27
+ by `live-tokens components`, weighed by the description its header comment
28
+ states, and never written into a skill file, so `setup-claude --force` no
29
+ longer loses anything. build-page and fix-findings read a component's props
30
+ from the same query, and fix-findings reads a token scale from `tokens`.
31
+
32
+ ## 0.69.0 — Every value reads a token, and the build says so
33
+
34
+ ### Changed
35
+
36
+ - **SideNavigation's panel widths read the spacing scale.** `16rem` and `3rem`
37
+ were the only shipped defaults with no token behind them; both now derive
38
+ from the largest step, `calc(var(--space-64) * 4)` and
39
+ `calc(var(--space-64) * 0.75)`. Values are unchanged.
40
+
41
+ - **The starter `site.css` is fully tokenized.** A paragraph margin and three
42
+ rule and blockquote strokes were px literals; they read `--space-16` and
43
+ `--border-width-*` now, so a scaffolded project starts clean under
44
+ `check-page --strict`. The paragraph margin moves from 14px to 16px.
45
+
46
+ - **TabBar's active tab and Button's inline-code badge are tints, not scrims.**
47
+ Both washed a surface rather than dimming what sat behind it, and only read a
48
+ scrim because no tint family existed. **They restyle**: a 38% near-black wash
49
+ becomes a 10% white one, so each reads lighter and softer. A theme that pointed
50
+ the TabBar alias somewhere else keeps its choice.
51
+
52
+ - **Optional-interaction gates take `-enabled`, not a state word.**
53
+ `--card-hover-border-active`, `--card-hover-shadow-active`,
54
+ `--image-zoom-hover`, and `--image-grow-hover` read as state-after-property and
55
+ failed `check-component`, which meant the documented gate pattern was one a
56
+ consumer could not use. A gate is not a state: it says whether the interaction
57
+ is on at all. Values are unchanged.
58
+
59
+ - **Button's shimmer and ImageLightbox's tile fit are declared intrinsics.**
60
+ Both were bare keywords with no theme token and no declaration, which is the
61
+ thing `intrinsics` exists to record. `--button-shimmer` now defaults to
62
+ `var(--shimmer-on)`, the token that was already there for it.
63
+
64
+ - **`--hover-*` is now `--tint-*`, and it has a baseline for the first time.** A
65
+ state is a segment of a property name (`--button-outline-hover-surface`), not
66
+ a token of its own, so the three stops are named for what they are: a tint
67
+ shades the surface it sits on. Values are unchanged.
68
+
69
+ The theme engine had always emitted these stops, but `tokens.css` never
70
+ declared them, so they resolved to nothing until a theme was adopted. That is
71
+ why `var(--hover)` painted no pressed state on a fresh install. `--tint-*` is
72
+ baselined, and that half of the migration is additive, so it auto-applies.
73
+
74
+ - **`--overlay-*` is now `--scrim-*`.** A scrim is a translucent layer that dims
75
+ what sits behind it, which is what a dialog draws over the page. The old name
76
+ had spread to cover surface tints as well, which are the opposite operation,
77
+ and it collided with `backdrop`, the exported concept for what paints behind
78
+ an element and which way it leans. Each name now means one thing. Values are
79
+ unchanged, so nothing repaints.
80
+
81
+ `npx live-tokens migrate` renames the three tokens in a vendored `tokens.css`.
82
+ Token names are public API, so the migration is breaking and never
83
+ auto-applies. Saved themes and component configs migrate on load.
84
+
85
+ Dialog's part follows its token: `--dialog-overlay-surface` is
86
+ `--dialog-scrim-surface`, and the editor labels it "scrim color" rather than
87
+ the "backdrop color" that named a third thing again. The editor's Overlays
88
+ section is now Washes, holding Scrims and Tints.
89
+
90
+ ### Added
91
+
92
+ - **The shipped catalogue is now the component contract's fixture.**
93
+ `check-component` reported 109 errors across all 26 shipped components, and
94
+ none of them were defects in the components: the checker's suffix list had
95
+ simply drifted narrower than the catalogue it governs, rejecting `-accent`,
96
+ `-title`, `-margin`, `-easing` and a dozen more names our own components use.
97
+ A test now runs the full contract over every registered component and requires
98
+ zero errors, so the rule and the components can never disagree again.
99
+
100
+ The suffix vocabulary moved to `KIND_RULES` in the editor's `aliasKinds.ts`.
101
+ The picker, the `adjust` CLI, `check-component`, and `check:skills` all read
102
+ that one table, so a name the checker accepts always has a control behind it.
103
+
104
+ Three rules got more accurate along the way. A token the editor declares in
105
+ `intrinsics` is exempt from the suffix check, because a structural keyword is
106
+ not a themeable value. Membership in the package's own `builtInRegistry`
107
+ counts as registration. And a component may prefix its tokens with the
108
+ hyphenated form of its id, which is what CornerBadge has always done.
109
+
110
+ - **An optional hover tint on Button, IconButton, TabBar, SegmentedControl,
111
+ MenuSelect, and SideNavigation.** One stop for the whole component rather than
112
+ one hover surface per variant: Button and IconButton carried 36 hover tokens
113
+ each, and every one was tuned by hand. `--<id>-hover-tint` aliases a `--tint-*`
114
+ stop, and `--<id>-hover-tint-enabled` gates it.
115
+
116
+ Off by default, so nothing changes until a project turns it on. The tint rides
117
+ as a `background-image` over the hover rule's own `background-color`, so it
118
+ needs no pseudo-element.
119
+
120
+ A **tint layer** switch in the component editor's hover state turns it on and
121
+ points every hover surface at its own base surface, so hover is the tint alone
122
+ rather than a swap wearing a wash. The hover-surface rows grey out while it is
123
+ on, since they no longer change anything. Switching it off clears those
124
+ overrides, returning each surface to its shipped default.
125
+
126
+ Switching it on reveals a **tint color** row beside it. It starts on `--tint`,
127
+ the theme's middle tint stop, and takes any colour token with an alpha, like
128
+ every other colour row. A `-tint` suffix now resolves to the surface picker
129
+ rather than falling through, so the row offers the whole palette, not only the
130
+ tint stops.
131
+
132
+ Per instance, the `hoverTint` prop overrides the global default (`undefined`
133
+ inherits, `true` and `false` force).
134
+
135
+ - **`live-tokens check-page` validates a page against the build-page
136
+ contract.** It fails on a component outside the catalogue, a deep import into
137
+ package internals, a `var()` naming a token that does not exist, a colour
138
+ literal, a route under the reserved `/live-tokens/*` namespace, and `site.css`
139
+ imported from `main.ts`, a prop a shipped component does not declare, and a
140
+ variant or size outside that prop's union. It warns on a px or rem literal
141
+ in spacing, stroke, radius, or shadow, a hardcoded page-grid count of four
142
+ columns or more, an absolute type value (the `font` shorthand included), and
143
+ a route entry with no `source`. Sizing is layout and is never reported.
144
+ Inline `style=` attributes and `style:` directives are read under the same
145
+ rules as the `<style>` block, and a named colour is a literal like any hex.
146
+ Given no paths it checks every page under `src/`.
147
+
148
+ - **Both check commands take `--json`, `--strict`, and per-rule severity
149
+ flags.** `--json` prints findings with a stable `rule` id and line number, so
150
+ a skill can fix one rule at a time and re-run until the exit code is 0.
151
+ `--strict` promotes warnings to errors. `--off=<rule>`, `--warn=<rule>`, and
152
+ `--error=<rule>` change a rule for one run; `"checks": { "rules": { ... } }`
153
+ in `live-tokens.config.json` sets it for the project.
154
+
155
+ - **`check-component` now checks what a default *resolves to*, not just what it
156
+ is named.** A component token names a semantic property and its default is the
157
+ theme token that property reads, which is what makes the component repaint
158
+ when the theme changes. A default reading a token that does not exist is now
159
+ an error, including a `var()` naming a state word rather than the token that
160
+ state should paint. A default with no token behind it is an error unless the
161
+ editor declares it in `intrinsics`; a colour literal in any notation is an
162
+ error; a token-backed default that still carries a px or rem term warns.
163
+ Two more rules read the name alone: `disabled-is-terminal` rejects a token
164
+ that combines `disabled` with `hover`, `focus`, `selected`, `on`, `active`,
165
+ or `checked`, and `phantom-editor-token` rejects an editor row naming a
166
+ token the runtime never declares. Given no id, `check-component` checks
167
+ every component authored under `src/system/components`, and a scaffolded
168
+ project runs both checkers as `npm run check:design` before every
169
+ `vite build`. `check-component` also finds a shipped component's editor
170
+ beside the other editors, not only next to its runtime.
171
+
172
+ - **`npm run check:pages` runs the page check over this repo under `--strict`
173
+ and is part of `prepublishOnly`.** The unit suite covers the same ground:
174
+ the repo's pages carry no finding at all, and every shipped component
175
+ default resolves to a real token. Both suites also hold a mutation table: a
176
+ clean component and a clean page that pass `--strict`, and one smallest
177
+ break per rule that must fail, so a rule cannot stop firing unnoticed.
178
+
179
+ - **`Slider`, in two variants.** `single` moves one thumb to a value; `range`
180
+ moves two thumbs to a low and a high bound on one track. Both share track,
181
+ fill, and thumb tokens, linked in the editor so an edit to one moves the
182
+ other until deliberately unlinked, with hover and disabled states and a
183
+ label and value readout. It was authored by following
184
+ `live-tokens-create-component` end to end, and `check-component --strict`
185
+ was clean on the first run.
186
+
187
+ - **`live-tokens-fix-findings`, the seventh skill.** The loop for code that
188
+ already exists: run both checkers with `--json`, take the largest group of
189
+ errors first, apply that rule's recipe, re-run, stop at exit 0. It carries
190
+ one recipe per rule id, with colour mapped by role rather than hue and
191
+ geometry by scale, and three refusals: never silence a rule to pass, never
192
+ mint a token, never shift the look without saying so. Its first run, on the
193
+ package's own demo site, took three rounds and ended clean.
194
+
195
+ ### Fixed
196
+
197
+ - **The tint layer switch was unreachable in four editors.** SegmentedControl,
198
+ TabBar, MenuSelect, and SideNavigation gated the row on a state named
199
+ `hover`, and their hover states are `hover option`, `hover tab`,
200
+ `hover item`, and `<Part> / Hover`, so the switch and the tint colour row
201
+ never rendered. Each condition now names the state the editor has. The
202
+ render contract caught it once it learned to turn a gate on: a gate row
203
+ carries `data-token-variables` and the harness flips a `role="switch"`
204
+ Toggle the way it clicks a checkbox, then exercises the row the gate
205
+ revealed. A selector whose selections are locked carries a `locked` class,
206
+ which the harness skips instead of retrying a chip it can never click.
207
+
208
+ - **`check-page` no longer reports a `var()` fallback as a colour literal.**
209
+ `var(--surface-neutral, #111)` paints the token; the literal only renders
210
+ when the token is missing. Against the package's own demo site this was 33
211
+ of 39 errors.
212
+
213
+ - **`check-page` reads a custom property with a digit in its name as one
214
+ declaration.** `--heading-2xl: 1.875rem` was parsed as the property `xl`
215
+ and reported as a raw dimension. The project's own `tokens.css` and the
216
+ generated token files are also no longer discovered as pages.
217
+
218
+ - **Both checkers read a `:global(:root)` block the same way.** They had two
219
+ extractors, one of which stopped at the first `}`, so a nested at-rule or
220
+ SCSS block truncated the block. One brace-balanced extractor is shared.
221
+
222
+ - **An `intrinsics` array on one line still exempts its token.** The
223
+ exemption required the closing `];` on its own line.
224
+
225
+ - **A persisted editor session from before the rename no longer breaks the
226
+ renderer.** `hydrate` shallow-merges persisted state, so a `washes` (or
227
+ `overlays`) slice saved by an older build replaced the current one wholesale
228
+ and arrived without its tint stops, throwing `w.tints is not iterable` out of
229
+ the render path on load. Hydration now reshapes the slice: it carries the
230
+ saved stops across under their new names and falls back to the shipped
231
+ defaults for anything unusable.
232
+
233
+ - **The outline Button and IconButton pressed state had no colour.** Both read
234
+ `var(--hover)`, which names nothing: a state is a segment of a semantic
235
+ property name (`--button-outline-hover-surface`), never a token of its own.
236
+ They read `--surface-neutral-low` now, one step along the scale from their
237
+ hover surface. `check-component` catches this class of mistake by name.
238
+
3
239
  ## 0.68.1 — A look is a Theme, or it's a sketch style
4
240
 
5
241
  ### Changed