@motion-proto/live-tokens 0.70.0 → 0.72.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.
- package/.claude/skills/live-tokens-check-compliance/SKILL.md +78 -0
- package/.claude/skills/live-tokens-create-component/SKILL.md +2 -2
- package/.claude/skills/live-tokens-create-component/references/contract-tests.md +99 -0
- package/.claude/skills/live-tokens-create-component/references/sketch-mode.md +1 -2
- package/.claude/skills/live-tokens-create-component/references/token-naming.md +6 -0
- package/.claude/skills/live-tokens-fix-findings/SKILL.md +5 -2
- package/CHANGELOG.md +100 -0
- package/README.md +9 -2
- package/bin/check-component.mjs +2 -11
- package/bin/check-page.mjs +4 -3
- package/bin/cli.mjs +29 -1
- package/bin/lib/catalogue.mjs +6 -2
- package/bin/lib/findings.mjs +17 -0
- package/bin/lib/report.mjs +158 -0
- package/bin/lib/tokenVocabulary.mjs +20 -0
- package/dist-plugin/adjust/index.cjs +33 -6
- package/dist-plugin/adjust/index.d.cts +2 -2
- package/dist-plugin/adjust/index.d.ts +2 -2
- package/dist-plugin/adjust/index.js +7 -6
- package/dist-plugin/{chunk-2YNERPXY.js → chunk-RIXO2E55.js} +1 -1
- package/dist-plugin/{chunk-RVE3MNKM.js → chunk-W6Y4BWFB.js} +3 -1
- package/dist-plugin/{chunk-GPIBU44G.js → chunk-YLCOIGQC.js} +25 -1
- package/dist-plugin/{dataPaths-bJTCEO4H.d.ts → dataPaths-BhWzd5cL.d.cts} +4 -0
- package/dist-plugin/{dataPaths-bJTCEO4H.d.cts → dataPaths-BhWzd5cL.d.ts} +4 -0
- package/dist-plugin/fontPairing/index.cjs +3 -1
- package/dist-plugin/fontPairing/index.d.cts +1 -1
- package/dist-plugin/fontPairing/index.d.ts +1 -1
- package/dist-plugin/fontPairing/index.js +1 -1
- package/dist-plugin/generateColorsAndType/index.cjs +28 -2
- package/dist-plugin/generateColorsAndType/index.d.cts +1 -1
- package/dist-plugin/generateColorsAndType/index.d.ts +1 -1
- package/dist-plugin/generateColorsAndType/index.js +2 -2
- package/dist-plugin/index.cjs +28 -2
- package/dist-plugin/index.d.cts +1 -1
- package/dist-plugin/index.d.ts +1 -1
- package/dist-plugin/index.js +3 -3
- package/dist-plugin/migrateData/index.cjs +28 -2
- package/dist-plugin/migrateData/index.d.cts +1 -1
- package/dist-plugin/migrateData/index.d.ts +1 -1
- package/dist-plugin/migrateData/index.js +3 -3
- package/dist-plugin/tokensCssMigrations/index.cjs +3 -1
- package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
- package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
- package/dist-plugin/tokensCssMigrations/index.js +1 -1
- package/package.json +5 -1
- package/src/editor/component-editor/SectionDividerEditor.svelte +1 -11
- package/src/editor/component-editor/contract.ts +175 -0
- package/src/editor/component-editor/scaffolding/StateBlock.svelte +0 -8
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +5 -0
- package/src/editor/component-editor/scaffolding/TypeEditor.svelte +0 -21
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +2 -2
- package/src/editor/component-editor/scaffolding/types.ts +1 -7
- package/src/editor/core/components/aliasKinds.ts +6 -2
- package/src/editor/core/themes/migrations/2026-09-02-sectiondivider-drop-title-outline.ts +39 -0
- package/src/editor/core/themes/migrations/index.ts +2 -0
- package/src/live-tokens/data/themes/autumn.json +1 -7
- package/src/live-tokens/data/themes/halloween.json +1 -7
- package/src/live-tokens/data/themes/midnight-study.json +1 -7
- package/src/live-tokens/data/themes/ocean.json +1 -7
- package/src/live-tokens/data/themes/royal-velvet.json +1 -7
- package/src/live-tokens/data/themes/sketchy.json +1 -7
- package/src/live-tokens/data/themes/spring-meadow.json +1 -7
- package/src/live-tokens/data/themes/sunset.json +1 -7
- package/src/system/components/SectionDivider.svelte +3 -143
- package/src/system/components/FloatingTokenTags.css +0 -284
- package/src/system/components/FloatingTokenTags.svelte +0 -570
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: live-tokens-check-compliance
|
|
3
|
+
description: Check an existing @motion-proto/live-tokens project against its design system and report, without changing a file: which tokens each component reads, which page renders which component, what the two checkers find, and a list of recommended fixes handed to live-tokens-fix-findings. Use when the user asks to check, audit, validate, or review the project, a page, or a component against the design system; asks how compliant it is, what is off, or what it would take to make the build pass; or wants a look before an upgrade. Not for making the changes (live-tokens-fix-findings), and not for a single token (use the editor).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Checking a project against its design system
|
|
7
|
+
|
|
8
|
+
The answer to "check this project" is a report, and every fact in it comes
|
|
9
|
+
from one command. This skill runs it, reads it, and says what the facts mean
|
|
10
|
+
and what fixing them would involve. It edits nothing. When the user wants the
|
|
11
|
+
changes made, that is **live-tokens-fix-findings**, and the report is what it
|
|
12
|
+
starts from.
|
|
13
|
+
|
|
14
|
+
## Run the report
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npx live-tokens report --json
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
It always exits 0: it is a reading, not a gate. Unknown command means the
|
|
21
|
+
installed package predates it; upgrade `@motion-proto/live-tokens` first. The
|
|
22
|
+
sections, in the order the report gives them:
|
|
23
|
+
|
|
24
|
+
| Section | Fact | What it means when it is not clean |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| `migrations` | Whether `tokens.css` is behind the installed package | A stale file shows up downstream as unknown tokens. This is the first fix, and it is one command: `npx live-tokens migrate --check`, then `--write` (`--tokens <path>` for a tokens.css in an unusual place). |
|
|
27
|
+
| `components[].unread` | Tokens a component declares that nothing in its file reads | An editor row that edits nothing. Each is a token to wire into the CSS or to remove. |
|
|
28
|
+
| `components[].registered` | A component file with no `bootLiveTokens` or `registerComponent` entry | It renders on the page but has no editor. |
|
|
29
|
+
| `components[].described` | Whether the runtime file has the header comment the picker reads | Without one, `live-tokens components` cannot say what it is for. |
|
|
30
|
+
| `usage.byPage` | Which catalogue component each page renders, and how many times | A page rendering none is either chrome or hand-rolled markup that a shipped component covers. |
|
|
31
|
+
| `usage.unusedShipped` | Shipped components no page renders | Information, not a finding. |
|
|
32
|
+
| `usage.customUnregistered`, `usage.customUnused` | The project's own components that are unregistered or unused | Dead or half-wired work. |
|
|
33
|
+
| `findings.pages`, `findings.components` | Both checkers' findings by rule, under the project's severities and again under `--strict` | The errors are what fails the build today; the strict count is what a fully tokenized project would fail. |
|
|
34
|
+
|
|
35
|
+
`npx live-tokens components <id>` and `npx live-tokens tokens --family <name>`
|
|
36
|
+
(both take `--json`) answer any question the report raises about one
|
|
37
|
+
component or one scale.
|
|
38
|
+
|
|
39
|
+
## Read it
|
|
40
|
+
|
|
41
|
+
Facts are the report's; the reading is yours. For each rule with findings, say
|
|
42
|
+
in a line what the rule holds and which of two kinds the fix is:
|
|
43
|
+
|
|
44
|
+
- **Mechanical**: a spacing literal to its nearest `--space-*` step, a stroke
|
|
45
|
+
to `--border-width-*`, a hardcoded column count to `var(--columns-count)`,
|
|
46
|
+
`site.css` moved out of `main.ts`, a route given its `source`. Name any
|
|
47
|
+
visible shift, such as a `14px` margin becoming `16px`.
|
|
48
|
+
- **Judgement**: a colour literal mapped by the role it plays rather than its
|
|
49
|
+
hue, a raw type axis set from a text style, a prop the component does not
|
|
50
|
+
declare mapped or dropped. Say what the choice is, not what you would pick.
|
|
51
|
+
|
|
52
|
+
Where a finding looks deliberate, a translucent overlay on an app shell or a
|
|
53
|
+
layout size the project owns, say so and name the config entry that would
|
|
54
|
+
record the decision: `"checks": { "rules": { "<rule>": "warn" } }` in
|
|
55
|
+
`live-tokens.config.json`. Where a whole file is not a themed surface at all,
|
|
56
|
+
hand-tuned artwork or vendored CSS, the entry is
|
|
57
|
+
`"checks": { "exclude": ["src/art/hero.css"] }` — a project-relative path, a
|
|
58
|
+
directory covering what is under it, and naming the file on the command line
|
|
59
|
+
still checks it. Prefer the narrower one: an exclusion drops one file, a
|
|
60
|
+
severity change drops a rule everywhere. Recording either is the user's call,
|
|
61
|
+
not yours.
|
|
62
|
+
|
|
63
|
+
## Report
|
|
64
|
+
|
|
65
|
+
In this order, each line carrying its count:
|
|
66
|
+
|
|
67
|
+
1. Migrations pending, and the one command that clears them.
|
|
68
|
+
2. What fails the build now: errors by rule, with the files.
|
|
69
|
+
3. What `--strict` would add: warnings by rule.
|
|
70
|
+
4. Components: unread tokens, unregistered, undescribed.
|
|
71
|
+
5. Usage: what each page renders, and what is used nowhere.
|
|
72
|
+
6. Recommended fixes, in the order **live-tokens-fix-findings** would take
|
|
73
|
+
them: migrations, then the largest group of errors, then the rest, then
|
|
74
|
+
warnings. Mark each as mechanical or judgement.
|
|
75
|
+
|
|
76
|
+
End with the hand-off: "Run live-tokens-fix-findings to apply these", or the
|
|
77
|
+
subset the user chooses. Do not start applying them here, even when the fix is
|
|
78
|
+
one line, because the user asked how things stand.
|
|
@@ -230,9 +230,9 @@ It *warns* (non-fatal) when a token-backed default still carries a px or rem ter
|
|
|
230
230
|
|
|
231
231
|
Exit code 0 means the static contract is met. Resolve warnings before shipping, or run with `--strict` to make them fail. `--json` prints findings with a stable `rule` id, so you can work through one rule at a time and re-run.
|
|
232
232
|
|
|
233
|
-
**Then run the registry contract test.**
|
|
233
|
+
**Then run the registry contract test.** `checkRegistryEntry`, from `@motion-proto/live-tokens/component-editor/contract`, takes one registry entry and returns a violation line per failure, so a suite over your own components is a `describe.each` and one call. Per component it verifies that the registration resolves to a real `sourceFile` and a non-empty schema, that schema variables are unique, that every editable token is declared in the runtime `<style>` block and seeded in `component-configs/<id>/default.json`, that a token declaring `minOpacity` seeds at or above its floor, and that `setComponentAlias` round-trips the alias through the slice. The test file, the two lines in it that are load-bearing, and the path options are in `references/contract-tests.md`.
|
|
234
234
|
|
|
235
|
-
|
|
235
|
+
Inside the package, `src/editor/component-editor/registryContract.test.ts` is that same file over `builtInRegistry`, so a new first-party component is auto-covered the moment it lands there — `npm test` fails if any check misses.
|
|
236
236
|
|
|
237
237
|
**If your component declares `intrinsics`, the intrinsics contract test covers it too.** `src/editor/component-editor/intrinsicsContract.test.ts` iterates every entry with an `intrinsics` array and asserts, per (intrinsic, variant), that the runtime `:global(:root)` declares a default, the default is one of the spec's `values`, and the editor's `default` equals the runtime default. This is what would have caught a getter defaulting to `center` while `:global(:root)` says `start`. Same auto-coverage rule: declare `intrinsics` on the registry entry and the test picks it up.
|
|
238
238
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# The registry contract as a test in your own project
|
|
2
|
+
|
|
3
|
+
`checkRegistryEntry` is the contract the package holds its own 26 components
|
|
4
|
+
to, exported so a project outside the package can run it over its own. It takes
|
|
5
|
+
one registry entry and returns a violation line per failure; an empty array is
|
|
6
|
+
the pass.
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
// tests/registryContract.test.ts
|
|
10
|
+
// @vitest-environment happy-dom
|
|
11
|
+
import { describe, it, expect } from 'vitest';
|
|
12
|
+
import { getComponentRegistryEntries, registerComponent } from '@motion-proto/live-tokens';
|
|
13
|
+
import { checkRegistryEntry } from '@motion-proto/live-tokens/component-editor/contract';
|
|
14
|
+
import MyWidgetEditor, { allTokens } from '../src/system/components/MyWidgetEditor.svelte';
|
|
15
|
+
|
|
16
|
+
registerComponent({
|
|
17
|
+
id: 'mywidget',
|
|
18
|
+
label: 'My Widget',
|
|
19
|
+
icon: 'fas fa-magic',
|
|
20
|
+
sourceFile: 'src/system/components/MyWidget.svelte',
|
|
21
|
+
editorComponent: MyWidgetEditor,
|
|
22
|
+
schema: allTokens,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const mine = getComponentRegistryEntries().filter((e) => e.origin === 'custom');
|
|
26
|
+
|
|
27
|
+
describe.each(mine.map((e) => [e.id, e] as const))('%s', (_id, entry) => {
|
|
28
|
+
it('meets the registry contract', () => {
|
|
29
|
+
expect(checkRegistryEntry(entry)).toEqual([]);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Two lines there are load-bearing.
|
|
35
|
+
|
|
36
|
+
- **Register at the top of the test file**, rather than importing `main.ts`.
|
|
37
|
+
The entries have to exist before `describe.each` reads them, and
|
|
38
|
+
`bootLiveTokens` would mount the app.
|
|
39
|
+
- **Filter on `origin`.** The registry always carries the shipped components
|
|
40
|
+
too, and their `sourceFile` paths are relative to the package root, not
|
|
41
|
+
yours. Without the filter every built-in fails on a path that does not exist
|
|
42
|
+
in your project.
|
|
43
|
+
|
|
44
|
+
## Setup
|
|
45
|
+
|
|
46
|
+
`vitest` and `happy-dom` as devDependencies, and the svelte plugin already in
|
|
47
|
+
`vite.config.ts` so the editor `.svelte` import resolves. The helper reads the
|
|
48
|
+
runtime file and `default.json` off disk, which is why it is node-only and has
|
|
49
|
+
its own subpath.
|
|
50
|
+
|
|
51
|
+
The package ships Svelte and TypeScript source, and `bootLiveTokens` imports the
|
|
52
|
+
FontAwesome stylesheet. Left external, Node meets that `.css` and stops with
|
|
53
|
+
`Unknown file extension ".css"`, before a single test runs. Inline both so Vite
|
|
54
|
+
transforms them:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// vitest.config.ts
|
|
58
|
+
import { defineConfig, mergeConfig } from 'vitest/config';
|
|
59
|
+
import viteConfig from './vite.config';
|
|
60
|
+
|
|
61
|
+
export default mergeConfig(
|
|
62
|
+
viteConfig,
|
|
63
|
+
defineConfig({
|
|
64
|
+
test: {
|
|
65
|
+
server: { deps: { inline: [/@motion-proto\/live-tokens/, /@fortawesome/] } },
|
|
66
|
+
},
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Paths
|
|
72
|
+
|
|
73
|
+
Paths resolve against `process.cwd()` and `src/live-tokens/data/component-configs`.
|
|
74
|
+
A project that moved either passes them:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
checkRegistryEntry(entry, { projectRoot, componentConfigsDir });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`componentConfigsDir` is the same directory `live-tokens.config.json` names.
|
|
81
|
+
|
|
82
|
+
## What it holds
|
|
83
|
+
|
|
84
|
+
1. **Registration** — `sourceFile` resolves to a real file, the schema is non-empty.
|
|
85
|
+
2. **Uniqueness** — no schema variable is declared twice.
|
|
86
|
+
3. **Editor to runtime** — every editable token's CSS var is declared in the
|
|
87
|
+
runtime's `<style>` block, so an edit has something to repaint.
|
|
88
|
+
4. **Editor to default config** — every editable token has a seed alias in
|
|
89
|
+
`component-configs/<id>/default.json`, so the component adopts with full
|
|
90
|
+
defaults. A component with no `default.json` is editor-only; this check and
|
|
91
|
+
the next one skip it.
|
|
92
|
+
5. **Opacity floors** — a token declaring `minOpacity` ships a default at or
|
|
93
|
+
above it, so a floating panel starts out legible over page content.
|
|
94
|
+
6. **Round-trip** — `setComponentAlias` persists into the slice under the same key.
|
|
95
|
+
|
|
96
|
+
Checks 3 and 4 exclude `hidden: true` tokens, `kind: 'gradient'` tokens (stored
|
|
97
|
+
as gradient objects, not vars), and `-padding-(top|right|bottom|left)` suffixes
|
|
98
|
+
(written on demand by the split-padding UI and read through the `themed-padding`
|
|
99
|
+
mixin's fallback chain, so they exist as neither `:root` declarations nor seeds).
|
|
@@ -176,8 +176,7 @@ inherits, so one declaration covers everything under it:
|
|
|
176
176
|
|
|
177
177
|
Travel is stated in px against a glyph whose size the layer cannot know, so the
|
|
178
178
|
dial that suits a card's worth of artwork tears a 16px icon apart. Reach for the
|
|
179
|
-
soft bank before reaching for `none`.
|
|
180
|
-
example.
|
|
179
|
+
soft bank before reaching for `none`.
|
|
181
180
|
|
|
182
181
|
## First-party components
|
|
183
182
|
|
|
@@ -64,6 +64,12 @@ to that list, so the two cannot drift apart.
|
|
|
64
64
|
| `-padding` | Internal spacing |
|
|
65
65
|
| `-gap` | Spacing between sibling elements |
|
|
66
66
|
|
|
67
|
+
`-width`, `-height` and `-size` are the fall-through: any dimension with no
|
|
68
|
+
more specific name behind it. They read the `--space-*` scale through the same
|
|
69
|
+
picker `-gap` uses, and they match last, so `-border-width`, `-divider-height`,
|
|
70
|
+
`-icon-size` and the rest claim their token first. Reach for the specific name
|
|
71
|
+
when one fits; a stroke is `-border-width` even where the CSS says `outline:`.
|
|
72
|
+
|
|
67
73
|
## Typography
|
|
68
74
|
|
|
69
75
|
| Suffix | Meaning |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
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,
|
|
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
6
|
# Fixing what the checkers report
|
|
@@ -14,7 +14,10 @@ passes repaints when the theme changes. One that does not has opted out of the
|
|
|
14
14
|
system silently, and these findings are where.
|
|
15
15
|
|
|
16
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.
|
|
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.
|
|
18
21
|
|
|
19
22
|
## Reach the checkers
|
|
20
23
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,105 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.72.0 — The contract a consumer can run
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **The registry contract is a function a consumer can run.** The six
|
|
8
|
+
per-component checks that hold this package's own components — registration
|
|
9
|
+
resolves, schema variables are unique, every editable token is declared in
|
|
10
|
+
the runtime `<style>` and seeded in `default.json`, a declared `minOpacity`
|
|
11
|
+
is honoured, `setComponentAlias` round-trips — are now `checkRegistryEntry`,
|
|
12
|
+
exported from `@motion-proto/live-tokens/component-editor/contract`. It takes
|
|
13
|
+
one registry entry and returns a violation line per failure, so a project
|
|
14
|
+
outside this package holds its own components to the same contract with a
|
|
15
|
+
`describe.each` and one call. Paths resolve against `process.cwd()` and
|
|
16
|
+
`src/live-tokens/data/component-configs`, or the `projectRoot` and
|
|
17
|
+
`componentConfigsDir` passed in. Node-only, hence its own subpath.
|
|
18
|
+
`live-tokens-create-component` carries the test file in
|
|
19
|
+
`references/contract-tests.md`; filtering the registry on
|
|
20
|
+
`origin === 'custom'` is the line that matters, since the shipped entries
|
|
21
|
+
name paths relative to the package root.
|
|
22
|
+
|
|
23
|
+
- **`"checks": { "exclude": [...] }` in `live-tokens.config.json`.** Paths the
|
|
24
|
+
checkers skip when they discover their own targets: a project-relative path,
|
|
25
|
+
a directory covering what is under it. For a file that is not a themed
|
|
26
|
+
surface at all — hand-tuned artwork, vendored CSS — where the only other way
|
|
27
|
+
out was downgrading a rule for the whole project. Naming the file on the
|
|
28
|
+
command line still checks it, so the escape hatch cannot hide a file from
|
|
29
|
+
someone looking straight at it. The dev plugin now also recognises `checks`
|
|
30
|
+
and `componentDirs`, which the CLI has read for some time while the plugin
|
|
31
|
+
warned they were unknown keys.
|
|
32
|
+
|
|
33
|
+
### Changed (breaking)
|
|
34
|
+
|
|
35
|
+
- **`FloatingTokenTags` is demo artwork, not a shipped component.** It moved to
|
|
36
|
+
`src/demo/`, so it leaves the published package, the component query and the
|
|
37
|
+
count, which now agree with the registry at 26. **Breaking for anyone
|
|
38
|
+
importing `@motion-proto/live-tokens/components/FloatingTokenTags.svelte`;**
|
|
39
|
+
it was only ever the hero animation of the demo, and it never had a
|
|
40
|
+
`:global(:root)` block, an editor, or a registry entry.
|
|
41
|
+
|
|
42
|
+
- **The SectionDivider title outline is removed.** It was the only reason the title was an
|
|
43
|
+
SVG `<text>` behind a `feMorphology` filter rather than an element, and that
|
|
44
|
+
SVG carried a `getBBox()` viewBox, a per-instance `MutationObserver` on the
|
|
45
|
+
document's inline style (filter primitives cannot read a CSS var, so the
|
|
46
|
+
resolved width and colour had to be read back and pushed onto them by hand),
|
|
47
|
+
font-load listeners to re-measure, and a title that was neither selectable nor
|
|
48
|
+
findable. It shipped transparent in the default and in all eight presets, and
|
|
49
|
+
the trap-out it was built for is done by the layout: the `through-label`
|
|
50
|
+
hairlines flank the title in a flex row, so no rule ever runs behind the
|
|
51
|
+
glyphs. The title is now a span that inherits typography like every other
|
|
52
|
+
component's. `--sectiondivider-{lg,md,sm}-title-outline-width` and
|
|
53
|
+
`-title-outline-color` are dropped by a component-config migration; the
|
|
54
|
+
`outlineWidthVariable` / `outlineColorVariable` rows on `TypeGroupConfig` go
|
|
55
|
+
with them, since nothing else declared one.
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- **`registered` is read from the registry for shipped components too.** The
|
|
60
|
+
query assumed a component under `src/system/components` was registered, so
|
|
61
|
+
`live-tokens components` could name one the editor cannot open and
|
|
62
|
+
pick-component does not list. It now reads the package's own
|
|
63
|
+
`builtInRegistry`, the same parse `check-component` uses, which the two now
|
|
64
|
+
share. A component that is discovered but registered nowhere reports
|
|
65
|
+
`registered: false`, prints `(NOT registered)`, and is counted out of the
|
|
66
|
+
catalogue in the summary line.
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
|
|
70
|
+
- **A bare `-width`, `-height` or `-size` no longer renders a colour picker.**
|
|
71
|
+
The three sat in `KIND_RULES` under `surface`, so `--widget-panel-width` drew
|
|
72
|
+
a palette while the naming vocabulary documented it as geometry. They are now
|
|
73
|
+
a `length` kind, matched last among the geometry rules so `-border-width`,
|
|
74
|
+
`-divider-height`, `-icon-size` and the rest still claim their token first,
|
|
75
|
+
and drawn with the `--space-*` picker `-gap` uses. The suffix vocabulary is
|
|
76
|
+
unchanged, so no token is renamed and no project has to migrate. `adjust`
|
|
77
|
+
does not take the kind: its ladders are density and shape, and a panel width
|
|
78
|
+
is neither.
|
|
79
|
+
|
|
80
|
+
## 0.71.0 — Check this project
|
|
81
|
+
|
|
82
|
+
### Added
|
|
83
|
+
|
|
84
|
+
- **`npx live-tokens report` is the project as facts.** Pending `tokens.css`
|
|
85
|
+
migrations, the tokens each component declares and how many its own CSS
|
|
86
|
+
reads (a read counts a `var()`, a `style:` directive, a padding mixin's
|
|
87
|
+
string, or an SCSS-interpolated pattern), whether a component is registered
|
|
88
|
+
and carries the description comment the picker reads, which page renders
|
|
89
|
+
which component and how many times, the shipped and custom components used
|
|
90
|
+
nowhere, and both checkers' findings by rule under the project's severities
|
|
91
|
+
and again under `--strict`. It always exits 0: a reading, not a gate. `--json`
|
|
92
|
+
for data.
|
|
93
|
+
|
|
94
|
+
- **`live-tokens-check-compliance`, the eighth skill.** "Check this project"
|
|
95
|
+
runs the report and presents it without editing a file: what fails the
|
|
96
|
+
build now, what `--strict` would add, the components and usage facts, and a
|
|
97
|
+
list of recommended fixes marked mechanical or judgement with any visible
|
|
98
|
+
shift named, handed to `live-tokens-fix-findings`. A finding that looks
|
|
99
|
+
deliberate is flagged with the config entry that would record the decision,
|
|
100
|
+
which stays the user's call. `live-tokens-fix-findings` no longer claims the
|
|
101
|
+
audit wording, and starts from the report when the user has not seen it.
|
|
102
|
+
|
|
3
103
|
## 0.70.0 — The registry is a query
|
|
4
104
|
|
|
5
105
|
### 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.
|
|
@@ -325,6 +325,7 @@ npx @motion-proto/live-tokens <command>
|
|
|
325
325
|
| `setup-claude [--force]` | Install the bundled Claude Code skills into `./.claude/skills/`. |
|
|
326
326
|
| `components [id] [--json]` | List every component the project has, shipped and its own, with the props each takes; with an id, its props, variants, tokens, and defaults. |
|
|
327
327
|
| `tokens [--family <name>] [--json]` | List every theme token the project's `tokens.css` declares, by family, with its value. |
|
|
328
|
+
| `report [--json]` | The project as facts: pending migrations, tokens each component reads, which page renders which component, and both checkers' findings by rule. Always exits 0. |
|
|
328
329
|
| `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`. |
|
|
329
330
|
| `check-page [paths...]` | Validate pages against the build-page contract: catalogue components and their props, theme tokens over literals, route wiring. |
|
|
330
331
|
| `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. |
|
|
@@ -336,7 +337,7 @@ Once installed in a project, the same commands are available as `npx live-tokens
|
|
|
336
337
|
|
|
337
338
|
## Claude Code skills
|
|
338
339
|
|
|
339
|
-
The package bundles
|
|
340
|
+
The package bundles eight 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, how a project is checked against all of that, and how an existing page or component is brought back into line. Each triggers from an ordinary request, so there are no slash commands to learn.
|
|
340
341
|
|
|
341
342
|
### Install
|
|
342
343
|
|
|
@@ -400,6 +401,12 @@ Ask for something the catalogue lacks: "author a Rating component", "make my Chi
|
|
|
400
401
|
|
|
401
402
|
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.
|
|
402
403
|
|
|
404
|
+
### `live-tokens-check-compliance`
|
|
405
|
+
|
|
406
|
+
Ask how things stand: "check this project against the design system", "audit the pricing page", "what would it take to make the build pass?", "review this before I upgrade".
|
|
407
|
+
|
|
408
|
+
The skill runs `npx live-tokens report --json`, which is the project as facts: pending `tokens.css` migrations, the tokens each component declares and reads, which page renders which component, and both checkers' findings by rule under the project's severities and under `--strict`. It presents the report, says what each rule holds, marks each recommended fix as mechanical or a judgement call, names any visible shift, and flags a finding that looks deliberate together with the config entry that would record the decision. It edits nothing and ends by handing the list to `live-tokens-fix-findings`.
|
|
409
|
+
|
|
403
410
|
### `live-tokens-fix-findings`
|
|
404
411
|
|
|
405
412
|
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?".
|
package/bin/check-component.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
package/bin/check-page.mjs
CHANGED
|
@@ -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';
|
|
@@ -35,7 +35,7 @@ export const PAGE_RULES = {
|
|
|
35
35
|
// Directories that hold the system, not pages built on it.
|
|
36
36
|
const NOT_PAGES = ['src/system', 'src/editor', 'src/lib', 'src/live-tokens'];
|
|
37
37
|
|
|
38
|
-
const COMPONENT_IMPORT =
|
|
38
|
+
export const COMPONENT_IMPORT =
|
|
39
39
|
/(?:@motion-proto\/live-tokens\/components|[./][^'"]*\/system\/components)\/([A-Za-z0-9]+)\.svelte$/;
|
|
40
40
|
|
|
41
41
|
const DEEP_IMPORT_PATTERNS = [
|
|
@@ -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
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// setup-claude [--force] Copy bundled Claude Code skills into ./.claude/skills/.
|
|
6
6
|
// components [id] List every component the project has, shipped and its own, with props and tokens.
|
|
7
7
|
// tokens [--family <name>] List every theme token by family, with its value.
|
|
8
|
+
// report The project as facts: tokens read, components used, findings by rule. Always exits 0.
|
|
8
9
|
// check-component [id] Validate a component (or every authored one) against the create-component skill contract.
|
|
9
10
|
// check-page [paths...] Validate pages against the build-page skill contract.
|
|
10
11
|
// generate-theme <brief> Build a theme from a 10-seed OKLCH brief and open it.
|
|
@@ -19,6 +20,7 @@ import process from 'node:process';
|
|
|
19
20
|
import { COMPONENT_RULES, checkComponent, discoverComponents, formatReport } from './check-component.mjs';
|
|
20
21
|
import { PAGE_RULES, checkPages, discoverPages } from './check-page.mjs';
|
|
21
22
|
import { describeComponents, describeTokens, formatComponents, formatTokens } from './lib/catalogue.mjs';
|
|
23
|
+
import { buildReport, formatReport as formatProjectReport } from './lib/report.mjs';
|
|
22
24
|
import { loadVocabulary } from './lib/tokenVocabulary.mjs';
|
|
23
25
|
import {
|
|
24
26
|
applySeverity,
|
|
@@ -54,6 +56,12 @@ Commands:
|
|
|
54
56
|
tokens [--family <name>] [--json]
|
|
55
57
|
List every theme token the project's tokens.css
|
|
56
58
|
declares, by family, with its value
|
|
59
|
+
report [--json] The project as facts: pending migrations, tokens
|
|
60
|
+
each component declares and reads, which page
|
|
61
|
+
renders which component, and both checkers'
|
|
62
|
+
findings by rule under the project's severities
|
|
63
|
+
and under --strict. A reading, not a gate: always
|
|
64
|
+
exits 0
|
|
57
65
|
check-component [id] Validate <id>'s runtime, editor, and registration
|
|
58
66
|
against the live-tokens-create-component contract
|
|
59
67
|
check-page [paths...] Validate pages against the live-tokens-build-page
|
|
@@ -67,7 +75,8 @@ Both check commands accept:
|
|
|
67
75
|
--strict Treat warnings as errors
|
|
68
76
|
--off=<rule,...> Silence rules; --warn=/--error= change severity
|
|
69
77
|
(or set "checks": { "rules": {...} } in
|
|
70
|
-
live-tokens.config.json
|
|
78
|
+
live-tokens.config.json; "checks": { "exclude":
|
|
79
|
+
[...] } drops paths from discovery entirely)
|
|
71
80
|
generate-theme <brief.json> [--no-activate] [--dry-run] [--carry-from <name>]
|
|
72
81
|
Build a full theme from a 10-seed OKLCH brief
|
|
73
82
|
(see the live-tokens-generate-theme skill),
|
|
@@ -190,6 +199,24 @@ if (command === 'tokens') {
|
|
|
190
199
|
process.exit(0);
|
|
191
200
|
}
|
|
192
201
|
|
|
202
|
+
if (command === 'report') {
|
|
203
|
+
const opts = parseCheckFlags(rest);
|
|
204
|
+
const report = buildReport(loadVocabulary());
|
|
205
|
+
try {
|
|
206
|
+
const plan = await runMigrate({ check: true });
|
|
207
|
+
report.migrations =
|
|
208
|
+
plan.status === 'no-path'
|
|
209
|
+
? { status: 'no tokens.css' }
|
|
210
|
+
: plan.status === 'would-change'
|
|
211
|
+
? { status: 'pending', pending: plan.applied ?? plan.migrations ?? [] }
|
|
212
|
+
: { status: 'none pending' };
|
|
213
|
+
} catch {
|
|
214
|
+
report.migrations = { status: 'unavailable (compiled engine not built)' };
|
|
215
|
+
}
|
|
216
|
+
writeOut(opts.json ? JSON.stringify(report, null, 2) : formatProjectReport(report));
|
|
217
|
+
process.exit(0);
|
|
218
|
+
}
|
|
219
|
+
|
|
193
220
|
if (command === 'check-component') {
|
|
194
221
|
const opts = parseCheckFlags(rest);
|
|
195
222
|
const ids = opts.rest.length > 0 ? [opts.rest[0]] : discoverComponents();
|
|
@@ -368,6 +395,7 @@ const SAMPLE_PROMPTS = {
|
|
|
368
395
|
'live-tokens-adjust-geometry': 'make the buttons pill shaped',
|
|
369
396
|
'live-tokens-pair-fonts': 'pair some fonts for this theme',
|
|
370
397
|
'live-tokens-fix-findings': 'make check:design pass',
|
|
398
|
+
'live-tokens-check-compliance': 'check this project against the design system',
|
|
371
399
|
};
|
|
372
400
|
|
|
373
401
|
const installedSamples = skills
|
package/bin/lib/catalogue.mjs
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
|
package/bin/lib/findings.mjs
CHANGED
|
@@ -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.
|