@motion-proto/live-tokens 0.71.0 → 0.72.1

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 (70) hide show
  1. package/.claude/skills/live-tokens-adjust-geometry/SKILL.md +1 -1
  2. package/.claude/skills/live-tokens-build-page/SKILL.md +3 -3
  3. package/.claude/skills/live-tokens-check-compliance/SKILL.md +18 -40
  4. package/.claude/skills/live-tokens-create-component/SKILL.md +40 -98
  5. package/.claude/skills/live-tokens-create-component/references/contract-tests.md +99 -0
  6. package/.claude/skills/live-tokens-create-component/references/sketch-mode.md +9 -3
  7. package/.claude/skills/live-tokens-create-component/references/token-naming.md +6 -0
  8. package/.claude/skills/live-tokens-fix-findings/SKILL.md +33 -80
  9. package/.claude/skills/live-tokens-generate-theme/SKILL.md +2 -2
  10. package/.claude/skills/live-tokens-pick-component/SKILL.md +3 -3
  11. package/CHANGELOG.md +109 -0
  12. package/README.md +1 -1
  13. package/bin/check-component.mjs +2 -11
  14. package/bin/check-page.mjs +3 -2
  15. package/bin/cli.mjs +2 -1
  16. package/bin/lib/catalogue.mjs +6 -2
  17. package/bin/lib/findings.mjs +17 -0
  18. package/bin/lib/report.mjs +1 -1
  19. package/bin/lib/tokenVocabulary.mjs +20 -0
  20. package/dist-plugin/adjust/index.cjs +33 -6
  21. package/dist-plugin/adjust/index.d.cts +2 -2
  22. package/dist-plugin/adjust/index.d.ts +2 -2
  23. package/dist-plugin/adjust/index.js +7 -6
  24. package/dist-plugin/{chunk-2YNERPXY.js → chunk-RIXO2E55.js} +1 -1
  25. package/dist-plugin/{chunk-RVE3MNKM.js → chunk-W6Y4BWFB.js} +3 -1
  26. package/dist-plugin/{chunk-GPIBU44G.js → chunk-YLCOIGQC.js} +25 -1
  27. package/dist-plugin/{dataPaths-bJTCEO4H.d.ts → dataPaths-BhWzd5cL.d.cts} +4 -0
  28. package/dist-plugin/{dataPaths-bJTCEO4H.d.cts → dataPaths-BhWzd5cL.d.ts} +4 -0
  29. package/dist-plugin/fontPairing/index.cjs +3 -1
  30. package/dist-plugin/fontPairing/index.d.cts +1 -1
  31. package/dist-plugin/fontPairing/index.d.ts +1 -1
  32. package/dist-plugin/fontPairing/index.js +1 -1
  33. package/dist-plugin/generateColorsAndType/index.cjs +28 -2
  34. package/dist-plugin/generateColorsAndType/index.d.cts +1 -1
  35. package/dist-plugin/generateColorsAndType/index.d.ts +1 -1
  36. package/dist-plugin/generateColorsAndType/index.js +2 -2
  37. package/dist-plugin/index.cjs +28 -2
  38. package/dist-plugin/index.d.cts +1 -1
  39. package/dist-plugin/index.d.ts +1 -1
  40. package/dist-plugin/index.js +3 -3
  41. package/dist-plugin/migrateData/index.cjs +28 -2
  42. package/dist-plugin/migrateData/index.d.cts +1 -1
  43. package/dist-plugin/migrateData/index.d.ts +1 -1
  44. package/dist-plugin/migrateData/index.js +3 -3
  45. package/dist-plugin/tokensCssMigrations/index.cjs +3 -1
  46. package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
  47. package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
  48. package/dist-plugin/tokensCssMigrations/index.js +1 -1
  49. package/package.json +5 -1
  50. package/src/editor/component-editor/SectionDividerEditor.svelte +1 -11
  51. package/src/editor/component-editor/contract.ts +175 -0
  52. package/src/editor/component-editor/scaffolding/StateBlock.svelte +0 -8
  53. package/src/editor/component-editor/scaffolding/TokenLayout.svelte +5 -0
  54. package/src/editor/component-editor/scaffolding/TypeEditor.svelte +0 -21
  55. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +2 -2
  56. package/src/editor/component-editor/scaffolding/types.ts +1 -7
  57. package/src/editor/core/components/aliasKinds.ts +6 -2
  58. package/src/editor/core/themes/migrations/2026-09-02-sectiondivider-drop-title-outline.ts +39 -0
  59. package/src/editor/core/themes/migrations/index.ts +2 -0
  60. package/src/live-tokens/data/themes/autumn.json +1 -7
  61. package/src/live-tokens/data/themes/halloween.json +1 -7
  62. package/src/live-tokens/data/themes/midnight-study.json +1 -7
  63. package/src/live-tokens/data/themes/ocean.json +1 -7
  64. package/src/live-tokens/data/themes/royal-velvet.json +1 -7
  65. package/src/live-tokens/data/themes/sketchy.json +1 -7
  66. package/src/live-tokens/data/themes/spring-meadow.json +1 -7
  67. package/src/live-tokens/data/themes/sunset.json +1 -7
  68. package/src/system/components/SectionDivider.svelte +3 -143
  69. package/src/system/components/FloatingTokenTags.css +0 -284
  70. package/src/system/components/FloatingTokenTags.svelte +0 -570
@@ -3,73 +3,36 @@ name: live-tokens-fix-findings
3
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, or apply what a check reported. Not for the check itself (live-tokens-check-compliance reports and edits nothing), 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
4
  ---
