@motion-proto/live-tokens 0.68.1 → 0.69.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 (93) hide show
  1. package/.claude/skills/live-tokens-build-page/SKILL.md +16 -5
  2. package/.claude/skills/live-tokens-create-component/SKILL.md +43 -8
  3. package/.claude/skills/live-tokens-create-component/references/token-naming.md +30 -1
  4. package/.claude/skills/live-tokens-fix-findings/SKILL.md +131 -0
  5. package/.claude/skills/live-tokens-pick-component/SKILL.md +2 -1
  6. package/CHANGELOG.md +207 -0
  7. package/README.md +11 -4
  8. package/bin/check-component.mjs +367 -63
  9. package/bin/check-page.mjs +409 -0
  10. package/bin/cli.mjs +57 -8
  11. package/bin/lib/cssValues.mjs +50 -0
  12. package/bin/lib/findings.mjs +106 -0
  13. package/bin/lib/tokenVocabulary.mjs +213 -0
  14. package/dist-plugin/adjust/index.cjs +174 -23
  15. package/dist-plugin/adjust/index.js +68 -23
  16. package/dist-plugin/{chunk-2UX6EVVA.js → chunk-2YNERPXY.js} +1 -1
  17. package/dist-plugin/{chunk-NE6N66EE.js → chunk-GPIBU44G.js} +107 -1
  18. package/dist-plugin/{chunk-ZHPX7ZYQ.js → chunk-RFVYPNRO.js} +39 -1
  19. package/dist-plugin/generateColorsAndType/index.cjs +107 -1
  20. package/dist-plugin/generateColorsAndType/index.js +1 -1
  21. package/dist-plugin/index.cjs +146 -2
  22. package/dist-plugin/index.js +3 -3
  23. package/dist-plugin/migrateData/index.cjs +107 -1
  24. package/dist-plugin/migrateData/index.js +2 -2
  25. package/dist-plugin/tokensCssMigrations/index.cjs +39 -1
  26. package/dist-plugin/tokensCssMigrations/index.js +1 -1
  27. package/package.json +3 -2
  28. package/src/app/site.css +4 -4
  29. package/src/editor/component-editor/ButtonEditor.svelte +71 -4
  30. package/src/editor/component-editor/CardEditor.svelte +6 -6
  31. package/src/editor/component-editor/DialogEditor.svelte +3 -3
  32. package/src/editor/component-editor/IconButtonEditor.svelte +68 -3
  33. package/src/editor/component-editor/ImageEditor.svelte +8 -8
  34. package/src/editor/component-editor/ImageLightboxEditor.svelte +12 -1
  35. package/src/editor/component-editor/MenuSelectEditor.svelte +63 -3
  36. package/src/editor/component-editor/SegmentedControlEditor.svelte +63 -3
  37. package/src/editor/component-editor/SideNavigationEditor.svelte +67 -3
  38. package/src/editor/component-editor/SliderEditor.svelte +186 -0
  39. package/src/editor/component-editor/TabBarEditor.svelte +63 -3
  40. package/src/editor/component-editor/registry.ts +10 -0
  41. package/src/editor/core/components/aliasKinds.ts +51 -28
  42. package/src/editor/core/sketch/sketchLayer.ts +16 -0
  43. package/src/editor/core/store/editorPersistence.ts +44 -1
  44. package/src/editor/core/store/editorRenderer.ts +2 -2
  45. package/src/editor/core/store/editorStore.ts +18 -18
  46. package/src/editor/core/store/editorTypes.ts +7 -6
  47. package/src/editor/core/themes/migrations/2026-09-01-gate-suffix-enabled.ts +31 -0
  48. package/src/editor/core/themes/migrations/2026-09-01-scrim-rename.ts +52 -0
  49. package/src/editor/core/themes/migrations/2026-09-01-tabbar-active-tint.ts +27 -0
  50. package/src/editor/core/themes/migrations/2026-09-01-tint-rename.ts +42 -0
  51. package/src/editor/core/themes/migrations/index.ts +16 -0
  52. package/src/editor/core/themes/slices/domainVars.ts +2 -2
  53. package/src/editor/core/themes/slices/washes.ts +107 -0
  54. package/src/editor/docs/content/editing-tokens.md +5 -3
  55. package/src/editor/docs/content.generated.ts +1 -1
  56. package/src/editor/pages/EditorShell.svelte +1 -1
  57. package/src/editor/ui/SurfacesTab.svelte +3 -3
  58. package/src/editor/ui/UITokenSelector.svelte +1 -0
  59. package/src/editor/ui/VariablesTab.svelte +2 -2
  60. package/src/editor/ui/sections/{OverlaysSection.svelte → WashesSection.svelte} +44 -43
  61. package/src/live-tokens/data/colors-and-type/autumn.json +6 -6
  62. package/src/live-tokens/data/colors-and-type/default.json +6 -6
  63. package/src/live-tokens/data/colors-and-type/halloween.json +6 -6
  64. package/src/live-tokens/data/colors-and-type/midnight-study.json +6 -6
  65. package/src/live-tokens/data/colors-and-type/ocean.json +6 -6
  66. package/src/live-tokens/data/colors-and-type/royal-velvet.json +6 -6
  67. package/src/live-tokens/data/colors-and-type/sketchy.json +6 -6
  68. package/src/live-tokens/data/colors-and-type/spring-meadow.json +6 -6
  69. package/src/live-tokens/data/colors-and-type/sunset.json +6 -6
  70. package/src/live-tokens/data/themes/autumn.json +81 -13
  71. package/src/live-tokens/data/themes/halloween.json +81 -13
  72. package/src/live-tokens/data/themes/midnight-study.json +81 -13
  73. package/src/live-tokens/data/themes/ocean.json +81 -13
  74. package/src/live-tokens/data/themes/royal-velvet.json +81 -13
  75. package/src/live-tokens/data/themes/sketchy.json +81 -13
  76. package/src/live-tokens/data/themes/spring-meadow.json +81 -13
  77. package/src/live-tokens/data/themes/sunset.json +81 -13
  78. package/src/live-tokens/data/tokens.generated.css +6 -6
  79. package/src/system/components/Button.svelte +28 -10
  80. package/src/system/components/Card.svelte +6 -6
  81. package/src/system/components/Dialog.svelte +3 -3
  82. package/src/system/components/IconButton.svelte +23 -7
  83. package/src/system/components/Image.svelte +6 -6
  84. package/src/system/components/MenuSelect.svelte +17 -1
  85. package/src/system/components/SegmentedControl.svelte +20 -2
  86. package/src/system/components/SideNavigation.svelte +22 -2
  87. package/src/system/components/Slider.svelte +348 -0
  88. package/src/system/components/TabBar.svelte +20 -2
  89. package/src/system/styles/CONVENTIONS.md +2 -2
  90. package/src/system/styles/tokens.css +12 -4
  91. package/template/package.json +3 -2
  92. package/template/src/pages/Home.svelte +1 -1
  93. 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 its `interface Props` declares, with variant and size values from its union: a prop it 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`.
@@ -52,7 +52,24 @@ For pattern reference, read any shipped component's source directly from the con
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, then place the component on a page with **live-tokens-build-page**.
55
+ 6. **Gate on the checker.** Run it, fix every error, and run it again. Do not
56
+ call the component done while it reports one:
57
+ ```bash
58
+ npx live-tokens check-component <id> --strict --json
59
+ ```
60
+ `--json` gives each finding a stable `rule` id and a line number, so work one
61
+ rule at a time and re-run rather than guessing. `--strict` fails on warnings
62
+ too, which is the right setting for a new component: every warning it raises
63
+ is a naming or token decision that is cheaper to make now than to migrate
64
+ later. Exit code 0 is the gate. With no id it checks every component under
65
+ `src/system/components`; a project scaffolded by `create` runs that as
66
+ `npm run check:design` before every `vite build`.
67
+
68
+ If it rejects a suffix, do not invent a new name for the role. Find a shipped
69
+ component that paints the same thing and use the name it uses: the catalogue
70
+ is the worked reference, and `bin/check-component.test.ts` holds all 26 of
71
+ them to this same contract.
72
+ 7. **Verify** with the checklist at the bottom of this file, then place the component on a page with **live-tokens-build-page**.
56
73
 
57
74
  ## Token discipline
58
75
 
@@ -73,18 +90,32 @@ For pattern reference, read any shipped component's source directly from the con
73
90
  The editor picker is chosen by the token's suffix, so the suffix is the naming
74
91
  decision that matters. Color and surface: `-surface`, `-border`, `-text`,
75
92
  `-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`.
93
+ `-tint`, `-background`, `-accent`, `-indicator`, `-thumb`, and the
94
+ element-named text roles `-title`, `-body`, `-eyebrow`, `-description`,
95
+ `-hint`, `-error`, `-placeholder`, `-value`. Geometry: `-radius`,
96
+ `-border-width`, `-accent-width`, `-hairline-thickness`, `-thickness`,
97
+ `-width`, `-height`, `-size`, `-padding`, `-margin`, `-gap`, `-inset`,
98
+ `-divider-width`, `-divider-thickness`, `-divider-height`, `-divider-inset`,
99
+ `-track-height`, `-dot-size`, `-thumb-size`, `-icon-size`, `-scale`, `-blur`.
100
+ Motion: `-duration`, `-easing`. Typography: `-font-family`, `-font-weight`,
101
+ `-font-size`, `-line-height`, `-letter-spacing`.
102
+
103
+ A token that carries a structural keyword rather than a value takes no suffix
104
+ from this list. Declare it in the editor's `intrinsics` instead, which is what
105
+ exempts it, and never end its name in a state word, which reads as
106
+ state-after-property and fails.
79
107
 
80
108
  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`.
