@motion-proto/live-tokens 0.70.0 → 0.71.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.
@@ -0,0 +1,72 @@
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`. Recording it is the user's call, not yours.
56
+
57
+ ## Report
58
+
59
+ In this order, each line carrying its count:
60
+
61
+ 1. Migrations pending, and the one command that clears them.
62
+ 2. What fails the build now: errors by rule, with the files.
63
+ 3. What `--strict` would add: warnings by rule.
64
+ 4. Components: unread tokens, unregistered, undescribed.
65
+ 5. Usage: what each page renders, and what is used nowhere.
66
+ 6. Recommended fixes, in the order **live-tokens-fix-findings** would take
67
+ them: migrations, then the largest group of errors, then the rest, then
68
+ warnings. Mark each as mechanical or judgement.
69
+
70
+ End with the hand-off: "Run live-tokens-fix-findings to apply these", or the
71
+ subset the user chooses. Do not start applying them here, even when the fix is
72
+ one line, because the user asked how things stand.
@@ -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, 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).
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,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.71.0 — Check this project
4
+
5
+ ### Added
6
+
7
+ - **`npx live-tokens report` is the project as facts.** Pending `tokens.css`
8
+ migrations, the tokens each component declares and how many its own CSS
9
+ reads (a read counts a `var()`, a `style:` directive, a padding mixin's
10
+ string, or an SCSS-interpolated pattern), whether a component is registered
11
+ and carries the description comment the picker reads, which page renders
12
+ which component and how many times, the shipped and custom components used
13
+ nowhere, and both checkers' findings by rule under the project's severities
14
+ and again under `--strict`. It always exits 0: a reading, not a gate. `--json`
15
+ for data.
16
+
17
+ - **`live-tokens-check-compliance`, the eighth skill.** "Check this project"
18
+ runs the report and presents it without editing a file: what fails the
19
+ build now, what `--strict` would add, the components and usage facts, and a
20
+ list of recommended fixes marked mechanical or judgement with any visible
21
+ shift named, handed to `live-tokens-fix-findings`. A finding that looks
22
+ deliberate is flagged with the config entry that would record the decision,
23
+ which stays the user's call. `live-tokens-fix-findings` no longer claims the
24
+ audit wording, and starts from the report when the user has not seen it.
25
+
3
26
  ## 0.70.0 — The registry is a query
4
27
 
5
28
  ### Added
package/README.md CHANGED
@@ -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 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.
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?".
@@ -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 = [
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
@@ -190,6 +198,24 @@ if (command === 'tokens') {
190
198
  process.exit(0);
191
199
  }
192
200
 
201
+ if (command === 'report') {
202
+ const opts = parseCheckFlags(rest);
203
+ const report = buildReport(loadVocabulary());
204
+ try {
205
+ const plan = await runMigrate({ check: true });
206
+ report.migrations =
207
+ plan.status === 'no-path'
208
+ ? { status: 'no tokens.css' }
209
+ : plan.status === 'would-change'
210
+ ? { status: 'pending', pending: plan.applied ?? plan.migrations ?? [] }
211
+ : { status: 'none pending' };
212
+ } catch {
213
+ report.migrations = { status: 'unavailable (compiled engine not built)' };
214
+ }
215
+ writeOut(opts.json ? JSON.stringify(report, null, 2) : formatProjectReport(report));
216
+ process.exit(0);
217
+ }
218
+
193
219
  if (command === 'check-component') {
194
220
  const opts = parseCheckFlags(rest);
195
221
  const ids = opts.rest.length > 0 ? [opts.rest[0]] : discoverComponents();
@@ -368,6 +394,7 @@ const SAMPLE_PROMPTS = {
368
394
  'live-tokens-adjust-geometry': 'make the buttons pill shaped',
369
395
  'live-tokens-pair-fonts': 'pair some fonts for this theme',
370
396
  'live-tokens-fix-findings': 'make check:design pass',
397
+ 'live-tokens-check-compliance': 'check this project against the design system',
371
398
  };
372
399
 
373
400
  const installedSamples = skills
@@ -0,0 +1,158 @@
1
+ // The project as facts. Every section is deterministic: what the tokens.css
2
+ // declares, what each component reads, which component each page renders, and
3
+ // what the two checkers report under the project's severities and under
4
+ // --strict. Nothing here interprets; the check skill narrates it and the fix
5
+ // skill acts on it. Reads files only, like the vocabulary it is built on.
6
+
7
+ import { readFileSync } from 'node:fs';
8
+ import { relative } from 'node:path';
9
+ import { COMPONENT_RULES, checkComponent, discoverComponents } from '../check-component.mjs';
10
+ import { COMPONENT_IMPORT, PAGE_RULES, checkPages, discoverPages } from '../check-page.mjs';
11
+ import { applySeverity, readChecksConfig } from './findings.mjs';
12
+ import { extractGlobalRootBlocks } from './tokenVocabulary.mjs';
13
+
14
+ const SIDES = ['-top', '-right', '-bottom', '-left'];
15
+
16
+ /**
17
+ * Tokens a component declares that nothing in its file reads. A read is the
18
+ * name appearing outside the `:global(:root)` block: in a `var()`, in a `style:`
19
+ * directive, or as the string a padding mixin takes. SCSS interpolation
20
+ * (`--badge-#{$v}-surface`) reads every token the pattern covers. A per-side
21
+ * padding is read through its parent.
22
+ */
23
+ export function unreadTokens(source, tokens) {
24
+ let body = source.replace(/\/\*[\s\S]*?\*\//g, ' ').replace(/<!--[\s\S]*?-->/g, ' ');
25
+ for (const block of extractGlobalRootBlocks(body)) body = body.replace(block, ' ');
26
+ const patterns = [...body.matchAll(/--[a-z0-9-]*(?:#\{[^}]*\}[a-z0-9-]*)+/g)].map(
27
+ (m) => new RegExp(`^${m[0].replace(/[.*+?^()|[\]\\]/g, '\\$&').replace(/#\{[^}]*\}/g, '[a-z0-9-]+')}$`),
28
+ );
29
+ const isRead = (name) => body.includes(name) || patterns.some((re) => re.test(name));
30
+ return [...tokens].filter((name) => {
31
+ const side = SIDES.find((s) => name.endsWith(s));
32
+ return !isRead(name) && !(side && isRead(name.slice(0, -side.length)));
33
+ });
34
+ }
35
+
36
+ function countByRule(findings) {
37
+ const out = {};
38
+ for (const f of findings) out[f.rule] = (out[f.rule] ?? 0) + 1;
39
+ return out;
40
+ }
41
+
42
+ function summarise(findings, rules, config) {
43
+ const resolved = applySeverity(findings, rules, {}, config);
44
+ const strict = applySeverity(findings, rules, { strict: true }, config);
45
+ return {
46
+ errors: resolved.filter((f) => f.severity === 'error').length,
47
+ warnings: resolved.filter((f) => f.severity === 'warn').length,
48
+ strictErrors: strict.filter((f) => f.severity === 'error').length,
49
+ byRule: countByRule(resolved),
50
+ items: resolved.map((f) => ({ rule: f.rule, severity: f.severity, file: f.file, line: f.line, message: f.message })),
51
+ };
52
+ }
53
+
54
+ export function buildReport(vocab, { root = process.cwd() } = {}) {
55
+ const config = readChecksConfig(root);
56
+
57
+ const components = [...vocab.components.values()].map((c) => {
58
+ const source = readFileSync(c.file, 'utf8');
59
+ return {
60
+ id: c.id,
61
+ name: c.name,
62
+ origin: c.origin,
63
+ file: relative(root, c.file),
64
+ registered: c.origin === 'shipped' || vocab.registered.has(c.id),
65
+ described: /^\s*<!--[\s\S]*?-->/.test(source),
66
+ tokens: c.tokens.size,
67
+ unread: unreadTokens(source, c.tokens.keys()),
68
+ };
69
+ });
70
+
71
+ const pageFiles = discoverPages(root);
72
+ const byPage = [];
73
+ const pagesOf = new Map();
74
+ for (const file of pageFiles) {
75
+ if (!file.endsWith('.svelte')) continue;
76
+ const text = readFileSync(file, 'utf8').replace(/<style[^>]*>[\s\S]*?<\/style>/g, ' ');
77
+ const used = [];
78
+ for (const m of text.matchAll(/import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/g)) {
79
+ const comp = m[2].match(COMPONENT_IMPORT);
80
+ if (!comp) continue;
81
+ const id = comp[1].toLowerCase();
82
+ const rendered = [...text.matchAll(new RegExp(`<${m[1]}(?=[\\s/>])`, 'g'))].length;
83
+ used.push({ id, rendered });
84
+ if (!pagesOf.has(id)) pagesOf.set(id, []);
85
+ pagesOf.get(id).push(relative(root, file));
86
+ }
87
+ byPage.push({ file: relative(root, file), components: used });
88
+ }
89
+ const byComponent = components.map((c) => ({ id: c.id, origin: c.origin, pages: pagesOf.get(c.id) ?? [] }));
90
+
91
+ const pageFindings = checkPages(pageFiles, { root, vocabulary: vocab }).findings;
92
+ const authored = discoverComponents(root);
93
+ const componentFindings = authored.flatMap((id) => checkComponent(id, root, { vocabulary: vocab }).findings);
94
+
95
+ return {
96
+ project: {
97
+ root,
98
+ tokensCss: vocab.tokensCssPath ? relative(root, vocab.tokensCssPath) : null,
99
+ themeTokens: vocab.themeTokens.size,
100
+ components: components.length,
101
+ pages: pageFiles.length,
102
+ },
103
+ components,
104
+ usage: {
105
+ byPage,
106
+ byComponent,
107
+ unusedShipped: byComponent.filter((c) => c.origin === 'shipped' && c.pages.length === 0).map((c) => c.id),
108
+ customUnregistered: components.filter((c) => c.origin === 'custom' && !c.registered).map((c) => c.id),
109
+ customUnused: byComponent.filter((c) => c.origin === 'custom' && c.pages.length === 0).map((c) => c.id),
110
+ },
111
+ findings: {
112
+ pages: summarise(pageFindings, PAGE_RULES, config),
113
+ components: { checked: authored, ...summarise(componentFindings, COMPONENT_RULES, config) },
114
+ },
115
+ };
116
+ }
117
+
118
+ const list = (items, max = 20) =>
119
+ items.length <= max ? items.join(', ') : `${items.slice(0, max).join(', ')}, +${items.length - max} more`;
120
+
121
+ export function formatReport(r) {
122
+ const out = [];
123
+ out.push(`Project: ${r.project.pages} page file(s), ${r.project.components} component(s), ${r.project.themeTokens} theme tokens from ${r.project.tokensCss ?? '(no tokens.css)'}`);
124
+ if (r.migrations) {
125
+ out.push('');
126
+ out.push(`Migrations: ${r.migrations.status}${r.migrations.pending?.length ? ` (${list(r.migrations.pending)})` : ''}`);
127
+ }
128
+
129
+ out.push('');
130
+ out.push('Components');
131
+ const unread = r.components.filter((c) => c.unread.length);
132
+ out.push(` tokens declared and read by their own CSS: ${r.components.reduce((n, c) => n + c.tokens - c.unread.length, 0)} of ${r.components.reduce((n, c) => n + c.tokens, 0)}`);
133
+ for (const c of unread) out.push(` ${c.id}: ${c.unread.length} unread (${list(c.unread, 6)})`);
134
+ const custom = r.components.filter((c) => c.origin === 'custom');
135
+ out.push(` custom: ${custom.length}${custom.length ? ` (${list(custom.map((c) => c.id))})` : ''}`);
136
+ if (r.usage.customUnregistered.length) out.push(` not registered: ${list(r.usage.customUnregistered)}`);
137
+ const undescribed = custom.filter((c) => !c.described).map((c) => c.id);
138
+ if (undescribed.length) out.push(` no description comment: ${list(undescribed)}`);
139
+
140
+ out.push('');
141
+ out.push('Usage');
142
+ for (const p of r.usage.byPage) {
143
+ if (p.components.length === 0) continue;
144
+ out.push(` ${p.file}: ${p.components.map((c) => `${c.id}×${c.rendered}`).join(', ')}`);
145
+ }
146
+ out.push(` pages rendering no catalogue component: ${r.usage.byPage.filter((p) => p.components.length === 0).length}`);
147
+ out.push(` shipped components used nowhere: ${r.usage.unusedShipped.length}${r.usage.unusedShipped.length ? ` (${list(r.usage.unusedShipped)})` : ''}`);
148
+ if (r.usage.customUnused.length) out.push(` custom components used nowhere: ${list(r.usage.customUnused)}`);
149
+
150
+ const section = (label, s) => {
151
+ out.push('');
152
+ out.push(`${label}: ${s.errors} error(s), ${s.warnings} warning(s); ${s.strictErrors} under --strict`);
153
+ for (const [rule, n] of Object.entries(s.byRule).sort((a, b) => b[1] - a[1])) out.push(` ${rule}: ${n}`);
154
+ };
155
+ section('check-page', r.findings.pages);
156
+ section(`check-component (${r.findings.components.checked.length} authored)`, r.findings.components);
157
+ return out.join('\n');
158
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motion-proto/live-tokens",
3
- "version": "0.70.0",
3
+ "version": "0.71.0",
4
4
  "type": "module",
5
5
  "description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
6
6
  "keywords": [