5
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. When the user has
18
- not seen the state of the project yet, `npx live-tokens report --json` is the
19
- reading to start from, and **live-tokens-check-compliance** is the skill that
20
- presents it without editing; this one edits.
21
-
22
- ## Reach the checkers
23
-
24
- ```sh
25
- npx live-tokens check-page --json # every page under src/
26
- npx live-tokens check-component --json # every component authored under src/system/components
27
- ```
28
-
29
- - **Unknown command.** The installed package predates the checkers. Upgrade
30
- `@motion-proto/live-tokens`, then run `npx live-tokens migrate --check` and
31
- apply what it plans with `--write`; `--tokens <path>` names a tokens.css that
32
- sits somewhere other than the four default locations.
33
- - **No `check:design` script.** A project scaffolded by `create` has one. Add
34
- it to any other project's `package.json`:
35
- `"check:design": "live-tokens check-page && live-tokens check-component"`.
36
- Once it passes, gate the build: `"build": "npm run check:design && vite build"`.
37
- - **A file, not the project.** `check-page src/pages/Home.svelte` and
38
- `check-component <id>` scope a run when the user names one thing.
39
-
40
- ## The loop
41
-
42
- 1. Run with `--json`. Each finding carries a stable `rule`, a file, and a line.
43
- 2. Group by rule. Take errors before warnings, and the rule with the most
44
- findings first, because one recipe clears the whole group.
45
- 3. Apply that rule's recipe, below, to every finding in the group.
46
- 4. Run again. New findings can appear as old ones clear: a token you reached
47
- for may not exist, or a moved import may land somewhere the rule now sees.
48
- 5. Stop at exit 0. Then run once with `--strict` and report what it adds, so
49
- the user can decide whether warnings are worth clearing now.
50
-
51
- Three things the loop never does:
52
-
53
- - **Silence a rule to pass.** `--off=<rule>` is for a single run while
54
- working. A severity the project wants changed goes in
55
- `live-tokens.config.json` under `"checks": { "rules": { "<rule>": "warn" } }`,
56
- with the reason in the commit, and only when the user has made that call.
57
- - **Mint a token.** A literal with no token behind it is remapped to the
58
- nearest existing token by role. No new `--surface-*`, `--text-*`, or
59
- `--space-*` is added to `tokens.css` to match a value the page happened to
60
- use. If nothing fits, say so and leave the finding.
61
- - **Change what the page looks like without saying so.** Most remaps land on
62
- the same value. When the nearest token differs, `14px` to `--space-16` or a
63
- 55% black to `--scrim`, name the shift in the report.
6
+ # Fixing the checkers' findings
7
+
8
+ Two checkers hold a project to its design system. `check-page` holds pages: every component comes from the catalogue and is passed only the props it declares, and every value in page CSS is a theme token. `check-component` holds authored components: every token names a semantic property and its default is the theme token that property reads. A page or component that passes repaints when the theme changes. One that fails has opted out of the system silently, and its findings say where.
9
+
10
+ This skill is the loop for code that already exists. When the user has not seen the state of the project yet, **live-tokens-check-compliance** presents `npx live-tokens report` without editing; this skill edits.
11
+
12
+ ## Workflow
13
+
14
+ 1. Run both checkers with `--json`. Each finding carries a stable `rule`, a file, and a line.
15
+ ```sh
16
+ npx live-tokens check-page --json # every page under src/
17
+ npx live-tokens check-component --json # every component authored under src/system/components
18
+ ```
19
+ `check-page src/pages/Home.svelte` and `check-component <id>` scope a run when the user names one thing. Unknown command means the installed package predates the checkers: upgrade `@motion-proto/live-tokens`, then run `npx live-tokens migrate --check` and apply what it plans with `--write` (`--tokens <path>` for a tokens.css outside the four default locations).
20
+ 2. Group by rule. Take errors before warnings, and the rule with the most findings first, because one recipe clears the whole group.
21
+ 3. Apply that rule's recipe to every finding in the group: colour by role, geometry by scale, or the row in the table below. A component outside the catalogue hands off to **live-tokens-pick-component** for the shipped one that fits, or to **live-tokens-create-component**.
22
+ 4. Run again. New findings can appear as old ones clear: a token you reached for may not exist, or a moved import may land where a rule now sees it. Stop at exit 0.
23
+ 5. Run once with `--strict` and report what it adds, so the user can decide whether warnings are worth clearing now. Then report by rule.
24
+
25
+ A project scaffolded by `create` has a `check:design` script. Give any other project one in `package.json`, `"check:design": "live-tokens check-page && live-tokens check-component"`, and once it passes, gate the build: `"build": "npm run check:design && vite build"`.
26
+
27
+ ## Three things the loop never does
28
+
29
+ - **Silence a rule to pass.** `--off=<rule>` is for a single run while working. A severity the project wants changed goes in `live-tokens.config.json` under `"checks": { "rules": { "<rule>": "warn" } }`, with the reason in the commit, and only when the user has made that call.
30
+ - **Mint a token.** A literal with no token behind it is remapped to the nearest existing token by role. No new `--surface-*`, `--text-*`, or `--space-*` is added to `tokens.css` to match a value the page happened to use. If nothing fits, say so and leave the finding.
31
+ - **Change what the page looks like without saying so.** Most remaps land on the same value. When the nearest token differs, `14px` to `--space-16` or a 55% black to `--scrim`, name the shift in the report.
64
32
 
65
33
  ## Colour by role, never by hue
66
34
 