109
+ compete. A suffix outside that list fails `check-component`. The list lives in
110
+ `KIND_RULES` in the editor's `aliasKinds.ts`, which the picker, the `adjust`
111
+ CLI, and `check-component` all read, so a name accepted here always has a
112
+ control behind it.
82
113
 
83
114
  ### Rules that bite
84
115
 
85
116
  - **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.
117
+ - **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.
118
+ - **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
119
  - **No abbreviations.** `bg` → `surface`; `fg` → `text`; component ids are never abbreviated.
89
120
  - **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
121
  - **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,7 +217,11 @@ npx live-tokens check-component <id>
186
217
  # or: npx @motion-proto/live-tokens check-component <id>
187
218
  ```
188
219
 
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.
220
+ 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.
221
+
222
+ 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.
223
+
224
+ 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.
190
225
 
191
226
  **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:
192
227
 
@@ -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,131 @@
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. Read `tokens.css` for the names; the families are fixed.
68
+
69
+ | The literal is | Token family | Notes |
70
+ | --- | --- | --- |
71
+ | Text on a surface | `--text-primary`, `-secondary`, `-tertiary`, `-muted`, `-disabled` | The neutral scale. Family colour is `--text-accent`, `--text-success`, and so on. |
72
+ | Light text on a dark chip over the page | `--text-inverted` | The one flip; no AA guarantee. |
73
+ | A box's fill | `--surface-<family>-<level>` | `neutral` for chrome; `brand`, `accent`, `special` for emphasis; `info`, `success`, `warning`, `danger` for status. |
74
+ | A stroke | `--border-<family>-<level>` | `faint`, `subtle`, base, `medium`, `strong` in the neutral family. |
75
+ | A translucent layer that dims what is behind it | `--scrim-low`, `--scrim`, `--scrim-high` | Behind a modal, under a floating control. |
76
+ | A translucent wash on a surface | `--tint-low`, `--tint`, `--tint-high` | Hover, an active tab, a code chip's background. |
77
+ | Fully transparent | `--color-transparent` | Never `transparent` inside a component default. |
78
+ | A gradient | `--gradient-*` | Or compose one from surface tokens. |
79
+
80
+ A `var(--x, #fff)` fallback is not a finding. A named colour is: `white` and
81
+ `rebeccapurple` are literals like any hex.
82
+
83
+ ## Geometry by scale
84
+
85
+ `dimension-literal` fires only on the geometry the theme owns: padding,
86
+ margin, gap, border and outline widths, inset offsets, radius, and shadow.
87
+ Sizing (a hero's height, a max content width, a `minmax()` floor) is layout
88
+ and is never reported, so leave it.
89
+
90
+ | The literal is | Token | Notes |
91
+ | --- | --- | --- |
92
+ | Padding, margin, gap, an offset | `--space-<px>` | Steps: 0, 2, 4, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 64, 96, 128. Round to the nearest step and name the shift. |
93
+ | A stroke width | `--border-width-1`, `-2`, `-4` | Also for `outline`. |
94
+ | A corner | `--radius-sm` through `-4xl`, `--radius-full` | |
95
+ | A shadow | `--shadow-sm` through `-xl` | Replace the whole value, never one offset. |
96
+ | Part of a `calc()` | The token inside the calc | `calc(var(--space-64) * -2 + var(--space-8))` |
97
+
98
+ While in the file, motion values take `--duration-*` and `--ease-*` even
99
+ though no rule reports them, and a `blur()` takes `--blur-*`.
100
+
101
+ ## Every other rule
102
+
103
+ | Rule | Fix |
104
+ | --- | --- |
105
+ | `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. |
106
+ | `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. |
107
+ | `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**. |
108
+ | `unknown-prop` | The component drops it at runtime. Read its `interface Props` in `node_modules/@motion-proto/live-tokens/src/system/components/<Name>.svelte` and either map it to a declared prop or delete it. A `class` on a component that declares none does nothing. |
109
+ | `unknown-prop-value` | Pick a value from the union the message lists. |
110
+ | `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. |
111
+ | `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. |
112
+ | `missing-source` | Add `source: 'src/...'` to the route entry so Page Source can open it. |
113
+ | `reserved-route` | Move the route out of `/live-tokens/*`; the package owns that namespace. |
114
+ | `deep-import` | Import from `@motion-proto/live-tokens` or `/component-editor` or `/components/<Name>.svelte`, never from `/src/`. |
115
+ | `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**. |
116
+ | `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`. |
117
+ | `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. |
118
+ | `missing-registration`, `missing-file`, `missing-root-block` | The component is not wired the way the recipe in **live-tokens-create-component** wires it. |
119
+
120
+ ## Report
121
+
122
+ Say what changed by rule, one line per rule with the count and any visible
123
+ shift. Say what was left and why, with the config entry if the user chose to
124
+ lower a severity. End with the two commands and their exit codes.
125
+
126
+ ## Verify
127
+
128
+ Open `/live-tokens/editor` in dev and change a surface colour and a spacing
129
+ step. Every file the loop touched should repaint. One that does not still
130
+ holds a literal the checker cannot see, which is worth reporting as a gap in
131
+ the checker rather than patching around.
@@ -11,7 +11,7 @@ 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
15
 
16
16
  ## Action family: Button vs IconButton
17
17
 
@@ -42,6 +42,7 @@ All four pick one option from a set. The right one depends on **option count**,
42
42
 
43
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
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
+ - `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
46
  - **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
47
  - 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
48
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,212 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.69.0 — Every value reads a token, and the build says so
4
+
5
+ ### Changed
6
+
7
+ - **SideNavigation's panel widths read the spacing scale.** `16rem` and `3rem`
8
+ were the only shipped defaults with no token behind them; both now derive
9
+ from the largest step, `calc(var(--space-64) * 4)` and
10
+ `calc(var(--space-64) * 0.75)`. Values are unchanged.
11
+
12
+ - **The starter `site.css` is fully tokenized.** A paragraph margin and three
13
+ rule and blockquote strokes were px literals; they read `--space-16` and
14
+ `--border-width-*` now, so a scaffolded project starts clean under
15
+ `check-page --strict`. The paragraph margin moves from 14px to 16px.
16
+
17
+ - **TabBar's active tab and Button's inline-code badge are tints, not scrims.**
18
+ Both washed a surface rather than dimming what sat behind it, and only read a
19
+ scrim because no tint family existed. **They restyle**: a 38% near-black wash
20
+ becomes a 10% white one, so each reads lighter and softer. A theme that pointed
21
+ the TabBar alias somewhere else keeps its choice.
22
+
23
+ - **Optional-interaction gates take `-enabled`, not a state word.**
24
+ `--card-hover-border-active`, `--card-hover-shadow-active`,
25
+ `--image-zoom-hover`, and `--image-grow-hover` read as state-after-property and
26
+ failed `check-component`, which meant the documented gate pattern was one a
27
+ consumer could not use. A gate is not a state: it says whether the interaction
28
+ is on at all. Values are unchanged.
29
+
30
+ - **Button's shimmer and ImageLightbox's tile fit are declared intrinsics.**
31
+ Both were bare keywords with no theme token and no declaration, which is the
32
+ thing `intrinsics` exists to record. `--button-shimmer` now defaults to
33
+ `var(--shimmer-on)`, the token that was already there for it.
34
+
35
+ - **`--hover-*` is now `--tint-*`, and it has a baseline for the first time.** A
36
+ state is a segment of a property name (`--button-outline-hover-surface`), not
37
+ a token of its own, so the three stops are named for what they are: a tint
38
+ shades the surface it sits on. Values are unchanged.
39
+
40
+ The theme engine had always emitted these stops, but `tokens.css` never
41
+ declared them, so they resolved to nothing until a theme was adopted. That is
42
+ why `var(--hover)` painted no pressed state on a fresh install. `--tint-*` is
43
+ baselined, and that half of the migration is additive, so it auto-applies.
44
+
45
+ - **`--overlay-*` is now `--scrim-*`.** A scrim is a translucent layer that dims
46
+ what sits behind it, which is what a dialog draws over the page. The old name
47
+ had spread to cover surface tints as well, which are the opposite operation,
48
+ and it collided with `backdrop`, the exported concept for what paints behind
49
+ an element and which way it leans. Each name now means one thing. Values are
50
+ unchanged, so nothing repaints.
51
+
52
+ `npx live-tokens migrate` renames the three tokens in a vendored `tokens.css`.
53
+ Token names are public API, so the migration is breaking and never
54
+ auto-applies. Saved themes and component configs migrate on load.
55
+
56
+ Dialog's part follows its token: `--dialog-overlay-surface` is
57
+ `--dialog-scrim-surface`, and the editor labels it "scrim color" rather than
58
+ the "backdrop color" that named a third thing again. The editor's Overlays
59
+ section is now Washes, holding Scrims and Tints.
60
+
61
+ ### Added
62
+
63
+ - **The shipped catalogue is now the component contract's fixture.**
64
+ `check-component` reported 109 errors across all 26 shipped components, and
65
+ none of them were defects in the components: the checker's suffix list had
66
+ simply drifted narrower than the catalogue it governs, rejecting `-accent`,
67
+ `-title`, `-margin`, `-easing` and a dozen more names our own components use.
68
+ A test now runs the full contract over every registered component and requires
69
+ zero errors, so the rule and the components can never disagree again.
70
+
71
+ The suffix vocabulary moved to `KIND_RULES` in the editor's `aliasKinds.ts`.
72
+ The picker, the `adjust` CLI, `check-component`, and `check:skills` all read
73
+ that one table, so a name the checker accepts always has a control behind it.
74
+
75
+ Three rules got more accurate along the way. A token the editor declares in
76
+ `intrinsics` is exempt from the suffix check, because a structural keyword is
77
+ not a themeable value. Membership in the package's own `builtInRegistry`
78
+ counts as registration. And a component may prefix its tokens with the
79
+ hyphenated form of its id, which is what CornerBadge has always done.
80
+
81
+ - **An optional hover tint on Button, IconButton, TabBar, SegmentedControl,
82
+ MenuSelect, and SideNavigation.** One stop for the whole component rather than
83
+ one hover surface per variant: Button and IconButton carried 36 hover tokens
84
+ each, and every one was tuned by hand. `--<id>-hover-tint` aliases a `--tint-*`
85
+ stop, and `--<id>-hover-tint-enabled` gates it.
86
+
87
+ Off by default, so nothing changes until a project turns it on. The tint rides
88
+ as a `background-image` over the hover rule's own `background-color`, so it
89
+ needs no pseudo-element.
90
+
91
+ A **tint layer** switch in the component editor's hover state turns it on and
92
+ points every hover surface at its own base surface, so hover is the tint alone
93
+ rather than a swap wearing a wash. The hover-surface rows grey out while it is
94
+ on, since they no longer change anything. Switching it off clears those
95
+ overrides, returning each surface to its shipped default.
96
+
97
+ Switching it on reveals a **tint color** row beside it. It starts on `--tint`,
98
+ the theme's middle tint stop, and takes any colour token with an alpha, like
99
+ every other colour row. A `-tint` suffix now resolves to the surface picker
100
+ rather than falling through, so the row offers the whole palette, not only the
101
+ tint stops.
102
+
103
+ Per instance, the `hoverTint` prop overrides the global default (`undefined`
104
+ inherits, `true` and `false` force).
105
+
106
+ - **`live-tokens check-page` validates a page against the build-page
107
+ contract.** It fails on a component outside the catalogue, a deep import into
108
+ package internals, a `var()` naming a token that does not exist, a colour
109
+ literal, a route under the reserved `/live-tokens/*` namespace, and `site.css`
110
+ imported from `main.ts`, a prop a shipped component does not declare, and a
111
+ variant or size outside that prop's union. It warns on a px or rem literal
112
+ in spacing, stroke, radius, or shadow, a hardcoded page-grid count of four
113
+ columns or more, an absolute type value (the `font` shorthand included), and
114
+ a route entry with no `source`. Sizing is layout and is never reported.
115
+ Inline `style=` attributes and `style:` directives are read under the same
116
+ rules as the `<style>` block, and a named colour is a literal like any hex.
117
+ Given no paths it checks every page under `src/`.
118
+
119
+ - **Both check commands take `--json`, `--strict`, and per-rule severity
120
+ flags.** `--json` prints findings with a stable `rule` id and line number, so
121
+ a skill can fix one rule at a time and re-run until the exit code is 0.
122
+ `--strict` promotes warnings to errors. `--off=<rule>`, `--warn=<rule>`, and
123
+ `--error=<rule>` change a rule for one run; `"checks": { "rules": { ... } }`
124
+ in `live-tokens.config.json` sets it for the project.
125
+
126
+ - **`check-component` now checks what a default *resolves to*, not just what it
127
+ is named.** A component token names a semantic property and its default is the
128
+ theme token that property reads, which is what makes the component repaint
129
+ when the theme changes. A default reading a token that does not exist is now
130
+ an error, including a `var()` naming a state word rather than the token that
131
+ state should paint. A default with no token behind it is an error unless the
132
+ editor declares it in `intrinsics`; a colour literal in any notation is an
133
+ error; a token-backed default that still carries a px or rem term warns.
134
+ Two more rules read the name alone: `disabled-is-terminal` rejects a token
135
+ that combines `disabled` with `hover`, `focus`, `selected`, `on`, `active`,
136
+ or `checked`, and `phantom-editor-token` rejects an editor row naming a
137
+ token the runtime never declares. Given no id, `check-component` checks
138
+ every component authored under `src/system/components`, and a scaffolded
139
+ project runs both checkers as `npm run check:design` before every
140
+ `vite build`. `check-component` also finds a shipped component's editor
141
+ beside the other editors, not only next to its runtime.
142
+
143
+ - **`npm run check:pages` runs the page check over this repo under `--strict`
144
+ and is part of `prepublishOnly`.** The unit suite covers the same ground:
145
+ the repo's pages carry no finding at all, and every shipped component
146
+ default resolves to a real token. Both suites also hold a mutation table: a
147
+ clean component and a clean page that pass `--strict`, and one smallest
148
+ break per rule that must fail, so a rule cannot stop firing unnoticed.
149
+
150
+ - **`Slider`, in two variants.** `single` moves one thumb to a value; `range`
151
+ moves two thumbs to a low and a high bound on one track. Both share track,
152
+ fill, and thumb tokens, linked in the editor so an edit to one moves the
153
+ other until deliberately unlinked, with hover and disabled states and a
154
+ label and value readout. It was authored by following
155
+ `live-tokens-create-component` end to end, and `check-component --strict`
156
+ was clean on the first run.
157
+
158
+ - **`live-tokens-fix-findings`, the seventh skill.** The loop for code that
159
+ already exists: run both checkers with `--json`, take the largest group of
160
+ errors first, apply that rule's recipe, re-run, stop at exit 0. It carries
161
+ one recipe per rule id, with colour mapped by role rather than hue and
162
+ geometry by scale, and three refusals: never silence a rule to pass, never
163
+ mint a token, never shift the look without saying so. Its first run, on the
164
+ package's own demo site, took three rounds and ended clean.
165
+
166
+ ### Fixed
167
+
168
+ - **The tint layer switch was unreachable in four editors.** SegmentedControl,
169
+ TabBar, MenuSelect, and SideNavigation gated the row on a state named
170
+ `hover`, and their hover states are `hover option`, `hover tab`,
171
+ `hover item`, and `<Part> / Hover`, so the switch and the tint colour row
172
+ never rendered. Each condition now names the state the editor has. The
173
+ render contract caught it once it learned to turn a gate on: a gate row
174
+ carries `data-token-variables` and the harness flips a `role="switch"`
175
+ Toggle the way it clicks a checkbox, then exercises the row the gate
176
+ revealed. A selector whose selections are locked carries a `locked` class,
177
+ which the harness skips instead of retrying a chip it can never click.
178
+
179
+ - **`check-page` no longer reports a `var()` fallback as a colour literal.**
180
+ `var(--surface-neutral, #111)` paints the token; the literal only renders
181
+ when the token is missing. Against the package's own demo site this was 33
182
+ of 39 errors.
183
+
184
+ - **`check-page` reads a custom property with a digit in its name as one
185
+ declaration.** `--heading-2xl: 1.875rem` was parsed as the property `xl`
186
+ and reported as a raw dimension. The project's own `tokens.css` and the
187
+ generated token files are also no longer discovered as pages.
188
+
189
+ - **Both checkers read a `:global(:root)` block the same way.** They had two
190
+ extractors, one of which stopped at the first `}`, so a nested at-rule or
191
+ SCSS block truncated the block. One brace-balanced extractor is shared.
192
+
193
+ - **An `intrinsics` array on one line still exempts its token.** The
194
+ exemption required the closing `];` on its own line.
195
+
196
+ - **A persisted editor session from before the rename no longer breaks the
197
+ renderer.** `hydrate` shallow-merges persisted state, so a `washes` (or
198
+ `overlays`) slice saved by an older build replaced the current one wholesale
199
+ and arrived without its tint stops, throwing `w.tints is not iterable` out of
200
+ the render path on load. Hydration now reshapes the slice: it carries the
201
+ saved stops across under their new names and falls back to the shipped
202
+ defaults for anything unusable.
203
+
204
+ - **The outline Button and IconButton pressed state had no colour.** Both read
205
+ `var(--hover)`, which names nothing: a state is a segment of a semantic
206
+ property name (`--button-outline-hover-surface`), never a token of its own.
207
+ They read `--surface-neutral-low` now, one step along the scale from their
208
+ hover surface. `check-component` catches this class of mistake by name.
209
+
3
210
  ## 0.68.1 — A look is a Theme, or it's a sketch style
4
211
 
5
212
  ### Changed
package/README.md CHANGED
@@ -11,7 +11,7 @@ The editor is dev-only. Production builds get plain CSS variables and the compon
11
11
  ## Features
12
12
 
13
13
  - **Live token editing.** Colors, typography, spacing, radii, shadows, motion, palettes, and gradients. Every input writes a CSS variable, so the page repaints with no reload and no build step.
14
- - **Live component editing.** 25 shipped Svelte components (Button, IconButton, Input, Card, Dialog, Badge, Callout, Table, Tooltip, Toggle, TabBar, SegmentedControl, RadioButton, MenuSelect, ProgressBar, CornerBadge, SectionDivider, CollapsibleSection, Notification, Image, ImageLightbox, CodeSnippet, SideNavigation, Panel, InlineEditActions) declare their design-token aliases in a `:global(:root)` block. Rewire an alias from the component's editor and it updates everywhere that component is used, on your real pages.
14
+ - **Live component editing.** 26 shipped Svelte components (Button, IconButton, Input, Card, Dialog, Badge, Callout, Table, Tooltip, Toggle, TabBar, SegmentedControl, RadioButton, MenuSelect, ProgressBar, CornerBadge, SectionDivider, CollapsibleSection, Notification, Image, ImageLightbox, CodeSnippet, SideNavigation, Panel, InlineEditActions) declare their design-token aliases in a `:global(:root)` block. Rewire an alias from the component's editor and it updates everywhere that component is used, on your real pages.
15
15
  - **Four dev-only routes.** `/live-tokens/editor` for tokens, `/live-tokens/colors` for palettes, `/live-tokens/components` for per-component aliases, `/live-tokens/docs` for the user guide.
16
16
  - **Editor overlay.** Pins to the top right of every dev page and opens the editor in a side panel or floating window, so you edit on the page you are styling. Its "Page Source" button opens the current page's `.svelte` file in VS Code.
17
17
  - **Themes.** A theme is a whole look in one file: colors and type plus a config for every component, stored by value. Loading one changes a single pointer file, and nothing your site ships changes until you Adopt. Export a theme and import it into another project to restore the look in one step.
@@ -323,7 +323,8 @@ npx @motion-proto/live-tokens <command>
323
323
  |---|---|
324
324
  | `create <dir> [--force]` | Scaffold a new Svelte + Vite app wired up with live-tokens. |
325
325
  | `setup-claude [--force]` | Install the bundled Claude Code skills into `./.claude/skills/`. |
326
- | `check-component <id>` | Validate a component's runtime, editor, and registration against the authoring contract. |
326
+ | `check-component [id]` | Validate a component's runtime, editor, and registration against the authoring contract; with no id, every component authored under `src/system/components`. |
327
+ | `check-page [paths...]` | Validate pages against the build-page contract: catalogue components and their props, theme tokens over literals, route wiring. |
327
328
  | `generate-theme <brief.json> [--no-activate] [--dry-run] [--carry-from <name>]` | Build a full theme from a 10-seed OKLCH brief, enforce AA contrast, write `themes/<slug>.json`, and open it. |
328
329
  | `adjust <ops.json> [--dry-run]` | Move radius, padding, gap, and border-width aliases along their token scales. |
329
330
  | `set-fonts <brief.json> [--dry-run] [--no-verify]` | Bind Google Fonts families to the theme's font stacks, verified against the API. |
@@ -333,7 +334,7 @@ Once installed in a project, the same commands are available as `npx live-tokens
333
334
 
334
335
  ## Claude Code skills
335
336
 
336
- The package bundles six Claude Code skills. They encode the conventions this README cannot carry in full: which component fits a need, how a page is wired, what a valid theme looks like in OKLCH, how two typefaces sit together, and how geometry moves along the token scales. Each triggers from an ordinary request, so there are no slash commands to learn.
337
+ The package bundles seven Claude Code skills. They encode the conventions this README cannot carry in full: which component fits a need, how a page is wired, what a valid theme looks like in OKLCH, how two typefaces sit together, how geometry moves along the token scales, and how an existing page or component is brought back into line with all of that. Each triggers from an ordinary request, so there are no slash commands to learn.
337
338
 
338
339
  ### Install
339
340
 
@@ -397,13 +398,19 @@ Ask for something the catalogue lacks: "author a Rating component", "make my Chi
397
398
 
398
399
  The skill covers the recipe: the runtime `.svelte` file with its `:global(:root)` token block, the editor `.svelte` file exporting `allTokens` and its variant groups, the `registerComponent()` call, and the catalogue entry that keeps `live-tokens-pick-component` current. It carries the naming scheme, the token suffix vocabulary, the state model (component states such as selected and disabled are separate from interaction states such as hover), and the public-imports rule, and points at the shipped `Toggle` in `node_modules` as the worked example. Linked siblings, intrinsics, and the fixed-overlay portal rule sit in reference files the skill reads only when a component needs them.
399
400
 
401
+ ### `live-tokens-fix-findings`
402
+
403
+ Ask for the existing code to catch up: "make check:design pass", "fix the design-system warnings", "replace the hex and pixel values with tokens", "why is check-page failing on the pricing page?".
404
+
405
+ The two checkers report a stable rule id per finding. The skill runs them with `--json`, groups the findings by rule, and carries one fix recipe per rule: a colour literal becomes the token for its role rather than the nearest hue, a spacing literal moves to the nearest `--space-*` step with the shift named, a raw `font-size` becomes a whole text style, a prop the component does not declare is mapped or dropped, and `site.css` moves out of `main.ts` into each page. It re-runs after every rule and stops at exit 0, then reports what changed, what it left and why, and what `--strict` would add. It never silences a rule to pass and never adds a token to `tokens.css` to match a value a page happened to use.
406
+
400
407
  Verify the result:
401
408
 
402
409
  ```bash
403
410
  npx @motion-proto/live-tokens check-component <id>
404
411
  ```
405
412
 
406
- The validator checks the file layout, the `:global(:root)` block, the token-suffix vocabulary, the state-before-property rule, the no-raw-color-defaults rule, the public-imports rule, and the `registerComponent({ id })` call. Exit code 0 means the static contract is met. Use it after Claude generates a component, and as a pre-commit guard on hand-authored ones.
413
+ The validator checks the file layout, the `:global(:root)` block, the token-suffix vocabulary, the state-before-property rule, the public-imports rule, the `registerComponent({ id })` call, and that every default resolves to a theme token rather than a literal. Exit code 0 means the static contract is met. A project scaffolded by `create` runs it, with `check-page`, as `npm run check:design` before every `vite build`, so a component or page that opts out of the theme cannot ship by accident.
407
414
 
408
415
  ## From edit to production
409
416