@motion-proto/live-tokens 0.79.0 → 0.80.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.
@@ -37,7 +37,7 @@ import {
37
37
 
38
38
  // vite-plugin/themeFileApi.ts
39
39
  import fs2 from "fs";
40
- import path2 from "path";
40
+ import path3 from "path";
41
41
 
42
42
  // src/editor/core/themes/migrations/2026-07-21-palette-oklch-basis.ts
43
43
  var round = (v, dp) => {
@@ -229,18 +229,40 @@ function dataTreeWatch(opts) {
229
229
  return { noteOwnWrite, subscribe };
230
230
  }
231
231
 
232
+ // vite-plugin/buildChecks.ts
233
+ import path2 from "path";
234
+ import { fileURLToPath, pathToFileURL } from "url";
235
+ var runPackageChecks = async (root) => {
236
+ const runner = path2.resolve(path2.dirname(fileURLToPath(import.meta.url)), "..", "bin", "lib", "buildChecks.mjs");
237
+ const { runBuildChecks } = await import(pathToFileURL(runner).href);
238
+ return runBuildChecks({ root });
239
+ };
240
+ async function checkBeforeBuild({
241
+ root,
242
+ logger,
243
+ run = runPackageChecks
244
+ }) {
245
+ const result = await run(root);
246
+ if (result.errors > 0) {
247
+ logger.error(result.report);
248
+ return `live-tokens: the design checks found ${result.errors} error(s). Run the live-tokens-check-compliance skill, or \`npx live-tokens check-page\` and \`npx live-tokens check-component\`, to repair them.`;
249
+ }
250
+ (result.warnings > 0 ? logger.warn : logger.info).call(logger, result.report);
251
+ return null;
252
+ }
253
+
232
254
  // vite-plugin/themeFileApi.ts
233
- import { fileURLToPath } from "url";
255
+ import { fileURLToPath as fileURLToPath2 } from "url";
234
256
  var PKG_VERSION = (() => {
235
257
  try {
236
- let dir = path2.dirname(fileURLToPath(import.meta.url));
258
+ let dir = path3.dirname(fileURLToPath2(import.meta.url));
237
259
  for (let i = 0; i < 4; i++) {
238
- const p = path2.join(dir, "package.json");
260
+ const p = path3.join(dir, "package.json");
239
261
  if (fs2.existsSync(p)) {
240
262
  const json = JSON.parse(fs2.readFileSync(p, "utf-8"));
241
263
  if (json?.name === "@motion-proto/live-tokens") return json.version ?? "";
242
264
  }
243
- const up = path2.dirname(dir);
265
+ const up = path3.dirname(dir);
244
266
  if (up === dir) break;
245
267
  dir = up;
246
268
  }
@@ -284,37 +306,37 @@ function themeFileApi(opts) {
284
306
  const COMPONENT_CONFIGS_DIR = dataDirs.componentConfigsDir;
285
307
  const THEMES_DIR = dataDirs.themesDir;
286
308
  const SKETCH_STYLES_DIR = dataDirs.sketchStylesDir;
287
- const CSS_PATH = path2.resolve(opts.tokensCssPath);
309
+ const CSS_PATH = path3.resolve(opts.tokensCssPath);
288
310
  const ISOLATED_DATA_DIR = testDataDir();
289
- const GENERATED_CSS_PATH = ISOLATED_DATA_DIR ? path2.join(ISOLATED_DATA_DIR, "tokens.generated.css") : opts.tokensGeneratedCssPath ? path2.resolve(opts.tokensGeneratedCssPath) : path2.join(dataDirs.dataDir, "tokens.generated.css");
290
- const FONTS_CSS_PATH = ISOLATED_DATA_DIR ? path2.join(ISOLATED_DATA_DIR, "fonts.css") : opts.fontsCssPath ? path2.resolve(opts.fontsCssPath) : path2.join(path2.dirname(CSS_PATH), "fonts.css");
311
+ const GENERATED_CSS_PATH = ISOLATED_DATA_DIR ? path3.join(ISOLATED_DATA_DIR, "tokens.generated.css") : opts.tokensGeneratedCssPath ? path3.resolve(opts.tokensGeneratedCssPath) : path3.join(dataDirs.dataDir, "tokens.generated.css");
312
+ const FONTS_CSS_PATH = ISOLATED_DATA_DIR ? path3.join(ISOLATED_DATA_DIR, "fonts.css") : opts.fontsCssPath ? path3.resolve(opts.fontsCssPath) : path3.join(path3.dirname(CSS_PATH), "fonts.css");
291
313
  const API_BASE = opts.apiBase ?? "/api/live-tokens";
292
- const consumerComponentDirs = opts.componentsSrcDir ? [path2.resolve(opts.componentsSrcDir)] : [path2.resolve("src/components"), path2.resolve("src/system/components")];
293
- const packageComponentsDir = path2.resolve(
294
- path2.dirname(fileURLToPath(import.meta.url)),
314
+ const consumerComponentDirs = opts.componentsSrcDir ? [path3.resolve(opts.componentsSrcDir)] : [path3.resolve("src/components"), path3.resolve("src/system/components")];
315
+ const packageComponentsDir = path3.resolve(
316
+ path3.dirname(fileURLToPath2(import.meta.url)),
295
317
  "..",
296
318
  "src",
297
319
  "system",
298
320
  "components"
299
321
  );
300
- const packageDataDir = testPackageDataDir() ?? path2.resolve(
301
- path2.dirname(fileURLToPath(import.meta.url)),
322
+ const packageDataDir = testPackageDataDir() ?? path3.resolve(
323
+ path3.dirname(fileURLToPath2(import.meta.url)),
302
324
  "..",
303
325
  "src",
304
326
  "live-tokens",
305
327
  "data"
306
328
  );
307
- const packageColorsAndTypeDir = path2.join(packageDataDir, "colors-and-type");
308
- const packageThemesDir = path2.join(packageDataDir, "themes");
309
- const packageComponentConfigsDir = path2.join(packageDataDir, "component-configs");
310
- const packageSketchStylesDir = path2.join(packageDataDir, "sketch-styles");
329
+ const packageColorsAndTypeDir = path3.join(packageDataDir, "colors-and-type");
330
+ const packageThemesDir = path3.join(packageDataDir, "themes");
331
+ const packageComponentConfigsDir = path3.join(packageDataDir, "component-configs");
332
+ const packageSketchStylesDir = path3.join(packageDataDir, "sketch-styles");
311
333
  function shippedNames(dataDir, subdir) {
312
334
  try {
313
- const pkgRoot = path2.resolve(dataDir, "..", "..", "..");
314
- const pkg = JSON.parse(fs2.readFileSync(path2.join(pkgRoot, "package.json"), "utf-8"));
335
+ const pkgRoot = path3.resolve(dataDir, "..", "..", "..");
336
+ const pkg = JSON.parse(fs2.readFileSync(path3.join(pkgRoot, "package.json"), "utf-8"));
315
337
  const files = Array.isArray(pkg.files) ? pkg.files : [];
316
338
  const owned = new RegExp(`^src/live-tokens/data/${subdir}/[^/]+\\.json$`);
317
- return files.filter((f) => owned.test(f)).map((f) => path2.basename(f, ".json"));
339
+ return files.filter((f) => owned.test(f)).map((f) => path3.basename(f, ".json"));
318
340
  } catch {
319
341
  return [];
320
342
  }
@@ -323,7 +345,7 @@ function themeFileApi(opts) {
323
345
  if (!COMPONENTS_SCAN_DIRS.includes(packageComponentsDir) && fs2.existsSync(packageComponentsDir)) {
324
346
  COMPONENTS_SCAN_DIRS.push(packageComponentsDir);
325
347
  }
326
- const isLiveStateFile = (file) => file === path2.join(THEMES_DIR, "_active.json") || path2.basename(file) === "_working.json" && (file.startsWith(COLORS_AND_TYPE_DIR + path2.sep) || file.startsWith(COMPONENT_CONFIGS_DIR + path2.sep));
348
+ const isLiveStateFile = (file) => file === path3.join(THEMES_DIR, "_active.json") || path3.basename(file) === "_working.json" && (file.startsWith(COLORS_AND_TYPE_DIR + path3.sep) || file.startsWith(COMPONENT_CONFIGS_DIR + path3.sep));
327
349
  const liveWatch = dataTreeWatch({
328
350
  dirs: [COLORS_AND_TYPE_DIR, COMPONENT_CONFIGS_DIR, THEMES_DIR],
329
351
  isLive: isLiveStateFile
@@ -339,8 +361,8 @@ function themeFileApi(opts) {
339
361
  let r = componentResourceCache.get(comp);
340
362
  if (!r) {
341
363
  r = versionedFileResourceServer({
342
- dir: path2.join(COMPONENT_CONFIGS_DIR, comp),
343
- packageDir: path2.join(packageComponentConfigsDir, comp),
364
+ dir: path3.join(COMPONENT_CONFIGS_DIR, comp),
365
+ packageDir: path3.join(packageComponentConfigsDir, comp),
344
366
  onWrite: liveWatch.noteOwnWrite
345
367
  });
346
368
  componentResourceCache.set(comp, r);
@@ -431,7 +453,7 @@ function themeFileApi(opts) {
431
453
  const productionThemeName = themesResource.getProductionName();
432
454
  const productionTheme = readTheme(productionThemeName)?.theme ?? null;
433
455
  if (themesResource.existingPath(productionThemeName) !== null && !productionTheme?.colorsAndType) {
434
- const tail = `${path2.basename(GENERATED_CSS_PATH)} was left as it was.`;
456
+ const tail = `${path3.basename(GENERATED_CSS_PATH)} was left as it was.`;
435
457
  throw new Error(
436
458
  unreadableThemeReason(productionThemeName) === "colors-and-type" ? `[live-tokens] ${notAThemeError(productionThemeName)} ${tail}` : `[live-tokens] Production theme "${productionThemeName}" is unreadable, so ${tail} Repair the theme file or adopt another theme.`
437
459
  );
@@ -502,12 +524,12 @@ function themeFileApi(opts) {
502
524
  lines.push("");
503
525
  }
504
526
  }
505
- if (!fs2.existsSync(path2.dirname(GENERATED_CSS_PATH))) {
506
- fs2.mkdirSync(path2.dirname(GENERATED_CSS_PATH), { recursive: true });
527
+ if (!fs2.existsSync(path3.dirname(GENERATED_CSS_PATH))) {
528
+ fs2.mkdirSync(path3.dirname(GENERATED_CSS_PATH), { recursive: true });
507
529
  }
508
530
  fs2.writeFileSync(GENERATED_CSS_PATH, lines.join("\n"));
509
531
  console.log(
510
- `[regenerateTokensCss] Wrote ${path2.basename(GENERATED_CSS_PATH)} (${colorsAndTypeVarCount} theme vars, ${componentOverrideCount} component overrides)`
532
+ `[regenerateTokensCss] Wrote ${path3.basename(GENERATED_CSS_PATH)} (${colorsAndTypeVarCount} theme vars, ${componentOverrideCount} component overrides)`
511
533
  );
512
534
  }
513
535
  function syncFontsToCss(colorsAndTypeData) {
@@ -535,14 +557,14 @@ function themeFileApi(opts) {
535
557
  lines.push("");
536
558
  }
537
559
  const content = lines.join("\n");
538
- if (!fs2.existsSync(path2.dirname(FONTS_CSS_PATH))) {
539
- fs2.mkdirSync(path2.dirname(FONTS_CSS_PATH), { recursive: true });
560
+ if (!fs2.existsSync(path3.dirname(FONTS_CSS_PATH))) {
561
+ fs2.mkdirSync(path3.dirname(FONTS_CSS_PATH), { recursive: true });
540
562
  }
541
563
  fs2.writeFileSync(FONTS_CSS_PATH, content);
542
- console.log(`[syncFontsToCss] Wrote ${sources.length} source(s) into ${path2.basename(FONTS_CSS_PATH)}`);
564
+ console.log(`[syncFontsToCss] Wrote ${sources.length} source(s) into ${path3.basename(FONTS_CSS_PATH)}`);
543
565
  }
544
566
  function componentNameFromFile(filePath) {
545
- return path2.basename(filePath, ".svelte").toLowerCase();
567
+ return path3.basename(filePath, ".svelte").toLowerCase();
546
568
  }
547
569
  function isThemeAwareComponent(filePath) {
548
570
  try {
@@ -558,7 +580,7 @@ function themeFileApi(opts) {
558
580
  if (!fs2.existsSync(dir)) continue;
559
581
  for (const f of fs2.readdirSync(dir)) {
560
582
  if (!f.endsWith(".svelte")) continue;
561
- const full = path2.join(dir, f);
583
+ const full = path3.join(dir, f);
562
584
  if (!isThemeAwareComponent(full)) continue;
563
585
  const name = componentNameFromFile(f);
564
586
  if (!byName.has(name)) byName.set(name, full);
@@ -591,7 +613,7 @@ function themeFileApi(opts) {
591
613
  (m) => ` ${m.token} (referenced by ${m.referencedBy.join(", ")})`
592
614
  );
593
615
  log(
594
- `[live-tokens] ${missing.length} token(s) referenced by components are not defined in ${path2.relative(process.cwd(), CSS_PATH)}:
616
+ `[live-tokens] ${missing.length} token(s) referenced by components are not defined in ${path3.relative(process.cwd(), CSS_PATH)}:
595
617
  ${lines.join("\n")}
596
618
  These render as blank/empty editor slots. Run \`npx live-tokens migrate\` to reconcile.`
597
619
  );
@@ -607,7 +629,7 @@ ${lines.join("\n")}
607
629
  if (!changed) return;
608
630
  fs2.writeFileSync(CSS_PATH, css);
609
631
  log(
610
- `[live-tokens] autoMigrate applied ${applied.length} additive migration(s) to ${path2.relative(process.cwd(), CSS_PATH)}: ${applied.join(", ")}. Review the diff in git.`
632
+ `[live-tokens] autoMigrate applied ${applied.length} additive migration(s) to ${path3.relative(process.cwd(), CSS_PATH)}: ${applied.join(", ")}. Review the diff in git.`
611
633
  );
612
634
  }
613
635
  function generateDefaultConfig(comp, sourcePath) {
@@ -672,14 +694,14 @@ ${lines.join("\n")}
672
694
  const legacy = [];
673
695
  const check = (dir) => {
674
696
  for (const name of ["_active.json", "_production.json"]) {
675
- const p = path2.join(dir, name);
676
- if (fs2.existsSync(p)) legacy.push(path2.relative(process.cwd(), p));
697
+ const p = path3.join(dir, name);
698
+ if (fs2.existsSync(p)) legacy.push(path3.relative(process.cwd(), p));
677
699
  }
678
700
  };
679
701
  check(COLORS_AND_TYPE_DIR);
680
702
  if (fs2.existsSync(COMPONENT_CONFIGS_DIR)) {
681
703
  for (const entry of fs2.readdirSync(COMPONENT_CONFIGS_DIR, { withFileTypes: true })) {
682
- if (entry.isDirectory()) check(path2.join(COMPONENT_CONFIGS_DIR, entry.name));
704
+ if (entry.isDirectory()) check(path3.join(COMPONENT_CONFIGS_DIR, entry.name));
683
705
  }
684
706
  }
685
707
  return legacy;
@@ -687,19 +709,19 @@ ${lines.join("\n")}
687
709
  function warnOnLegacyPointers(legacy, bakeHeld, warn) {
688
710
  if (legacy.length === 0) return;
689
711
  warn(
690
- `[live-tokens] ${legacy.length} legacy pointer file(s) left from the pre-working-set data model, starting with ${legacy[0]}. They are no longer read. Run \`npx live-tokens migrate\` to heal the tree.` + (bakeHeld ? ` Until then this tree has named no production theme, so ${path2.basename(GENERATED_CSS_PATH)} is left exactly as it is rather than rebuilt from the default.` : "")
712
+ `[live-tokens] ${legacy.length} legacy pointer file(s) left from the pre-working-set data model, starting with ${legacy[0]}. They are no longer read. Run \`npx live-tokens migrate\` to heal the tree.` + (bakeHeld ? ` Until then this tree has named no production theme, so ${path3.basename(GENERATED_CSS_PATH)} is left exactly as it is rather than rebuilt from the default.` : "")
691
713
  );
692
714
  }
693
715
  function warnOnLegacyLayout(evidence, warn) {
694
716
  warn(
695
- `[live-tokens] This project still uses the data layout from 0.47 and earlier: ${evidence}. Since 0.48 the whole-theme theme files live in \`themes/\` and the colors and type live in \`colors-and-type/\`. Nothing in the data directory was written, the editor cannot save, and ${path2.basename(GENERATED_CSS_PATH)} is left exactly as it is. Run \`npx live-tokens migrate\` to move the directories and heal the tree, then restart the dev server.`
717
+ `[live-tokens] This project still uses the data layout from 0.47 and earlier: ${evidence}. Since 0.48 the whole-theme theme files live in \`themes/\` and the colors and type live in \`colors-and-type/\`. Nothing in the data directory was written, the editor cannot save, and ${path3.basename(GENERATED_CSS_PATH)} is left exactly as it is. Run \`npx live-tokens migrate\` to move the directories and heal the tree, then restart the dev server.`
696
718
  );
697
719
  }
698
720
  function warnOnUnmigratedSketchStyles(warn) {
699
- const legacyDir = path2.join(dataDirs.dataDir, "sketch-presets");
721
+ const legacyDir = path3.join(dataDirs.dataDir, "sketch-presets");
700
722
  if (!fs2.existsSync(legacyDir)) return;
701
723
  warn(
702
- `[live-tokens] Saved sketchstyles still live under the retired ${path2.basename(legacyDir)}/ directory name. Run \`npx live-tokens migrate\` to rename it to ${path2.basename(SKETCH_STYLES_DIR)}/; until then they will not show up in the Sketchstyle view.`
724
+ `[live-tokens] Saved sketchstyles still live under the retired ${path3.basename(legacyDir)}/ directory name. Run \`npx live-tokens migrate\` to rename it to ${path3.basename(SKETCH_STYLES_DIR)}/; until then they will not show up in the Sketchstyle view.`
703
725
  );
704
726
  }
705
727
  const diskThemeResolvers = {
@@ -731,7 +753,7 @@ ${lines.join("\n")}
731
753
  return "corrupt";
732
754
  }
733
755
  function notAThemeError(fileName) {
734
- return `"${fileName}" is a colors-and-type file, not a theme. Before 0.48 those lived in themes/; move it to ${path2.basename(COLORS_AND_TYPE_DIR)}/ or delete it. To build a theme, use the theme skill "create-theme".`;
756
+ return `"${fileName}" is a colors-and-type file, not a theme. Before 0.48 those lived in themes/; move it to ${path3.basename(COLORS_AND_TYPE_DIR)}/ or delete it. To build a theme, use the theme skill "create-theme".`;
735
757
  }
736
758
  function respondUnreadableTheme(res, fileName, missingError) {
737
759
  switch (unreadableThemeReason(fileName)) {
@@ -753,7 +775,7 @@ ${lines.join("\n")}
753
775
  fs2.writeFileSync(themesResource.filePath(fileName), JSON.stringify(theme, null, 2));
754
776
  }
755
777
  function migrateLocalThemes(warn) {
756
- if (path2.resolve(THEMES_DIR) === path2.resolve(packageThemesDir)) return;
778
+ if (path3.resolve(THEMES_DIR) === path3.resolve(packageThemesDir)) return;
757
779
  if (!fs2.existsSync(THEMES_DIR)) return;
758
780
  for (const file of fs2.readdirSync(THEMES_DIR)) {
759
781
  if (!file.endsWith(".json") || file.startsWith("_")) continue;
@@ -761,7 +783,7 @@ ${lines.join("\n")}
761
783
  if (fileName === "default") continue;
762
784
  let raw;
763
785
  try {
764
- raw = JSON.parse(fs2.readFileSync(path2.join(THEMES_DIR, file), "utf-8"));
786
+ raw = JSON.parse(fs2.readFileSync(path3.join(THEMES_DIR, file), "utf-8"));
765
787
  } catch {
766
788
  continue;
767
789
  }
@@ -1649,7 +1671,9 @@ data: ${JSON.stringify(state)}
1649
1671
  { method: "POST", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
1650
1672
  { method: "DELETE", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed }
1651
1673
  ];
1652
- const isEditorOwnedJson = (file) => file.endsWith(".json") && path2.resolve(file).startsWith(dataDirs.dataDir + path2.sep);
1674
+ const isEditorOwnedJson = (file) => file.endsWith(".json") && path3.resolve(file).startsWith(dataDirs.dataDir + path3.sep);
1675
+ let buildLogger = null;
1676
+ let buildChecked = false;
1653
1677
  return {
1654
1678
  name: "theme-file-api",
1655
1679
  config() {
@@ -1662,6 +1686,15 @@ data: ${JSON.stringify(state)}
1662
1686
  server: { watch: { ignored: [isEditorOwnedJson] } }
1663
1687
  };
1664
1688
  },
1689
+ configResolved(config) {
1690
+ buildLogger = config.command === "build" && opts.checks !== false ? config.logger : null;
1691
+ },
1692
+ async buildStart() {
1693
+ if (!buildLogger || buildChecked) return;
1694
+ buildChecked = true;
1695
+ const failure = await checkBeforeBuild({ root: process.cwd(), logger: buildLogger });
1696
+ if (failure) this.error(failure);
1697
+ },
1665
1698
  configureServer(server) {
1666
1699
  legacyLayout = detectLegacyLayout({
1667
1700
  dataDir: dataDirs.dataDir,
@@ -1703,7 +1736,7 @@ data: ${JSON.stringify(state)}
1703
1736
  },
1704
1737
  handleHotUpdate(ctx) {
1705
1738
  if (legacyLayout) return;
1706
- const normalized = path2.resolve(ctx.file);
1739
+ const normalized = path3.resolve(ctx.file);
1707
1740
  if (!COMPONENTS_SCAN_DIRS.some((d) => normalized.startsWith(d))) return;
1708
1741
  if (!normalized.endsWith(".svelte")) return;
1709
1742
  if (!isThemeAwareComponent(normalized)) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motion-proto/live-tokens",
3
- "version": "0.79.0",
3
+ "version": "0.80.0",
4
4
  "type": "module",
5
5
  "description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
6
6
  "keywords": [
@@ -5,7 +5,7 @@ export const SKILL_DOC = 'SKILL.md' as const;
5
5
 
6
6
  export const skillDocs: Record<string, Record<string, string[]>> = {
7
7
  "check-compliance": {
8
- "SKILL.md": ["---","name: live-tokens-check-compliance","description: Check an existing @motion-proto/live-tokens project against the design system and fix it until check-page and check-component both exit 0. Checks for correct use of components, properties, and tokens. Reads the whole project from one run of npx live-tokens report, runs the token migrations, then runs both checkers, which apply every auto repair and return each remaining finding with its own guidance, repair level, and details. Use when the user asks to check, audit, or review the project. Use when the user asks to fix the project or to make check:design pass. Edits the files the checkers name. Updates tokens.css only through the migration command.","---","","# Checking and fixing a project's adherence to live-tokens","","Check the project, then fix every finding of `check-page` and `check-component` until both exit 0. `check-page` checks pages. Every component comes from the catalogue, every prop is declared, and every value in page CSS is a design token. `check-component` checks the project's own components. Every token names a semantic property, and its default is the design token that property reads. Every finding carries its own `guidance`, and the repair follows it.","","The report and both checkers are static. They read the project's source, never open a browser, and never mount a runtime. `npx live-tokens check-page <file> --tests` proves a page's rendered paint, and `npx live-tokens check-component <id> --tests` proves a component's declared behavior. **live-tokens-create-page** and **live-tokens-create-component** run those, one file or one id at a time, and their findings carry guidance the same way.","","## Workflow","","1. Run `npx live-tokens report --json`. Read it in its own key order: `project`, `migrations`, `components`, `findings.pages`, `findings.components`, `usage`, per Report sections below.","2. Run `npx live-tokens migrate --check` to see the plan, then `npx live-tokens migrate` to apply it. The command brings `tokens.css` up to the installed package and heals the data tree. With `--write` it also rewrites the route references it lists. `--tokens <path>` names a tokens.css in an unusual place.","3. Run both checkers with `--json`. Each first applies every finding whose `repair` is `auto`, checks again, and returns the fixes it applied in `fix.applied` beside the findings that remain. A fix in `fix.skipped` found its text moved, and the next run applies it. `--no-fix` reports without editing, for a build or CI."," ```sh"," npx live-tokens check-page --json"," npx live-tokens check-component --json"," ```","4. Read what remains. Each finding carries the fields under Finding fields, with a `repair` of `choice` or `authored`.","5. Group the findings by rule. Take the largest error group first, then the remaining errors. Take warnings only when the repair scope includes warnings.","6. Make every repair in the group from its `guidance`, within Scope below.","7. Run both checkers again. When repairable findings remain in scope, return to step 5. When no token fits a remaining finding, leave it and continue to the reply with its reason.","8. When the errors are clear, run both checkers with `--strict`. Report what `--strict` adds. Clear warnings within the existing request. Otherwise ask whether to clear the warnings now.","9. When the repair scope includes warnings, return to step 5 with `--strict`. When strict checks pass or the user defers warnings, set up the build script.","10. Add `check:design` to the build, per Build script below.","11. Reply with:"," - the report's picture: pending migrations, component facts, and usage"," - the fixes the checkers applied, each with its count and any visible shift"," - the remaining changes by rule, each with its count and any visible shift"," - the findings left, each with its reason and any config entry the user chose"," - both checker commands with their exit codes","","`check-page <path>` scopes a run to one page. `check-component <id>` scopes a run to one component: its runtime, its editor, and its registration. The checkers read tokens.css from its default location.","","## Report sections","","| Section | Contents |","| --- | --- |","| Pending token migrations (`migrations`) | Whether `tokens.css` is behind the installed package. `status` is `pending`, `none pending`, `no tokens.css`, or `unavailable`. A stale file shows downstream as unknown tokens, so step 2 runs the migrations before either checker. |","| Component facts (`components[]`) | Every component the project has, each with `id`, `origin`, `file`, `registered`, and `tokens` (the count of semantic properties it declares). A component's unread tokens, missing description, and missing registration are the `unread-token`, `missing-description`, and `missing-registration` findings. |","| Checker findings by rule (`findings.pages`, `findings.components`) | Both checkers' findings by rule under the project's severities, and one strict total with every warning as an error. Errors fail the build today. The strict total is what a fully tokenized project would fail. |","| Components each page renders (`usage.byPage`) | Which component each page imports, and how many times the page renders it. When a page renders none, say whether it is chrome or markup that a shipped component covers. |","| Shipped components no page renders (`usage.unusedShipped`) | Information only. |","| Project components unused (`usage.customUnused`) | The project's own components no page renders. |","","## Finding fields","","Every finding from both checkers, under `report --json` and under each checker's `--json`:","","| Field | Value |","| --- | --- |","| `rule`, `severity`, `file`, `line`, `message` | What the finding is and where. |","| `guidance` | How to make the repair: the token for the role or the scale step, the command that prints the candidates, or the section of a create skill that owns the fix. |","| `repair` | `auto`, `choice`, or `authored`. See Repair levels. |","| `exception` | The narrower config entry that records a decision to leave the finding as it is: `{ \"checks\": { \"exclude\": [\"<file>\"] } }` for a page or CSS file, otherwise `{ \"checks\": { \"rules\": { \"<rule>\": \"warn\" } } }`. Applying it steps the rule down one level, error to warn and warn to off. |","| `details` | Per-rule data the message already states in prose, such as the accepted values behind `unknown-prop-value` or the nearest design-token candidates behind `dimension-literal`. Absent when a rule has nothing to add. |","","## Repair levels","","Every finding's `repair` says what moving it costs.","","- **`auto`.** The value determines the fix, such as a spacing literal with one nearest design-token step. The checkers apply it and list it in `fix.applied`.","- **`choice`.** A role or an ambiguous value determines the fix, such as a color literal and the role it plays, or a spacing literal tied between two steps. `details` lists the candidates, and `guidance` says how to pick one.","- **`authored`.** New code is the fix: a runtime that has to start behaving, an editor schema that has to name a token, a route that has to move. `guidance` names what to write. No candidate list applies.","","## Scope","","- Add no token to `tokens.css`. Map a literal with no matching token to the nearest existing token by role. When no token fits, leave the finding and say so.","- When the nearest token differs from the literal, use the token and name the shift in the reply, such as `14px` to `--space-16`.","- Any finding, at any repair level, can stay as a deliberate exception when the user chooses to keep it. Record that decision in the config entry its `exception` field names, and prefer the narrower entry. When the user has chosen to lower a rule's severity, record it in `live-tokens.config.json` under `\"checks\": { \"rules\": { \"<rule>\": \"warn\" } }`. `--off=<rule>` silences a rule for one run only.","","## Build script","","When `package.json` has no `check:design` script, add `\"check:design\": \"live-tokens check-page --no-fix && live-tokens check-component --no-fix\"`. `--no-fix` keeps the build from editing files. When both checkers exit 0, prepend `npm run check:design &&` to the existing build command. Preserve its other build steps."],
8
+ "SKILL.md": ["---","name: live-tokens-check-compliance","description: Check an existing @motion-proto/live-tokens project against the design system and fix it until check-page and check-component both exit 0. Checks for correct use of components, properties, and tokens. Runs both checkers, which bring tokens.css up to the installed package, apply every auto repair, and return each remaining finding with its own guidance, repair level, and details. Use when the user asks to check, audit, or review the project. Use when the user asks to fix the project or to make the build's design checks pass. Edits the files the checkers name. Changes tokens.css only through its migrations.","---","","# Checking and fixing a project's adherence to live-tokens","","Check the project, then fix every finding of `check-page` and `check-component` until both exit 0. `check-page` checks pages. Every component comes from the catalogue, every prop is declared, and every value in page CSS is a design token. `check-component` checks the project's own components. Every token names a semantic property, and its default is the design token that property reads. Every finding carries its own `guidance`, and the repair follows it.","","Both checkers are static. They read the project's source, never open a browser, and never mount a runtime. `npx live-tokens check-page <file> --tests` proves a page's rendered paint, and `npx live-tokens check-component <id> --tests` proves a component's declared behavior. **live-tokens-create-page** and **live-tokens-create-component** run those, one file or one id at a time, and their findings carry guidance the same way.","","## Workflow","","1. Run both checkers with `--json`. Each first brings `tokens.css` up to the installed package by applying every additive migration, then applies every finding whose `repair` is `auto`, checks again, and returns the fixes it applied in `fix.applied` beside the findings that remain. A pending breaking migration returns as a `tokens-breaking-migration` finding, since it renames design tokens the project may read. A fix in `fix.skipped` found its text moved, and the next run applies it. `--no-fix` reports without editing, for a build or CI."," ```sh"," npx live-tokens check-page --json"," npx live-tokens check-component --json"," ```","2. Read what remains. Each finding carries the fields under Finding fields, with a `repair` of `choice` or `authored`.","3. Group the findings by rule. Take the largest error group first, then the remaining errors. Take warnings only when the repair scope includes warnings.","4. Make every repair in the group from its `guidance`, within Scope below.","5. Run both checkers again. When repairable findings remain in scope, return to step 3. When no token fits a remaining finding, leave it and continue to the reply with its reason.","6. When the errors are clear, run both checkers with `--strict`. Report what `--strict` adds. Clear warnings within the existing request. Otherwise ask whether to clear the warnings now.","7. When the repair scope includes warnings, return to step 3 with `--strict`. When strict checks pass or the user defers warnings, continue to the reply.","8. Reply with:"," - the fixes the checkers applied, each with its count and any visible shift"," - the remaining changes by rule, each with its count and any visible shift"," - the findings left, each with its reason and any config entry the user chose"," - both checker commands with their exit codes","","`check-page <path>` scopes a run to one page. `check-component <id>` scopes a run to one component: its runtime, its editor, and its registration. The checkers read tokens.css from its default location.","","## Finding fields","","Every finding from both checkers, under each checker's `--json`:","","| Field | Value |","| --- | --- |","| `rule`, `severity`, `file`, `line`, `message` | What the finding is and where. |","| `guidance` | How to make the repair: the token for the role or the scale step, the command that prints the candidates, or the section of a create skill that owns the fix. |","| `repair` | `auto`, `choice`, or `authored`. See Repair levels. |","| `exception` | The narrower config entry that records a decision to leave the finding as it is: `{ \"checks\": { \"exclude\": [\"<file>\"] } }` for a page or CSS file, otherwise `{ \"checks\": { \"rules\": { \"<rule>\": \"warn\" } } }`. Applying it steps the rule down one level, error to warn and warn to off. |","| `details` | Per-rule data the message already states in prose, such as the accepted values behind `unknown-prop-value` or the nearest design-token candidates behind `dimension-literal`. Absent when a rule has nothing to add. |","","## Repair levels","","Every finding's `repair` says what moving it costs.","","- **`auto`.** The value determines the fix, such as a spacing literal with one nearest design-token step. The checkers apply it and list it in `fix.applied`.","- **`choice`.** A role or an ambiguous value determines the fix, such as a color literal and the role it plays, or a spacing literal tied between two steps. `details` lists the candidates, and `guidance` says how to pick one.","- **`authored`.** New code is the fix: a runtime that has to start behaving, an editor schema that has to name a token, a route that has to move. `guidance` names what to write. No candidate list applies.","","## Scope","","- Add no token to `tokens.css`. Map a literal with no matching token to the nearest existing token by role. When no token fits, leave the finding and say so.","- When the nearest token differs from the literal, use the token and name the shift in the reply, such as `14px` to `--space-16`.","- Apply a `tokens-breaking-migration` finding with `npx live-tokens migrate`, after `npx live-tokens migrate --check` prints the plan. `--tokens <path>` names a tokens.css in an unusual place, and `--write` also rewrites the route references the plan lists. Name each renamed design token in the reply.","- Any finding, at any repair level, can stay as a deliberate exception when the user chooses to keep it. Record that decision in the config entry its `exception` field names, and prefer the narrower entry. When the user has chosen to lower a rule's severity, record it in `live-tokens.config.json` under `\"checks\": { \"rules\": { \"<rule>\": \"warn\" } }`. `--off=<rule>` silences a rule for one run only."],
9
9
  },
10
10
  "create-component": {
11
11
  "SKILL.md": ["---","name: live-tokens-create-component","description: Create an editable component for a @motion-proto/live-tokens project. A component is a runtime Svelte file, an editor Svelte file, and one registration. The runtime file declares one semantic property per editable CSS property and assigns each an existing design token. The property names are semantic, based on function, and reuse the names of the existing components. Use when live-tokens-pick-component finds no suitable component, or the user asks for a new component. Use when the user asks to make an existing Svelte component editable in the live-tokens editor. For page integration, read live-tokens-create-page.","---","","# Creating a component for a live-tokens project","","Create a component whose structure and behavior serve the user's purpose. Give each editable visual property a semantic name. Assign its default from the existing design tokens. Deliver the runtime file, the editor file, and the registration together.","","## Workflow","","1. Read the project: `package.json`, `live-tokens.config.json`, `src/main.ts`, the catalogue, the token scales the component will use, a shipped runtime and editor pair, and the property suffixes.","2. Design the properties: separate the component's parts, variants, and states, then write one row per editable role with its token and the CSS it controls, named the way the shipped components name the same role.","3. Write the runtime file: the catalogue export and the `:global(:root)` block. A structural choice is an intrinsic. Every component joins the sketch layer, and a fixed overlay portals to `<body>`.","4. Write the editor file: the schema, the preview props, and the markup. Variants that share a value are linked.","5. Register the component in the module `src/main.ts` and `live-tokens.testing.ts` both name, and write its contract in the module `contractsModule` names.","6. Run `npx live-tokens check-component <id> --tests --strict --json` until it exits 0 with complete applicable coverage, then the Svelte check and the build.","7. Reply with the files, the id, the props, and each check's result. Then place the component on a page with **live-tokens-create-page**.","","## Design model","","A live-tokens project has two layers.","","| Layer | Responsibility | Example |","|---|---|---|","| Design tokens | Name the available colors, typography, geometry, and motion values. A theme sets the values. | `--space-16`, `--radius-md`, `--surface-neutral` |","| Semantic properties | Name the visual roles within a component and reference tokens. A component config records these assignments. | `--statcard-padding: var(--space-16)` |","","A token is assigned to a property, and a CSS declaration reads the property. The editor changes the assignment; the runtime reads the property. Keep the assignment a token reference, so a theme change reaches the component.","","A property describes its purpose: `--statcard-value`, `--statcard-radius`, `--statcard-label-font-size`. Its name stays stable when its assigned color or size changes. Use role names such as `surface` and `text`. Use full words for component ids and parts.","","A component is distinct in its anatomy, its proportions, its content hierarchy, and its behavior. Its appearance comes from the existing tokens. Create only the variants and states the task requires. The tokens stay as they are; a new token is a separate change to the design system.","","Props carry content and behavior: a value, a label, a callback. Properties carry the editable appearance. When a variant is a choice the page makes, expose it as a prop.","","Prop names follow the shipped components. `label` names a control, `title` heads content, `text` is body copy. `open` is the one prop for an open state. `value` holds a selection's id. A size prop's values are `default` and `small`. A callback prop is `on` followed by the event name, all lowercase, as in `onchange`, `onclose`, and `onsave`.","","## Source inspection","","Before writing a file:","","1. Read the project's `package.json`, `live-tokens.config.json`, and `src/main.ts`.","2. Run `npx live-tokens components`. The list holds every component the project has, with its variants and catalogue entry. `npx live-tokens components <id>` prints one component's props.","3. Run `npx live-tokens tokens --scale <name>` for each token scale the component will use. Those names are the tokens a property can reference.","4. Read a shipped runtime and editor pair: `Toggle` for interaction states, `Badge` for variants and linked values, `Card` for text and container parts.","5. Read `references/token-naming.md` for the suffixes that select editor controls.","","The shipped sources are in `node_modules/@motion-proto/live-tokens/src/`: the runtime at `system/components/<Name>.svelte`, the editor at `editor/component-editor/<Name>Editor.svelte`. Inside the live-tokens repository, read them from the repository root. The source is the contract.","","## Variants and states","","A component has three kinds of division. Keep them apart in the props, the names, and the editor.","","| Kind | Meaning | Example |","|---|---|---|","| Part | Regions present at once | Dialog's overlay, header, body, footer |","| Variant | Alternative presentations the page chooses | Badge's brand, danger |","| State | A runtime condition | Toggle's on, hover, disabled |","","States have two axes. A component state is one of a set that excludes the others: default, selected (or on), disabled. An interaction state layers on a component state: default, hover, and later focus or active. `disabled` is terminal: no other state layers on it, in the names or in the editor.","","The default state carries the shared geometry and typography. A state adds properties only for the values that change: `--toggle-on-track-surface`, `--toggle-on-hover-track-surface`.","","The preview renders the state being edited. Pair each `:hover` selector with a `.force-hover` selector and expose a `class` prop, so the editor shows hover without a pointer. Keep native disabled behavior, keyboard operation, and visible focus on an interactive control.","","A component supplies its variants. The page chooses the one primary action.","","## Property design","","Before writing a file, identify the component's parts, text roles, variants, and states. Then write a property map: one row per editable role, with the token it is assigned and the CSS property it controls. Keep separate roles independent even when they start with the same value.","","| Property | Assigned token | CSS use |","|---|---|---|","| `--statcard-surface` | `--surface-neutral` | `background` |","| `--statcard-border` | `--border-neutral` | `border-color` |","| `--statcard-border-width` | `--border-width-1` | `border-width` |","| `--statcard-radius` | `--radius-md` | `border-radius` |","| `--statcard-padding` | `--space-16` | `padding` |","| `--statcard-value` | `--text-primary` | `color` of the value |","| `--statcard-value-font-size` | `--font-size-2xl` | `font-size` of the value |","| `--statcard-label` | `--text-secondary` | `color` of the label |","","Assign from the tokens the project has. Match the token scale to the role: `--surface-*` for a fill, `--border-*` for an outline, `--text-*` for text, and the space, radius, border-width, and icon-size scales for geometry. Give each text role five properties: `-font-family`, `-font-size`, `-font-weight`, `-line-height`, and `-letter-spacing`.","","A property name starts with the component id and ends with the property suffix. Use this shape for part-specific states:","","```text","--<componentId>[-<variant>][-<part>][-<state>]-<property>","```","","- `componentId` is the runtime file name in lowercase with no dashes: `StatCard.svelte` is `statcard`.","- `variant` is present when the component has more than one: `--card-default-surface`, `--card-bare-surface`. A component with one variant has no variant segment: `--toggle-track-surface`.","- `part` names a region inside the component: `header`, `body`, `track`, `thumb`. The editor's `element` tag groups rows in the panel and is never a name segment.","- `state` comes before the property: `--card-hover-border`. `disabled` is terminal, so no name pairs `disabled` with another state.","- `property` is the suffix, and the suffix selects the editor control: `-surface` for a fill, `-border` for a border color, `-border-width` for a stroke, `-radius` for corners, `-padding` and `-gap` for spacing, and the five typography suffixes. `references/token-naming.md` lists every suffix.","","For a state that affects several parts, follow Toggle: `--toggle-on-hover-track-surface`. State segments precede the affected part.","","Name a role as the shipped component that paints the same thing names it. A fill is `-surface` in every shipped component. A knob is `-thumb`. A text role's color sits on the role's own name, `-title`, `-body`, `-label`, `-value`, and its typography hangs off that name: `--card-default-title-font-size`. A component with one text role uses `-text`: `--badge-brand-text`.","","## Runtime component","","Create `src/system/components/StatCard.svelte`. `check-component` finds a runtime there only. A component in another directory is listed by `components` and `report` when that directory is named in `\"componentDirs\"` in `live-tokens.config.json`, and `check-component` does not check it. Use Svelte 5 props and snippets, semantic HTML, and the behavior the task requires.","","Open the file with a `<script module lang=\"ts\">` block that exports a `catalogue` entry in the shape every shipped component carries. `npx live-tokens components` prints it beside the id, and `components <id>` prints it with the props. Each field is `key: <string literal>`, in single, double, or backtick quotes; no `${}` interpolation, no concatenation, no identifier reference. An optional `props` map adds one line per prop whose values carry a choice, such as `variant`; each key names a prop the file declares, and the text says what the values mean.","","```svelte","<script module lang=\"ts\">"," import type { CatalogueEntry } from '@motion-proto/live-tokens';",""," export const catalogue = {"," description: 'A figure with its label.',"," useFor: 'one number the reader takes in at a glance.',"," notFor: 'a set of records (Table); a titled block of content (Card).',"," } satisfies CatalogueEntry;","</script>","```","","Declare every editable property in a literal `:global(:root)` block, each assigned a token. The plugin parses the Svelte source to seed `component-configs/<id>/default.json`, so the block holds plain declarations with no SCSS loop or interpolation.","","```svelte","<style>"," :global(:root) {"," --statcard-surface: var(--surface-neutral);"," --statcard-border: var(--border-neutral);"," --statcard-border-width: var(--border-width-1);"," --statcard-radius: var(--radius-md);"," --statcard-padding: var(--space-16);"," --statcard-value: var(--text-primary);"," --statcard-value-font-size: var(--font-size-2xl);"," --statcard-label: var(--text-secondary);"," }",""," .statcard {"," display: grid;"," background: var(--statcard-surface);"," border: var(--statcard-border-width) solid var(--statcard-border);"," border-radius: var(--statcard-radius);"," padding: var(--statcard-padding);"," }",""," .value { color: var(--statcard-value); font-size: var(--statcard-value-font-size); }"," .label { color: var(--statcard-label); }","</style>","```","","The excerpt shows the chain for part of the property map. Every editable value reads a property. Structural CSS (`display: grid`, `width: 100%`, `align-items: center`) stays in the layout rules. A value beyond a scale is a token expression: `calc(var(--space-64) * 4)`. A property that carries a structural choice, an alignment or a visibility, is an intrinsic: read `references/intrinsics.md`.","","## Component editor","","Create `src/system/components/StatCardEditor.svelte` beside the runtime file. The editor has three parts.","","1. A `<script module>` block exports `component`, the id, and `allTokens`, one row per property in the map. A row is `{ label, variable, element? }`; `element` groups rows in the panel by part, and `label` names the property in the row.","2. The instance script imports the runtime component and the editor primitives from the package's public paths, and maps the state being edited to preview props.","3. The markup mounts `ComponentEditorBase` with one `VariantGroup` per variant, each rendering a preview.","","```svelte","<script module lang=\"ts\">"," import type { Token } from '@motion-proto/live-tokens/component-editor';",""," export const component = 'statcard';"," const states: Record<string, Token[]> = {"," default: ["," { label: 'surface', element: 'frame', variable: '--statcard-surface' },"," { label: 'border', element: 'frame', variable: '--statcard-border' },"," { label: 'border width', element: 'frame', variable: '--statcard-border-width' },"," { label: 'radius', element: 'frame', variable: '--statcard-radius' },"," { label: 'padding', element: 'frame', variable: '--statcard-padding' },"," { label: 'text', element: 'value', variable: '--statcard-value' },"," { label: 'font size', element: 'value', variable: '--statcard-value-font-size' },"," { label: 'text', element: 'label', variable: '--statcard-label' },"," ],"," };"," export const allTokens: Token[] = Object.values(states).flat();","</script>","","<script lang=\"ts\">"," import { ComponentEditorBase, VariantGroup } from '@motion-proto/live-tokens/component-editor';"," import StatCard from './StatCard.svelte';","</script>","","<ComponentEditorBase {component} title=\"Stat Card\" tokens={allTokens}>"," <VariantGroup name=\"statcard\" title=\"Stat Card\" {states} {component}>"," <StatCard value=\"1,204\" label=\"Sessions\" />"," </VariantGroup>","</ComponentEditorBase>","```","","The shipped editor for the closest component gives the preview snippet for a component with states. Custom chrome in an editor takes `--ui-*` tokens and no accent color; its copy uses periods and commas, never em-dashes.","","When variants share a value, read `references/linked-siblings.md`. A `groupKey` is scoped to the text role: `value-font-size` and `label-font-size` stay separate keys. A `buildTypeGroup*` helper takes `{ component, variants }` so it derives one key per role.","","## Registration","","Register the component in `src/registerComponents.ts`, a registration-only module, beside any registration already there. The id is unique; a registration that repeats a shipped id replaces that component.","","```ts","// src/registerComponents.ts","import { registerComponent } from '@motion-proto/live-tokens';","import { catalogue } from './system/components/StatCard.svelte';","import StatCardEditor, { allTokens as statCardTokens } from './system/components/StatCardEditor.svelte';","","registerComponent({"," id: 'statcard',"," label: 'Stat Card',"," icon: 'fas fa-chart-simple',"," sourceFile: 'src/system/components/StatCard.svelte',"," editorComponent: StatCardEditor,"," schema: statCardTokens,"," catalogue,","});","```","","Import the module from `src/main.ts`, before `bootLiveTokens` or `mount`, and name it as `registrySetup` in `live-tokens.testing.ts`. One module then serves the running app and `check-component --tests`, which imports it to see the registration without mounting the app. The component's contract, one `ComponentContract` in the module `contractsModule` names, is what the eight component suites run; without it `--tests` reports `contract-missing`. Read `references/contract-tests.md` for both. A component that declares intrinsics adds `intrinsics` to the entry. `check-component` finds the registration by the id literal inside the call.","","At boot the plugin reads the `:global(:root)` block and writes `component-configs/<id>/default.json`, one token per property. An edit in the editor writes `_working.json`; Save As writes a named config. The assignments stay token references through that flow.","","Inside the live-tokens repository, a first-party component keeps its editor in `src/editor/component-editor/` and takes an entry in `builtInRegistry` in `src/editor/component-editor/registry.ts`.","","## Sketch mode and overlays","","Every component joins the sketch layer: read `references/sketch-mode.md`. A suitable root or inner wrapper carries a reserved class and names five `--sketch-*` values from its own properties. Preserve positioning, clipping, and pseudo-elements as the reference specifies. A first-party component adds a `PartSpec` row instead.","","A fixed overlay portals to `<body>`: read `references/fixed-overlays.md`. A container that owns the typography of its content follows `Card` and its `prose` prop.","","## Verification","","1. Run `npx live-tokens check-component <id> --tests --strict --json`. Inside the live-tokens repository, run `node bin/cli.mjs check-component <id> --tests --strict --json`. It applies every `auto` repair, runs the registry contract and, for a component with a contract in the module `contractsModule` names, the component contract suites, and returns the fixes it applied, the findings that remain, and coverage by rule. `--off=<rule>` silences a rule for one run, and `--no-fix` reports without editing.","2. Each remaining finding carries a rule id, a line, and its `guidance`. Make each repair from its guidance, and run the command again until it exits 0 with complete applicable coverage and no disabled checks.","3. Run the project's Svelte check and its build.","4. Reply with the files, the component id, the props, and the result of each check, naming any check the environment prevented.","","`--tests` covers every line a reviewer once checked by eye: the component's listing, its controls and preview, persistence and reset, theme projection, linked properties, and Sketch mode.","","Then place the component on a page with **live-tokens-create-page**."],