67
- `color-literal` is the finding that takes judgement. The replacement is the
68
- token for what the colour *does*, not the token that happens to be closest in
69
- hue, because the theme will move every role together and the page must move
70
- with it. `npx live-tokens tokens --family surface` prints a family's names and
71
- values (`text`, `border`, `scrim`, `tint` likewise; `--json` for data); the
72
- families are fixed.
35
+ `color-literal` is the finding that takes judgement. The replacement is the token for what the colour *does*, not the token nearest in hue, because the theme moves every role together and the page must move with it. `npx live-tokens tokens --family surface` prints a family's names and values (`text`, `border`, `scrim`, `tint` likewise; `--json` for data); the families are fixed.
73
36
 
74
37
  | The literal is | Token family | Notes |
75
38
  | --- | --- | --- |
@@ -82,15 +45,11 @@ families are fixed.
82
45
  | Fully transparent | `--color-transparent` | Never `transparent` inside a component default. |
83
46
  | A gradient | `--gradient-*` | Or compose one from surface tokens. |
84
47
 
85
- A `var(--x, #fff)` fallback is not a finding. A named colour is: `white` and
86
- `rebeccapurple` are literals like any hex.
48
+ A `var(--x, #fff)` fallback is not a finding. A named colour is: `white` and `rebeccapurple` are literals like any hex.
87
49
 
88
50
  ## Geometry by scale
89
51
 
90
- `dimension-literal` fires only on the geometry the theme owns: padding,
91
- margin, gap, border and outline widths, inset offsets, radius, and shadow.
92
- Sizing (a hero's height, a max content width, a `minmax()` floor) is layout
93
- and is never reported, so leave it.
52
+ `dimension-literal` fires only on the geometry the theme owns: padding, margin, gap, border and outline widths, inset offsets, radius, and shadow. Sizing (a hero's height, a max content width, a `minmax()` floor) is layout and is never reported, so leave it.
94
53
 
95
54
  | The literal is | Token | Notes |
96
55
  | --- | --- | --- |
@@ -100,8 +59,7 @@ and is never reported, so leave it.
100
59
  | A shadow | `--shadow-sm` through `-xl` | Replace the whole value, never one offset. |
101
60
  | Part of a `calc()` | The token inside the calc | `calc(var(--space-64) * -2 + var(--space-8))` |
102
61
 
103
- While in the file, motion values take `--duration-*` and `--ease-*` even
104
- though no rule reports them, and a `blur()` takes `--blur-*`.
62
+ While in the file, motion values take `--duration-*` and `--ease-*` even though no rule reports them, and a `blur()` takes `--blur-*`.
105
63
 
106
64
  ## Every other rule
107
65
 
@@ -120,17 +78,12 @@ though no rule reports them, and a `blur()` takes `--blur-*`.
120
78
  | `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**. |
121
79
  | `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`. |
122
80
  | `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. |
123
- | `missing-registration`, `missing-file`, `missing-root-block` | The component is not wired the way the recipe in **live-tokens-create-component** wires it. |
81
+ | `invalid-id`, `missing-file`, `missing-root-block`, `no-tokens`, `missing-component-const`, `missing-all-tokens`, `missing-registration` | The component is not wired the way the recipe in **live-tokens-create-component** wires it: a lowercase id, a runtime file with a `:global(:root)` block, an editor file exporting `component` and `allTokens`, and a `bootLiveTokens` entry. |
124
82
 
125
83
  ## Report
126
84
 
127
- Say what changed by rule, one line per rule with the count and any visible
128
- shift. Say what was left and why, with the config entry if the user chose to
129
- lower a severity. End with the two commands and their exit codes.
85
+ Say what changed by rule, one line per rule with the count and any visible shift. Say what was left and why, with the config entry if the user chose to lower a severity. End with the two commands and their exit codes.
130
86
 
131
87
  ## Verify
132
88
 
133
- Open `/live-tokens/editor` in dev and change a surface colour and a spacing
134
- step. Every file the loop touched should repaint. One that does not still
135
- holds a literal the checker cannot see, which is worth reporting as a gap in
136
- the checker rather than patching around.
89
+ Open `/live-tokens/editor` in dev and change a surface colour and a spacing step. Every file the loop touched should repaint. One that does not still holds a literal the checker cannot see, which is worth reporting as a gap in the checker rather than patching around.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: live-tokens-generate-theme
3
- description: Generate a complete live-tokens theme — color, type, and geometry — from a natural-language brief, delegating the type and geometry halves to its sibling skills. Use whenever the user asks for a theme, look, vibe, brand feel, color scheme, or palette by mood, style, era, season, holiday, or hue; when they name only a color (something red-based, green and gold for St. Patrick's Day); or when they refine an existing theme (warmer, more contrast, calmer). Examples: bright and cheerful, dark and moody, brutalist, mid-century modern, Swiss and minimal, cyberpunk neon, editorial magazine, make it feel like a terminal. Not for a single token (use the editor), type alone (live-tokens-pair-fonts), or geometry alone (live-tokens-adjust-geometry).
3
+ description: Generate a complete live-tokens theme (color, type, and geometry) from a natural-language brief, delegating the type and geometry halves to its sibling skills. Use whenever the user asks for a theme, look, vibe, brand feel, color scheme, or palette by mood, style, era, season, holiday, or hue; when they name only a color (something red-based, green and gold for St. Patrick's Day); or when they refine an existing theme (warmer, more contrast, calmer). Examples: bright and cheerful, dark and moody, brutalist, mid-century modern, Swiss and minimal, cyberpunk neon, editorial magazine, make it feel like a terminal. Not for a single token (use the editor), type alone (live-tokens-pair-fonts), or geometry alone (live-tokens-adjust-geometry).
4
4
  ---
5
5
 
6
6
  # Generating a theme from a brief
@@ -145,7 +145,7 @@ This table is the fallback. When the brief matched an entry in the mood or style
145
145
 
146
146
  One adjective moves one dial. Warmer and cooler rotate hue; calmer and louder move chroma; lighter, darker, and moodier move Canvas L and the scheme; more contrast widens the L gap between Canvas and Brand and takes chroma out of the ground rather than adding it to the garnish. Leave every seed the user did not name alone, because a refinement that re-rolls the whole palette reads as a different theme and loses the thing they liked.
147
147
 
148
- ## What each step writes
148
+ ## Files each step writes
149
149
 
150
150
  Color writes `themes/<slug>.json` and opens it. Type and geometry write the unsaved buffers, which the page already runs. One Save in the editor keeps all three; Adopt ships them. Component aliases and gradients carry forward from the live look into a generated theme; user-tuned gradients survive, stock ones rebuild from the new families.
151
151
 
@@ -63,7 +63,7 @@ All four pick one option from a set. The right one depends on **option count**,
63
63
  | `Dialog` | Modal, blocks page | Confirmations, focused tasks the page can't continue around |
64
64
  | `Panel` | Inline, fixed stage | A demo or preview surface whose height must not reflow |
65
65
 
66
- - Default to `Card`. It's the workhorse. For full-bleed media — cover art, a poster, a chart that reaches its own border — pass `flush` (with `prose={false}`) rather than zeroing its padding tokens from the page.
66
+ - Default to `Card`. It's the workhorse. For full-bleed media (cover art, a poster, a chart that reaches its own border) pass `flush` (with `prose={false}`) rather than zeroing its padding tokens from the page.
67
67
  - Reach for `CollapsibleSection` only when the content is *legitimately secondary* (advanced users open it; most skip). Don't use collapse as a styling choice when the content matters.
68
68
  - `Panel` is a stage, not a content container. It pins its own height so what it shows can resize without moving the page, which is what a component preview or a live example needs and what article content does not. Content goes in `Card`.
69
69
  - **Don't use `Dialog` for routine forms.** Reach for it only when the page cannot meaningfully continue until the user decides (destructive confirmations, payment, sign-in). Routine forms go inline in a `Card`.
@@ -83,7 +83,7 @@ All four pick one option from a set. The right one depends on **option count**,
83
83
  - `Tooltip` is for *what an element means*. **Don't use `Tooltip` as the primary location of important content;** it auto-dismisses and isn't accessible for must-read content.
84
84
  - `Badge` and `CornerBadge` differ only in positioning. `CornerBadge` lives at a `top-right` / `bottom-left` anchor on a parent (notification counts, "NEW" stickers).
85
85
 
86
- ## Display family: what the page shows rather than what it asks
86
+ ## Display family: shown, not asked
87
87
 
88
88
  - `Image` frames a picture in the flow at one of four sizes, with an optional hover zoom. It is the default for any picture the page simply shows.
89
89
  - `ImageLightbox` adds click-to-open at full size and takes an array for a gallery. Use it when the detail is the point (screenshots, artwork, charts that need reading), and not for decoration: it puts a modal behind every picture it wraps.
@@ -108,4 +108,4 @@ All three can express a binary choice. The right one depends on what the choice
108
108
 
109
109
  ---
110
110
 
111
- If nothing in the catalogue fits (a `Slider`, a `DatePicker`, a `Stepper`, a custom widget), author it via **live-tokens-create-component**. **Don't reach for a custom component before checking the catalogue;** a custom component is a maintenance commitment.
111
+ If nothing in the catalogue fits (a `DatePicker`, a `Stepper`, a custom widget), author it via **live-tokens-create-component**. **Don't reach for a custom component before checking the catalogue;** a custom component is a maintenance commitment.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,114 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.72.1 — The compliance skills have a spine
4
+
5
+ ### Changed
6
+
7
+ - **`live-tokens-check-compliance` and `live-tokens-fix-findings` open with a
8
+ numbered workflow**, the same spine the other six skills have: run the
9
+ command, read the result, apply the recipe, re-run, hand off. Both were
10
+ prose-first, so a reader (and the Skill Atlas that draws them) had no steps,
11
+ no gate, and no hand-off to find. fix-findings now also covers the four
12
+ wiring rules its table skipped (`invalid-id`, `no-tokens`,
13
+ `missing-component-const`, `missing-all-tokens`).
14
+
15
+ - **`live-tokens-create-component` is 189 lines, down from 247.** The
16
+ standalone Toggle walkthrough is folded into the worked-examples list, the
17
+ verification checklist no longer restates the checker step, and the
18
+ registration caveat is shorter. Nothing a consumer needs to author a
19
+ component was removed.
20
+
21
+ - **The sketch reference's inner-part example carries its reserved class.**
22
+ The prose said a drawn part takes its own class and its own five
23
+ `--sketch-*` values; the code under it showed only the CSS, so a part
24
+ authored from the example was left crisp inside a drawn box.
25
+
26
+ ### Fixed
27
+
28
+ - **`live-tokens-build-page` no longer promises a Cmd+G shortcut.** There is
29
+ none; the columns overlay is the vertical-lines button in the overlay's
30
+ header.
31
+
32
+ - **`live-tokens-pick-component` no longer lists `Slider` as a component the
33
+ catalogue lacks.** It shipped in 0.71.0.
34
+
35
+ ## 0.72.0 — The contract a consumer can run
36
+
37
+ ### Added
38
+
39
+ - **The registry contract is a function a consumer can run.** The six
40
+ per-component checks that hold this package's own components — registration
41
+ resolves, schema variables are unique, every editable token is declared in
42
+ the runtime `<style>` and seeded in `default.json`, a declared `minOpacity`
43
+ is honoured, `setComponentAlias` round-trips — are now `checkRegistryEntry`,
44
+ exported from `@motion-proto/live-tokens/component-editor/contract`. It takes
45
+ one registry entry and returns a violation line per failure, so a project
46
+ outside this package holds its own components to the same contract with a
47
+ `describe.each` and one call. Paths resolve against `process.cwd()` and
48
+ `src/live-tokens/data/component-configs`, or the `projectRoot` and
49
+ `componentConfigsDir` passed in. Node-only, hence its own subpath.
50
+ `live-tokens-create-component` carries the test file in
51
+ `references/contract-tests.md`; filtering the registry on
52
+ `origin === 'custom'` is the line that matters, since the shipped entries
53
+ name paths relative to the package root.
54
+
55
+ - **`"checks": { "exclude": [...] }` in `live-tokens.config.json`.** Paths the
56
+ checkers skip when they discover their own targets: a project-relative path,
57
+ a directory covering what is under it. For a file that is not a themed
58
+ surface at all — hand-tuned artwork, vendored CSS — where the only other way
59
+ out was downgrading a rule for the whole project. Naming the file on the
60
+ command line still checks it, so the escape hatch cannot hide a file from
61
+ someone looking straight at it. The dev plugin now also recognises `checks`
62
+ and `componentDirs`, which the CLI has read for some time while the plugin
63
+ warned they were unknown keys.
64
+
65
+ ### Changed (breaking)
66
+
67
+ - **`FloatingTokenTags` is demo artwork, not a shipped component.** It moved to
68
+ `src/demo/`, so it leaves the published package, the component query and the
69
+ count, which now agree with the registry at 26. **Breaking for anyone
70
+ importing `@motion-proto/live-tokens/components/FloatingTokenTags.svelte`;**
71
+ it was only ever the hero animation of the demo, and it never had a
72
+ `:global(:root)` block, an editor, or a registry entry.
73
+
74
+ - **The SectionDivider title outline is removed.** It was the only reason the title was an
75
+ SVG `<text>` behind a `feMorphology` filter rather than an element, and that
76
+ SVG carried a `getBBox()` viewBox, a per-instance `MutationObserver` on the
77
+ document's inline style (filter primitives cannot read a CSS var, so the
78
+ resolved width and colour had to be read back and pushed onto them by hand),
79
+ font-load listeners to re-measure, and a title that was neither selectable nor
80
+ findable. It shipped transparent in the default and in all eight presets, and
81
+ the trap-out it was built for is done by the layout: the `through-label`
82
+ hairlines flank the title in a flex row, so no rule ever runs behind the
83
+ glyphs. The title is now a span that inherits typography like every other
84
+ component's. `--sectiondivider-{lg,md,sm}-title-outline-width` and
85
+ `-title-outline-color` are dropped by a component-config migration; the
86
+ `outlineWidthVariable` / `outlineColorVariable` rows on `TypeGroupConfig` go
87
+ with them, since nothing else declared one.
88
+
89
+ ### Changed
90
+
91
+ - **`registered` is read from the registry for shipped components too.** The
92
+ query assumed a component under `src/system/components` was registered, so
93
+ `live-tokens components` could name one the editor cannot open and
94
+ pick-component does not list. It now reads the package's own
95
+ `builtInRegistry`, the same parse `check-component` uses, which the two now
96
+ share. A component that is discovered but registered nowhere reports
97
+ `registered: false`, prints `(NOT registered)`, and is counted out of the
98
+ catalogue in the summary line.
99
+
100
+ ### Fixed
101
+
102
+ - **A bare `-width`, `-height` or `-size` no longer renders a colour picker.**
103
+ The three sat in `KIND_RULES` under `surface`, so `--widget-panel-width` drew
104
+ a palette while the naming vocabulary documented it as geometry. They are now
105
+ a `length` kind, matched last among the geometry rules so `-border-width`,
106
+ `-divider-height`, `-icon-size` and the rest still claim their token first,
107
+ and drawn with the `--space-*` picker `-gap` uses. The suffix vocabulary is
108
+ unchanged, so no token is renamed and no project has to migrate. `adjust`
109
+ does not take the kind: its ladders are density and shape, and a panel width
110
+ is neither.
111
+
3
112
  ## 0.71.0 — Check this project
4
113
 
5
114
  ### Added
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.** 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.
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, Slider) 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.
@@ -23,7 +23,7 @@ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
23
23
  import { extname, join, relative } from 'node:path';
24
24
  import { hasColorLiteral, hasDimensionLiteral, stripVarFallbacks } from './lib/cssValues.mjs';
25
25
  import { lineOf } from './lib/findings.mjs';
26
- import { PKG_ROOT, declaredCustomProperties, extractGlobalRootBlocks, isContractToken, loadVocabulary } from './lib/tokenVocabulary.mjs';
26
+ import { PKG_ROOT, builtInIds, declaredCustomProperties, extractGlobalRootBlocks, isContractToken, loadVocabulary } from './lib/tokenVocabulary.mjs';
27
27
 
28
28
  export const COMPONENT_RULES = {
29
29
  'invalid-id': 'error',
@@ -71,18 +71,9 @@ function readKnownSuffixes(root) {
71
71
  return [];
72
72
  }
73
73
 
74
- const BUILT_IN_REGISTRY = 'src/editor/component-editor/registry.ts';
75
-
76
74
  /** True when `id` is one of the package's own components. */
77
75
  function isBuiltIn(id) {
78
- for (const base of [PKG_ROOT, process.cwd()]) {
79
- const path = join(base, BUILT_IN_REGISTRY);
80
- if (!existsSync(path)) continue;
81
- const src = readFileSync(path, 'utf8');
82
- const block = src.match(/builtInRegistry[^=]*=\s*Object\.freeze\(\{([\s\S]*?)\n\}\);/);
83
- if (block && new RegExp(`\\bid:\\s*'${id}'`).test(block[1])) return true;
84
- }
85
- return false;
76
+ return builtInIds(process.cwd(), PKG_ROOT).has(id);
86
77
  }
87
78
 
88
79
  // Per-side padding names (`--card-body-padding-top`) are written by the padding
@@ -12,7 +12,7 @@
12
12
 
13
13
  import { existsSync, readFileSync, statSync } from 'node:fs';
14
14
  import { join, relative, resolve, basename } from 'node:path';
15
- import { lineOf } from './lib/findings.mjs';
15
+ import { isExcluded, lineOf } from './lib/findings.mjs';
16
16
  import { blankStrings, hasColorLiteral, hasDimensionLiteral, stripVarFallbacks } from './lib/cssValues.mjs';
17
17
  import { isContractToken, loadVocabulary, walk } from './lib/tokenVocabulary.mjs';
18
18
  import { resolveTokensCssPath } from './migrate.mjs';
@@ -382,6 +382,7 @@ export function discoverPages(root) {
382
382
  return walk(src, ['.svelte', '.css', '.ts', '.js']).filter((f) => {
383
383
  const rel = relative(root, f);
384
384
  if (NOT_PAGES.some((d) => rel.startsWith(`${d}/`))) return false;
385
+ if (isExcluded(rel, root)) return false;
385
386
  if (f === tokensCss || TOKEN_SOURCES.includes(basename(f))) return false;
386
387
  if (/\.(test|spec)\.[tj]s$/.test(rel)) return false;
387
388
  if (rel.endsWith('.ts') || rel.endsWith('.js')) return /main\.(ts|js)$/.test(rel);
@@ -396,7 +397,7 @@ export function checkPages(targets, { root = process.cwd(), vocabulary } = {}) {
396
397
  const full = resolve(root, t);
397
398
  if (!existsSync(full)) continue;
398
399
  if (statSync(full).isDirectory()) {
399
- files.push(...walk(full, ['.svelte', '.css']));
400
+ files.push(...walk(full, ['.svelte', '.css']).filter((f) => !isExcluded(relative(root, f), root)));
400
401
  } else {
401
402
  files.push(full);
402
403
  }
package/bin/cli.mjs CHANGED
@@ -75,7 +75,8 @@ Both check commands accept:
75
75
  --strict Treat warnings as errors
76
76
  --off=<rule,...> Silence rules; --warn=/--error= change severity
77
77
  (or set "checks": { "rules": {...} } in
78
- live-tokens.config.json)
78
+ live-tokens.config.json; "checks": { "exclude":
79
+ [...] } drops paths from discovery entirely)
79
80
  generate-theme <brief.json> [--no-activate] [--dry-run] [--carry-from <name>]
80
81
  Build a full theme from a 10-seed OKLCH brief
81
82
  (see the live-tokens-generate-theme skill),
@@ -44,7 +44,7 @@ export function describeComponents(vocab, { root = process.cwd() } = {}) {
44
44
  name: entry.name,
45
45
  origin: entry.origin,
46
46
  file: relative(root, entry.file),
47
- registered: entry.origin === 'shipped' || vocab.registered.has(entry.id),
47
+ registered: vocab.builtIn.has(entry.id) || vocab.registered.has(entry.id),
48
48
  description: descriptionOf(source),
49
49
  variants: entry.props?.enums.get('variant') ? [...entry.props.enums.get('variant')] : [],
50
50
  props,
@@ -99,7 +99,11 @@ export function formatComponents(list, { id } = {}) {
99
99
  if (c.description) lines.push(`${''.padEnd(29)} ${c.description}`);
100
100
  }
101
101
  lines.push('');
102
- lines.push(`${list.length} component(s). \`live-tokens components <id>\` prints one with its props and tokens.`);
102
+ const unregistered = list.filter((c) => !c.registered).length;
103
+ lines.push(
104
+ `${list.length} component(s)${unregistered ? `, ${unregistered} not registered and so not in the catalogue` : ''}. ` +
105
+ '`live-tokens components <id>` prints one with its props and tokens.',
106
+ );
103
107
  return lines.join('\n');
104
108
  }
105
109
 
@@ -30,6 +30,23 @@ export function readChecksConfig(root) {
30
30
  }
31
31
  }
32
32
 
33
+ /**
34
+ * `checks.exclude` in live-tokens.config.json: paths the checkers do not read
35
+ * when they discover their own targets. Each entry is a project-relative path;
36
+ * a directory entry covers everything under it. For a file that is not a themed
37
+ * surface — hand-tuned artwork, vendored CSS — where the alternative is
38
+ * downgrading a rule for the whole project. Naming the file explicitly on the
39
+ * command line still checks it.
40
+ */
41
+ export function isExcluded(relPath, root) {
42
+ const list = readChecksConfig(root).exclude;
43
+ if (!Array.isArray(list)) return false;
44
+ return list.some((entry) => {
45
+ const e = String(entry).replace(/\/+$/, '');
46
+ return relPath === e || relPath.startsWith(`${e}/`);
47
+ });
48
+ }
49
+
33
50
  /**
34
51
  * Parse `--off=a,b --warn=c --error=d --strict --json` out of argv.
35
52
  * Unrecognised flags are returned in `rest` for the caller to handle.
@@ -61,7 +61,7 @@ export function buildReport(vocab, { root = process.cwd() } = {}) {
61
61
  name: c.name,
62
62
  origin: c.origin,
63
63
  file: relative(root, c.file),
64
- registered: c.origin === 'shipped' || vocab.registered.has(c.id),
64
+ registered: vocab.builtIn.has(c.id) || vocab.registered.has(c.id),
65
65
  described: /^\s*<!--[\s\S]*?-->/.test(source),
66
66
  tokens: c.tokens.size,
67
67
  unread: unreadTokens(source, c.tokens.keys()),
@@ -164,6 +164,24 @@ function componentFiles(roots) {
164
164
  return files;
165
165
  }
166
166
 
167
+ /** The package's own component ids, read from the frozen registry that declares
168
+ them. A shipped component is registered by the package rather than by the
169
+ project, so it never appears in the project's own `registerComponent` scan. */
170
+ const BUILT_IN_REGISTRY = 'src/editor/component-editor/registry.ts';
171
+
172
+ export function builtInIds(root = process.cwd(), pkgRoot = PKG_ROOT) {
173
+ const ids = new Set();
174
+ for (const base of [pkgRoot, root]) {
175
+ const path = join(base, BUILT_IN_REGISTRY);
176
+ if (!existsSync(path)) continue;
177
+ const block = readFileSync(path, 'utf8')
178
+ .match(/builtInRegistry[^=]*=\s*Object\.freeze\(\{([\s\S]*?)\n\}\);/);
179
+ if (!block) continue;
180
+ for (const m of block[1].matchAll(/\bid:\s*'([a-z][a-z0-9]*)'/g)) ids.add(m[1]);
181
+ }
182
+ return ids;
183
+ }
184
+
167
185
  function registeredIds(root) {
168
186
  const ids = new Set();
169
187
  for (const file of walk(join(root, 'src'), ['.ts', '.js', '.mjs', '.svelte'])) {
@@ -223,12 +241,14 @@ export function loadVocabulary({ root = process.cwd(), pkgRoot = PKG_ROOT } = {}
223
241
  });
224
242
  }
225
243
  const registered = registeredIds(root);
244
+ const builtIn = builtInIds(root, pkgRoot);
226
245
 
227
246
  return {
228
247
  themeTokens,
229
248
  componentTokens,
230
249
  components,
231
250
  registered,
251
+ builtIn,
232
252
  tokensCssPath,
233
253
  /** True when `name` resolves to something real at runtime. */
234
254
  knows(name) {
@@ -81,6 +81,10 @@ var KIND_RULES = [
81
81
  prefix: ["--border-width-"]
82
82
  },
83
83
  { kind: "border", suffix: ["-border"], prefix: ["--border-"] },
84
+ // A dimension with no more specific name behind it — a panel's width, an
85
+ // avatar's size. Last of the geometry rules, so every `-border-width`,
86
+ // `-divider-height`, `-icon-size` and the rest claim their token first.
87
+ { kind: "length", suffix: ["-width", "-height", "-size"] },
84
88
  // Fills. A tint is a wash over a surface, so it takes the surface picker: the
85
89
  // full palette with an alpha, not just the tint stops it defaults to.
86
90
  {
@@ -95,10 +99,7 @@ var KIND_RULES = [
95
99
  "-accent",
96
100
  "-color",
97
101
  "-tint",
98
- "-opacity",
99
- "-width",
100
- "-height",
101
- "-size"
102
+ "-opacity"
102
103
  ],
103
104
  prefix: ["--surface-", "--tint", "--color-"]
104
105
  }
@@ -281,7 +282,9 @@ var KNOWN_CONFIG_KEYS = /* @__PURE__ */ new Set([
281
282
  "componentConfigsDir",
282
283
  "themesDir",
283
284
  "manifestsDir",
284
- "tokensCssPath"
285
+ "tokensCssPath",
286
+ "componentDirs",
287
+ "checks"
285
288
  ]);
286
289
  var cached = null;
287
290
  function readLiveTokensConfig() {
@@ -1125,6 +1128,29 @@ var componentMigration_2026_09_01_gateSuffixEnabled = {
1125
1128
  }
1126
1129
  };
1127
1130
 
1131
+ // src/editor/core/themes/migrations/2026-09-02-sectiondivider-drop-title-outline.ts
1132
+ var DROPPED2 = new Set(
1133
+ ["lg", "md", "sm"].flatMap((v) => [
1134
+ `--sectiondivider-${v}-title-outline-width`,
1135
+ `--sectiondivider-${v}-title-outline-color`
1136
+ ])
1137
+ );
1138
+ var componentMigration_2026_09_02_sectiondividerDropTitleOutline = {
1139
+ id: "2026-09-02-sectiondivider-drop-title-outline",
1140
+ fromVersion: 25,
1141
+ toVersion: 26,
1142
+ appliesTo: "component-config",
1143
+ apply(rawVars, meta) {
1144
+ if (meta.component !== "sectiondivider") return { ...rawVars };
1145
+ const out = {};
1146
+ for (const [key, value] of Object.entries(rawVars)) {
1147
+ if (DROPPED2.has(key)) continue;
1148
+ out[key] = value;
1149
+ }
1150
+ return out;
1151
+ }
1152
+ };
1153
+
1128
1154
  // src/editor/core/themes/migrations/index.ts
1129
1155
  var MIGRATIONS = [
1130
1156
  colorsAndTypeMigration_2026_04_24_legacyKeysAndBgToCanvas,
@@ -1158,7 +1184,8 @@ var MIGRATIONS = [
1158
1184
  colorsAndTypeMigration_2026_09_01_tintRename,
1159
1185
  componentMigration_2026_09_01_tintRename,
1160
1186
  componentMigration_2026_09_01_tabbarActiveTint,
1161
- componentMigration_2026_09_01_gateSuffixEnabled
1187
+ componentMigration_2026_09_01_gateSuffixEnabled,
1188
+ componentMigration_2026_09_02_sectiondividerDropTitleOutline
1162
1189
  ];
1163
1190
  function countFor(kind) {
1164
1191
  return MIGRATIONS.filter((m) => m.appliesTo === kind).length;
@@ -1,5 +1,5 @@
1
1
  import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-DSV3Zisf.cjs';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-bJTCEO4H.cjs';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-BhWzd5cL.cjs';
3
3
  export { C as CURRENT_COMPONENT_SCHEMA_VERSION } from '../index-4N-Orzzi.cjs';
4
4
 
5
5
  type AdjustKind = 'radius' | 'padding' | 'gap' | 'border-width';
@@ -37,7 +37,7 @@ interface AdjustResult {
37
37
  }
38
38
  declare function adjustAliases(configs: Record<string, ComponentConfig>, ops: readonly AdjustOp[], now: string): AdjustResult;
39
39
 
40
- type TokenKind = 'surface' | 'border' | 'border-width' | 'radius' | 'divider-width' | 'divider-height' | 'divider-inset' | 'dot-size' | 'blur' | 'scale' | 'shadow' | 'font-family' | 'font-weight' | 'font-size' | 'line-height' | 'letter-spacing' | 'padding' | 'gap' | 'duration' | 'easing' | 'text-color';
40
+ type TokenKind = 'surface' | 'border' | 'border-width' | 'radius' | 'divider-width' | 'divider-height' | 'divider-inset' | 'dot-size' | 'length' | 'blur' | 'scale' | 'shadow' | 'font-family' | 'font-weight' | 'font-size' | 'line-height' | 'letter-spacing' | 'padding' | 'gap' | 'duration' | 'easing' | 'text-color';
41
41
  /** Side-aware kind test. Per-side paddings (`--card-default-body-padding-top`)
42
42
  are written by UIPaddingSelector and never declared as editor tokens, so
43
43
  `rawKind` never meets one; the CLI does, and they belong with their parent. */
@@ -1,5 +1,5 @@
1
1
  import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-DSV3Zisf.js';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-bJTCEO4H.js';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-BhWzd5cL.js';
3
3
  export { C as CURRENT_COMPONENT_SCHEMA_VERSION } from '../index-4N-Orzzi.js';
4
4
 
5
5
  type AdjustKind = 'radius' | 'padding' | 'gap' | 'border-width';
@@ -37,7 +37,7 @@ interface AdjustResult {
37
37
  }
38
38
  declare function adjustAliases(configs: Record<string, ComponentConfig>, ops: readonly AdjustOp[], now: string): AdjustResult;
39
39
 
40
- type TokenKind = 'surface' | 'border' | 'border-width' | 'radius' | 'divider-width' | 'divider-height' | 'divider-inset' | 'dot-size' | 'blur' | 'scale' | 'shadow' | 'font-family' | 'font-weight' | 'font-size' | 'line-height' | 'letter-spacing' | 'padding' | 'gap' | 'duration' | 'easing' | 'text-color';
40
+ type TokenKind = 'surface' | 'border' | 'border-width' | 'radius' | 'divider-width' | 'divider-height' | 'divider-inset' | 'dot-size' | 'length' | 'blur' | 'scale' | 'shadow' | 'font-family' | 'font-weight' | 'font-size' | 'line-height' | 'letter-spacing' | 'padding' | 'gap' | 'duration' | 'easing' | 'text-color';
41
41
  /** Side-aware kind test. Per-side paddings (`--card-default-body-padding-top`)
42
42
  are written by UIPaddingSelector and never declared as editor tokens, so
43
43
  `rawKind` never meets one; the CLI does, and they belong with their parent. */
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  CURRENT_COMPONENT_SCHEMA_VERSION
3
- } from "../chunk-GPIBU44G.js";
3
+ } from "../chunk-YLCOIGQC.js";
4
4
  import {
5
5
  readLiveTokensConfig,
6
6
  resolveDataDirs
7
- } from "../chunk-RVE3MNKM.js";
7
+ } from "../chunk-W6Y4BWFB.js";
8
8
 
9
9
  // src/editor/core/components/aliasKinds.ts
10
10
  var KIND_RULES = [
@@ -49,6 +49,10 @@ var KIND_RULES = [
49
49
  prefix: ["--border-width-"]
50
50
  },
51
51
  { kind: "border", suffix: ["-border"], prefix: ["--border-"] },
52
+ // A dimension with no more specific name behind it — a panel's width, an
53
+ // avatar's size. Last of the geometry rules, so every `-border-width`,
54
+ // `-divider-height`, `-icon-size` and the rest claim their token first.
55
+ { kind: "length", suffix: ["-width", "-height", "-size"] },
52
56
  // Fills. A tint is a wash over a surface, so it takes the surface picker: the
53
57
  // full palette with an alpha, not just the tint stops it defaults to.
54
58
  {
@@ -63,10 +67,7 @@ var KIND_RULES = [
63
67
  "-accent",
64
68
  "-color",
65
69
  "-tint",
66
- "-opacity",
67
- "-width",
68
- "-height",
69
- "-size"
70
+ "-opacity"
70
71
  ],
71
72
  prefix: ["--surface-", "--tint", "--color-"]
72
73
  }