@motion-proto/live-tokens 0.72.0 → 0.73.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,3691 @@
1
+ import type { SkillTree } from './types';
2
+
3
+ export const skillTrees: Record<string, SkillTree> = {
4
+ "generate-theme": {
5
+ "id": "live-tokens-generate-theme",
6
+ "digest": "sha256:e6b9892d67820148",
7
+ "title": "generate-theme",
8
+ "tagline": "One brief guides color, type, and geometry across three skills.",
9
+ "nodes": [
10
+ {
11
+ "id": "gt-trig",
12
+ "row": 0,
13
+ "kind": "trigger",
14
+ "title": "Define or refine a theme",
15
+ "desc": "A palette, mood, style, occasion, hue, or color refinement drives the theme. The editor handles single tokens; pair-fonts handles type; adjust-geometry handles geometry.",
16
+ "lines": [
17
+ 2,
18
+ 8
19
+ ],
20
+ "anchor": "name: live-tokens-generate-theme",
21
+ "anchorEnd": "A theme is three decisions made from one brief: color, type,"
22
+ },
23
+ {
24
+ "id": "gt-voice",
25
+ "row": 1,
26
+ "kind": "step",
27
+ "n": "1",
28
+ "title": "Define the theme voice",
29
+ "desc": "The voice sentence captures the mood, hue family, scheme, and implied type and geometry. It guides every later choice.",
30
+ "lines": [
31
+ 12,
32
+ 12
33
+ ],
34
+ "anchor": "Read the brief once and name its voice in a sentence: the mo"
35
+ },
36
+ {
37
+ "id": "gt-anchor",
38
+ "row": 2,
39
+ "kind": "decide",
40
+ "title": "Anchor category?",
41
+ "desc": "Anchors define color, type, and geometry and override generic defaults. When two match, the idiom sets constraints and the feeling adjusts its dials.",
42
+ "lines": [
43
+ 13,
44
+ 13
45
+ ],
46
+ "anchor": "Read the anchor reference that matches the voice (feeling, i",
47
+ "n": "2"
48
+ },
49
+ {
50
+ "id": "gt-mood",
51
+ "row": 3,
52
+ "kind": "ref",
53
+ "title": "Read the mood anchor",
54
+ "reference": "references/mood-vocabulary.md",
55
+ "lines": [
56
+ 123,
57
+ 123
58
+ ],
59
+ "anchor": "- `references/mood-vocabulary.md` covers feelings: joyful, p"
60
+ },
61
+ {
62
+ "id": "gt-style",
63
+ "row": 3,
64
+ "kind": "ref",
65
+ "title": "Read the style anchor",
66
+ "reference": "references/style-vocabulary.md",
67
+ "lines": [
68
+ 124,
69
+ 124
70
+ ],
71
+ "anchor": "- `references/style-vocabulary.md` covers named idioms, eras"
72
+ },
73
+ {
74
+ "id": "gt-named",
75
+ "row": 3,
76
+ "kind": "ref",
77
+ "title": "Read the named-theme anchor",
78
+ "reference": "references/named-themes.md",
79
+ "lines": [
80
+ 125,
81
+ 125
82
+ ],
83
+ "anchor": "- `references/named-themes.md` covers holidays, seasons, and"
84
+ },
85
+ {
86
+ "id": "gt-none",
87
+ "row": 3,
88
+ "kind": "ref",
89
+ "title": "Use generic color and geometry rules",
90
+ "lines": [
91
+ 127,
92
+ 127
93
+ ],
94
+ "anchor": "Most briefs hit the first file. A brief that matches two (\"c"
95
+ },
96
+ {
97
+ "id": "gt-seed",
98
+ "row": 4,
99
+ "kind": "step",
100
+ "n": "3",
101
+ "title": "Define ten OKLCH seeds",
102
+ "desc": "scratch/<slug>-brief.json records Brand, Accent, Special, Canvas, Neutral, Alternate, and four status seeds.",
103
+ "lines": [
104
+ 14,
105
+ 14
106
+ ],
107
+ "anchor": "Translate the brief into a seed file using the framework bel"
108
+ },
109
+ {
110
+ "id": "gt-cons",
111
+ "row": 5,
112
+ "kind": "chipset",
113
+ "title": "Apply all palette constraints",
114
+ "chips": [
115
+ {
116
+ "label": "Brief schema",
117
+ "lines": [
118
+ 24,
119
+ 47
120
+ ],
121
+ "anchor": "## The brief",
122
+ "anchorEnd": "Roles: **Brand** is the dominant chromatic identity; **Accen"
123
+ },
124
+ {
125
+ "label": "Chroma budget",
126
+ "lines": [
127
+ 49,
128
+ 58
129
+ ],
130
+ "anchor": "## Chroma budget: color is inversely proportional to area",
131
+ "anchorEnd": "A good theme reads as 3 or 4 hue families on screen, never 1"
132
+ },
133
+ {
134
+ "label": "Role bands",
135
+ "lines": [
136
+ 60,
137
+ 72
138
+ ],
139
+ "anchor": "## Per-role bands",
140
+ "anchorEnd": "| Danger | shared status L, C 0.15 to 0.20 | same | H 20 to "
141
+ },
142
+ {
143
+ "label": "Canvas commitment",
144
+ "lines": [
145
+ 74,
146
+ 87
147
+ ],
148
+ "anchor": "**The canvas carries the theme's identity, so commit to it.*",
149
+ "anchorEnd": ""
150
+ },
151
+ {
152
+ "label": "Mood dials",
153
+ "lines": [
154
+ 88,
155
+ 94
156
+ ],
157
+ "anchor": "## Mood dials",
158
+ "anchorEnd": "Avoid mid-lightness yellow-green (H 100 to 120 at L 0.5 to 0"
159
+ },
160
+ {
161
+ "label": "Gamut guardrails",
162
+ "lines": [
163
+ 96,
164
+ 103
165
+ ],
166
+ "anchor": "## Gamut guardrails",
167
+ "anchorEnd": "- Peak chroma anchors: red H20 C 0.25 at L 0.63; orange H60 "
168
+ },
169
+ {
170
+ "label": "Hue harmony",
171
+ "lines": [
172
+ 105,
173
+ 111
174
+ ],
175
+ "anchor": "## Harmony",
176
+ "anchorEnd": "- Drama or maximum contrast: complementary, triadic, or tetr"
177
+ },
178
+ {
179
+ "label": "Canvas gradient and shadows",
180
+ "lines": [
181
+ 113,
182
+ 117
183
+ ],
184
+ "anchor": "## Canvas sky and shadows",
185
+ "anchorEnd": "Shadow opacity derives from Canvas lightness and re-derives "
186
+ }
187
+ ],
188
+ "tag": "constraints"
189
+ },
190
+ {
191
+ "id": "gt-cli",
192
+ "row": 6,
193
+ "kind": "cli",
194
+ "n": "4",
195
+ "title": "Generate and validate the theme",
196
+ "desc": "The CLI writes and opens themes/<slug>.json and reports contrast. Reruns replace every color, including editor edits. --carry-from <theme> copies gradients, fonts, and component aliases.",
197
+ "lines": [
198
+ 15,
199
+ 15
200
+ ],
201
+ "anchor": "Run `npx live-tokens generate-theme scratch/<slug>-brief.jso",
202
+ "chips": [
203
+ {
204
+ "label": "Flags",
205
+ "lines": [
206
+ 22,
207
+ 22
208
+ ],
209
+ "anchor": "Flags: `--dry-run` prints the report without writing; `--no-"
210
+ }
211
+ ],
212
+ "command": "npx live-tokens generate-theme scratch/<slug>-brief.json"
213
+ },
214
+ {
215
+ "id": "gt-fail",
216
+ "row": 7,
217
+ "kind": "gate",
218
+ "title": "A seed fails validation",
219
+ "desc": "The report names the failed seed. A valid repair raises its lightness or reduces its chroma before the next run.",
220
+ "lines": [
221
+ 15,
222
+ 15
223
+ ],
224
+ "anchor": "Run `npx live-tokens generate-theme scratch/<slug>-brief.jso"
225
+ },
226
+ {
227
+ "id": "gt-pass",
228
+ "row": 7,
229
+ "kind": "ok",
230
+ "title": "Colors pass validation",
231
+ "desc": "The flow continues to the type and geometry decisions.",
232
+ "lines": [
233
+ 15,
234
+ 15
235
+ ],
236
+ "anchor": "Run `npx live-tokens generate-theme scratch/<slug>-brief.jso"
237
+ },
238
+ {
239
+ "id": "gt-type-q",
240
+ "row": 8,
241
+ "kind": "decide",
242
+ "title": "Preserve the current type?",
243
+ "desc": "An explicit color-only instruction preserves current type. Complete themes pair type through pair-fonts.",
244
+ "lines": [
245
+ 16,
246
+ 16
247
+ ],
248
+ "anchor": "Invoke **live-tokens-pair-fonts** with the same voice. Skip "
249
+ },
250
+ {
251
+ "id": "gt-fonts",
252
+ "row": 9,
253
+ "kind": "hand",
254
+ "n": "5",
255
+ "title": "Pair fonts from the same voice",
256
+ "desc": "pair-fonts receives the voice sentence and aligns the type with the theme.",
257
+ "lines": [
258
+ 16,
259
+ 16
260
+ ],
261
+ "anchor": "Invoke **live-tokens-pair-fonts** with the same voice. Skip "
262
+ },
263
+ {
264
+ "id": "gt-geo-q",
265
+ "row": 10,
266
+ "kind": "decide",
267
+ "title": "Does the theme need geometry changes?",
268
+ "desc": "A shape or spacing cue continues to adjust-geometry. A neutral geometry voice preserves current aliases.",
269
+ "lines": [
270
+ 17,
271
+ 17
272
+ ],
273
+ "anchor": "Invoke **live-tokens-adjust-geometry** with the geometry the"
274
+ },
275
+ {
276
+ "id": "gt-geo",
277
+ "row": 12,
278
+ "kind": "hand",
279
+ "n": "6",
280
+ "title": "Adjust geometry from the same voice",
281
+ "desc": "adjust-geometry receives the geometry intent. Anchor geometry overrides the voice table.",
282
+ "lines": [
283
+ 17,
284
+ 17
285
+ ],
286
+ "anchor": "Invoke **live-tokens-adjust-geometry** with the geometry the"
287
+ },
288
+ {
289
+ "id": "gt-geotab",
290
+ "row": 11,
291
+ "kind": "chipset",
292
+ "title": "Prepare the geometry hand-off",
293
+ "chips": [
294
+ {
295
+ "label": "Voice-to-geometry map",
296
+ "lines": [
297
+ 129,
298
+ 140
299
+ ],
300
+ "anchor": "## Geometry from the voice",
301
+ "anchorEnd": "This table is the fallback. When the brief matched an entry "
302
+ },
303
+ {
304
+ "label": "Safe operation order",
305
+ "lines": [
306
+ 20,
307
+ 20
308
+ ],
309
+ "anchor": "Order matters only for safety, and the order above is safe: "
310
+ },
311
+ {
312
+ "label": "Output files",
313
+ "lines": [
314
+ 148,
315
+ 150
316
+ ],
317
+ "anchor": "## Files each step writes",
318
+ "anchorEnd": "Color writes `themes/<slug>.json` and opens it. Type and geo"
319
+ }
320
+ ],
321
+ "tag": "sources"
322
+ },
323
+ {
324
+ "id": "gt-tell",
325
+ "row": 13,
326
+ "kind": "step",
327
+ "n": "7",
328
+ "title": "Reload and review the theme",
329
+ "desc": "The browser holds type and geometry in the working buffer until Save. Further revisions update the same brief.",
330
+ "lines": [
331
+ 18,
332
+ 18
333
+ ],
334
+ "anchor": "Tell the user to look at the running app, and that type and "
335
+ },
336
+ {
337
+ "id": "gt-ver",
338
+ "row": 14,
339
+ "kind": "step",
340
+ "title": "Verify the complete theme",
341
+ "desc": "Colors pass validation, each invoked sibling reports success, and the reloaded app shows the complete theme. Selecting the previous theme restores the prior look.",
342
+ "lines": [
343
+ 152,
344
+ 156
345
+ ],
346
+ "anchor": "## Verify",
347
+ "anchorEnd": "- To return to the previous look, load the theme the CLI out"
348
+ },
349
+ {
350
+ "id": "gt-refine-q",
351
+ "row": 15,
352
+ "kind": "decide",
353
+ "title": "Refine the theme?",
354
+ "desc": "Every refinement updates the existing seed brief. The generator owns the derived theme data.",
355
+ "lines": [
356
+ 142,
357
+ 146
358
+ ],
359
+ "anchor": "## Refining a theme that exists",
360
+ "anchorEnd": "One adjective moves one dial. Warmer and cooler rotate hue; "
361
+ },
362
+ {
363
+ "id": "gt-refine",
364
+ "row": 16,
365
+ "kind": "step",
366
+ "tag": "Follow-up",
367
+ "title": "Refine the seed brief",
368
+ "desc": "The scratch brief supplies seeds. The theme file recovers them after scratch-file loss. Each refinement moves one requested dial and retains the other seeds.",
369
+ "lines": [
370
+ 142,
371
+ 146
372
+ ],
373
+ "anchor": "## Refining a theme that exists",
374
+ "anchorEnd": "One adjective moves one dial. Warmer and cooler rotate hue; "
375
+ },
376
+ {
377
+ "id": "gt-done",
378
+ "row": 16,
379
+ "kind": "done",
380
+ "title": "Theme complete",
381
+ "desc": "generate-theme has written the colors. Saving the open theme keeps any type or geometry changes.",
382
+ "lines": [
383
+ 148,
384
+ 150
385
+ ],
386
+ "anchor": "## Files each step writes",
387
+ "anchorEnd": "Color writes `themes/<slug>.json` and opens it. Type and geo"
388
+ }
389
+ ],
390
+ "edges": [
391
+ {
392
+ "from": "gt-trig",
393
+ "to": "gt-voice"
394
+ },
395
+ {
396
+ "from": "gt-voice",
397
+ "to": "gt-anchor"
398
+ },
399
+ {
400
+ "from": "gt-anchor",
401
+ "to": "gt-mood",
402
+ "label": "feeling"
403
+ },
404
+ {
405
+ "from": "gt-anchor",
406
+ "to": "gt-style",
407
+ "label": "idiom or era"
408
+ },
409
+ {
410
+ "from": "gt-anchor",
411
+ "to": "gt-named",
412
+ "label": "holiday, season, or scene"
413
+ },
414
+ {
415
+ "from": "gt-anchor",
416
+ "to": "gt-none",
417
+ "label": "generic rules"
418
+ },
419
+ {
420
+ "from": "gt-mood",
421
+ "to": "gt-seed"
422
+ },
423
+ {
424
+ "from": "gt-style",
425
+ "to": "gt-seed"
426
+ },
427
+ {
428
+ "from": "gt-named",
429
+ "to": "gt-seed"
430
+ },
431
+ {
432
+ "from": "gt-none",
433
+ "to": "gt-seed"
434
+ },
435
+ {
436
+ "from": "gt-seed",
437
+ "to": "gt-cons"
438
+ },
439
+ {
440
+ "from": "gt-cons",
441
+ "to": "gt-cli"
442
+ },
443
+ {
444
+ "from": "gt-cli",
445
+ "to": "gt-fail",
446
+ "label": "exit 1"
447
+ },
448
+ {
449
+ "from": "gt-cli",
450
+ "to": "gt-pass",
451
+ "label": "exit 0"
452
+ },
453
+ {
454
+ "from": "gt-fail",
455
+ "to": "gt-cli",
456
+ "label": "rerun",
457
+ "back": true
458
+ },
459
+ {
460
+ "from": "gt-pass",
461
+ "to": "gt-type-q"
462
+ },
463
+ {
464
+ "from": "gt-type-q",
465
+ "to": "gt-fonts",
466
+ "label": "pair type"
467
+ },
468
+ {
469
+ "from": "gt-type-q",
470
+ "to": "gt-geo-q",
471
+ "label": "preserve type"
472
+ },
473
+ {
474
+ "from": "gt-fonts",
475
+ "to": "gt-geo-q"
476
+ },
477
+ {
478
+ "from": "gt-geo-q",
479
+ "to": "gt-geotab",
480
+ "label": "change geometry"
481
+ },
482
+ {
483
+ "from": "gt-geotab",
484
+ "to": "gt-geo"
485
+ },
486
+ {
487
+ "from": "gt-geo",
488
+ "to": "gt-tell"
489
+ },
490
+ {
491
+ "from": "gt-geo-q",
492
+ "to": "gt-tell",
493
+ "label": "preserve geometry"
494
+ },
495
+ {
496
+ "from": "gt-tell",
497
+ "to": "gt-ver"
498
+ },
499
+ {
500
+ "from": "gt-ver",
501
+ "to": "gt-refine-q"
502
+ },
503
+ {
504
+ "from": "gt-refine-q",
505
+ "to": "gt-refine",
506
+ "label": "refine"
507
+ },
508
+ {
509
+ "from": "gt-refine-q",
510
+ "to": "gt-done",
511
+ "label": "done"
512
+ },
513
+ {
514
+ "from": "gt-refine",
515
+ "to": "gt-cli",
516
+ "label": "regenerate",
517
+ "back": true
518
+ }
519
+ ]
520
+ },
521
+ "pair-fonts": {
522
+ "id": "live-tokens-pair-fonts",
523
+ "digest": "sha256:f5f807a05b7665e2",
524
+ "title": "pair-fonts",
525
+ "tagline": "Choose the families; the CLI verifies them and builds URLs for their available weights.",
526
+ "nodes": [
527
+ {
528
+ "id": "pf-trig",
529
+ "row": 0,
530
+ "kind": "trigger",
531
+ "title": "Choose or pair font families",
532
+ "desc": "The pairing selects or restyles font families. generate-theme supplies the type voice for complete themes.",
533
+ "lines": [
534
+ 2,
535
+ 6
536
+ ],
537
+ "anchor": "name: live-tokens-pair-fonts",
538
+ "anchorEnd": "# Pairing fonts for a theme"
539
+ },
540
+ {
541
+ "id": "pf-pool",
542
+ "row": 1,
543
+ "kind": "step",
544
+ "title": "Use Google Fonts with the CLI",
545
+ "desc": "The CLI manages Google Fonts. The editor's Project fonts section handles other sources.",
546
+ "lines": [
547
+ 8,
548
+ 8
549
+ ],
550
+ "anchor": "You choose the families; the CLI verifies each against Googl"
551
+ },
552
+ {
553
+ "id": "pf-body",
554
+ "row": 2,
555
+ "kind": "step",
556
+ "title": "Choose the body face first",
557
+ "desc": "Body faces need regular, bold, italic, open apertures, a large x-height, and low or moderate contrast. Families that miss a requirement belong in display.",
558
+ "lines": [
559
+ 29,
560
+ 31
561
+ ],
562
+ "anchor": "## Choose the body face first",
563
+ "anchorEnd": "The body face is the anchor. It carries most of the words, a"
564
+ },
565
+ {
566
+ "id": "pf-spec",
567
+ "row": 3,
568
+ "kind": "decide",
569
+ "title": "Does the brief define a specific voice?",
570
+ "desc": "A specific voice follows the full matrix. A vague or quiet brief takes a conservative pairing.",
571
+ "lines": [
572
+ 65,
573
+ 67
574
+ ],
575
+ "anchor": "## Shortcuts",
576
+ "anchorEnd": "These find an adequate pairing fast and skip the reasoning; "
577
+ },
578
+ {
579
+ "id": "pf-matrix",
580
+ "row": 4,
581
+ "kind": "step",
582
+ "title": "Classify each face by form and finish",
583
+ "desc": "Form describes its model: dynamic, rational, or geometric. Finish describes stroke contrast and serif treatment.",
584
+ "lines": [
585
+ 35,
586
+ 44
587
+ ],
588
+ "anchor": "## The font matrix: the decision rule",
589
+ "anchorEnd": ""
590
+ },
591
+ {
592
+ "id": "pf-short",
593
+ "row": 4,
594
+ "kind": "step",
595
+ "title": "Choose a conservative pairing",
596
+ "desc": "Conservative options include a superfamily, one family across weights, two families by one designer, or a serif display face with a sans body.",
597
+ "lines": [
598
+ 65,
599
+ 72
600
+ ],
601
+ "anchor": "## Shortcuts",
602
+ "anchorEnd": "- **Serif display over sans body** when nothing else decides"
603
+ },
604
+ {
605
+ "id": "pf-rule",
606
+ "row": 5,
607
+ "kind": "chipset",
608
+ "title": "Compare form and finish",
609
+ "chips": [
610
+ {
611
+ "label": "Same form, different finish: reliable",
612
+ "lines": [
613
+ 45,
614
+ 45
615
+ ],
616
+ "anchor": "- **Same skeleton, different flesh: reliable.** Helvetica an"
617
+ },
618
+ {
619
+ "label": "Same finish, different form: clashes",
620
+ "lines": [
621
+ 46,
622
+ 46
623
+ ],
624
+ "anchor": "- **Same flesh, different skeleton: the failure case.** The "
625
+ },
626
+ {
627
+ "label": "Different on both: deliberate contrast",
628
+ "lines": [
629
+ 47,
630
+ 47
631
+ ],
632
+ "anchor": "- **Far apart on both: works, deliberately.** An unmistakabl"
633
+ },
634
+ {
635
+ "label": "Faces between models",
636
+ "lines": [
637
+ 49,
638
+ 49
639
+ ],
640
+ "anchor": "Many faces sit between columns. When one straddles, say so a"
641
+ }
642
+ ],
643
+ "tag": "pairing rule"
644
+ },
645
+ {
646
+ "id": "pf-voice",
647
+ "row": 6,
648
+ "kind": "step",
649
+ "title": "Match the pairing to the theme voice",
650
+ "desc": "The pairing matches type and color to the same brief. Conflicting voices make them feel unrelated.",
651
+ "lines": [
652
+ 51,
653
+ 63
654
+ ],
655
+ "anchor": "## Voice",
656
+ "anchorEnd": "Match the type to the same brief the color came from. A warm"
657
+ },
658
+ {
659
+ "id": "pf-watch",
660
+ "row": 7,
661
+ "kind": "chipset",
662
+ "title": "Check four pairing risks",
663
+ "chips": [
664
+ {
665
+ "label": "x-height parity",
666
+ "lines": [
667
+ 76,
668
+ 76
669
+ ],
670
+ "anchor": "- **x-height parity.** Both faces are set from one size scal"
671
+ },
672
+ {
673
+ "label": "Small-size legibility",
674
+ "lines": [
675
+ 77,
676
+ 77
677
+ ],
678
+ "anchor": "- **Print faces at small sizes.** Delicate serifs and high s"
679
+ },
680
+ {
681
+ "label": "More than two families",
682
+ "lines": [
683
+ 78,
684
+ 78
685
+ ],
686
+ "anchor": "- **Every family is a request.** Two is the target; three ne"
687
+ },
688
+ {
689
+ "label": "Repeated families across theme sets",
690
+ "lines": [
691
+ 79,
692
+ 79
693
+ ],
694
+ "anchor": "- **Sets of themes:** no two share a display face or a body "
695
+ }
696
+ ],
697
+ "lines": [
698
+ 74,
699
+ 74
700
+ ],
701
+ "anchor": "## Watch for",
702
+ "tag": "risks"
703
+ },
704
+ {
705
+ "id": "pf-brief",
706
+ "row": 8,
707
+ "kind": "step",
708
+ "n": "1",
709
+ "title": "Write scratch/font-brief.json",
710
+ "desc": "The brief changes listed slots and preserves the rest. Display maps to --font-display; body maps to --font-sans. Serif, mono, and editorial serve specialized roles.",
711
+ "lines": [
712
+ 12,
713
+ 12
714
+ ],
715
+ "anchor": "Choose the pairing with the framework below and write a brie"
716
+ },
717
+ {
718
+ "id": "pf-shape",
719
+ "row": 9,
720
+ "kind": "chipset",
721
+ "title": "Check font brief fields and flags",
722
+ "chips": [
723
+ {
724
+ "label": "Slots and stacks",
725
+ "lines": [
726
+ 21,
727
+ 27
728
+ ],
729
+ "anchor": "## The brief",
730
+ "anchorEnd": "Every slot is optional and an omitted slot is left exactly a"
731
+ },
732
+ {
733
+ "label": "Pairing rationale",
734
+ "lines": [
735
+ 17,
736
+ 17
737
+ ],
738
+ "anchor": "State your reasoning when you propose the pairing: each face"
739
+ },
740
+ {
741
+ "label": "Flags",
742
+ "lines": [
743
+ 19,
744
+ 19
745
+ ],
746
+ "anchor": "Flags: `--dry-run` reports without writing. `--no-verify` sk"
747
+ }
748
+ ],
749
+ "tag": "brief"
750
+ },
751
+ {
752
+ "id": "pf-cli",
753
+ "row": 10,
754
+ "kind": "cli",
755
+ "n": "2",
756
+ "title": "Validate and apply the font stacks",
757
+ "desc": "The report lists changed stacks, each family's weights and URL, and every required-weight gap.",
758
+ "lines": [
759
+ 13,
760
+ 13
761
+ ],
762
+ "anchor": "Run `npx live-tokens set-fonts scratch/font-brief.json`. It ",
763
+ "command": "npx live-tokens set-fonts scratch/font-brief.json"
764
+ },
765
+ {
766
+ "id": "pf-fail",
767
+ "row": 11,
768
+ "kind": "gate",
769
+ "title": "Google Fonts lacks a family",
770
+ "desc": "The next run uses a valid Google Fonts family name.",
771
+ "lines": [
772
+ 14,
773
+ 14
774
+ ],
775
+ "anchor": "Read the report. A weight gap is a quality note: name it and"
776
+ },
777
+ {
778
+ "id": "pf-gap",
779
+ "row": 11,
780
+ "kind": "ok",
781
+ "n": "3",
782
+ "title": "The CLI applies the font stacks",
783
+ "desc": "Meaningful gaps include body 400, 700, or italic and display 600. Display 300 remains optional.",
784
+ "lines": [
785
+ 14,
786
+ 14
787
+ ],
788
+ "anchor": "Read the report. A weight gap is a quality note: name it and"
789
+ },
790
+ {
791
+ "id": "pf-tell",
792
+ "row": 12,
793
+ "kind": "step",
794
+ "n": "4",
795
+ "title": "Reload and review the type",
796
+ "desc": "Reloading before Save gives the editor the new buffer. Save then keeps the change.",
797
+ "lines": [
798
+ 15,
799
+ 15
800
+ ],
801
+ "anchor": "Tell the user to reload the editor page before saving. A run"
802
+ },
803
+ {
804
+ "id": "pf-scope",
805
+ "row": 13,
806
+ "kind": "chipset",
807
+ "title": "Font-family scope",
808
+ "desc": "Font-family changes preserve color, component aliases, geometry, and the type scale.",
809
+ "chips": [
810
+ {
811
+ "label": "Buffer, Save, and Adopt",
812
+ "lines": [
813
+ 81,
814
+ 83
815
+ ],
816
+ "anchor": "## Scope",
817
+ "anchorEnd": "Type only. Color, component aliases, shape, and the type sca"
818
+ }
819
+ ],
820
+ "tag": "scope"
821
+ },
822
+ {
823
+ "id": "pf-ver",
824
+ "row": 14,
825
+ "kind": "done",
826
+ "title": "Verify the type",
827
+ "desc": "The report lists stacks and weights; the CLI builds matching URLs. Reloading shows the new type. Reselecting the open theme restores the previous type.",
828
+ "lines": [
829
+ 85,
830
+ 90
831
+ ],
832
+ "anchor": "## Verify",
833
+ "anchorEnd": "- To revert, run the inverse brief, or load the open theme a"
834
+ }
835
+ ],
836
+ "edges": [
837
+ {
838
+ "from": "pf-trig",
839
+ "to": "pf-pool"
840
+ },
841
+ {
842
+ "from": "pf-pool",
843
+ "to": "pf-body"
844
+ },
845
+ {
846
+ "from": "pf-body",
847
+ "to": "pf-spec"
848
+ },
849
+ {
850
+ "from": "pf-spec",
851
+ "to": "pf-matrix",
852
+ "label": "specific"
853
+ },
854
+ {
855
+ "from": "pf-spec",
856
+ "to": "pf-short",
857
+ "label": "vague or quiet"
858
+ },
859
+ {
860
+ "from": "pf-matrix",
861
+ "to": "pf-rule"
862
+ },
863
+ {
864
+ "from": "pf-rule",
865
+ "to": "pf-voice"
866
+ },
867
+ {
868
+ "from": "pf-short",
869
+ "to": "pf-voice"
870
+ },
871
+ {
872
+ "from": "pf-voice",
873
+ "to": "pf-watch"
874
+ },
875
+ {
876
+ "from": "pf-watch",
877
+ "to": "pf-brief"
878
+ },
879
+ {
880
+ "from": "pf-brief",
881
+ "to": "pf-shape"
882
+ },
883
+ {
884
+ "from": "pf-shape",
885
+ "to": "pf-cli"
886
+ },
887
+ {
888
+ "from": "pf-cli",
889
+ "to": "pf-fail",
890
+ "label": "exit 1"
891
+ },
892
+ {
893
+ "from": "pf-cli",
894
+ "to": "pf-gap",
895
+ "label": "exit 0"
896
+ },
897
+ {
898
+ "from": "pf-fail",
899
+ "to": "pf-cli",
900
+ "label": "rerun",
901
+ "back": true
902
+ },
903
+ {
904
+ "from": "pf-gap",
905
+ "to": "pf-tell"
906
+ },
907
+ {
908
+ "from": "pf-tell",
909
+ "to": "pf-scope"
910
+ },
911
+ {
912
+ "from": "pf-scope",
913
+ "to": "pf-ver"
914
+ }
915
+ ]
916
+ },
917
+ "adjust-geometry": {
918
+ "id": "live-tokens-adjust-geometry",
919
+ "digest": "sha256:cd28cf6a45c926e2",
920
+ "title": "adjust-geometry",
921
+ "tagline": "The CLI reads a small ops file, moves each alias along its token scale, and reports the result.",
922
+ "nodes": [
923
+ {
924
+ "id": "ag-trig",
925
+ "row": 0,
926
+ "kind": "trigger",
927
+ "title": "Adjust shape or spacing",
928
+ "desc": "Geometry changes cover radius, padding, gaps, border width, and density. generate-theme supplies the geometry intent for complete themes.",
929
+ "lines": [
930
+ 2,
931
+ 8
932
+ ],
933
+ "anchor": "name: live-tokens-adjust-geometry",
934
+ "anchorEnd": "You translate the request into a small ops file; the CLI res"
935
+ },
936
+ {
937
+ "id": "ag-live",
938
+ "row": 1,
939
+ "kind": "step",
940
+ "title": "Read the live configuration",
941
+ "desc": "Each run reads the buffer or falls back to the open theme and shipped default. Relative shifts compound across runs.",
942
+ "lines": [
943
+ 17,
944
+ 17
945
+ ],
946
+ "anchor": "Each run reads the LIVE config (buffer, else the open theme,"
947
+ },
948
+ {
949
+ "id": "ag-target",
950
+ "row": 2,
951
+ "kind": "step",
952
+ "title": "Choose a global or component target",
953
+ "desc": "An absent target applies globally. Component IDs map windows and modals to dialog, cards to card, and tabs to tabbar. Unknown IDs fail.",
954
+ "lines": [
955
+ 34,
956
+ 34
957
+ ],
958
+ "anchor": "- `target` (optional): a component id (the folder names unde"
959
+ },
960
+ {
961
+ "id": "ag-kind",
962
+ "row": 3,
963
+ "kind": "chipset",
964
+ "title": "Choose the geometry property",
965
+ "chips": [
966
+ {
967
+ "label": "radius",
968
+ "lines": [
969
+ 35,
970
+ 35
971
+ ],
972
+ "anchor": "- `kind`: `radius | padding | gap | border-width`."
973
+ },
974
+ {
975
+ "label": "padding",
976
+ "lines": [
977
+ 35,
978
+ 35
979
+ ],
980
+ "anchor": "- `kind`: `radius | padding | gap | border-width`."
981
+ },
982
+ {
983
+ "label": "gap",
984
+ "lines": [
985
+ 35,
986
+ 35
987
+ ],
988
+ "anchor": "- `kind`: `radius | padding | gap | border-width`."
989
+ },
990
+ {
991
+ "label": "border-width",
992
+ "lines": [
993
+ 35,
994
+ 35
995
+ ],
996
+ "anchor": "- `kind`: `radius | padding | gap | border-width`."
997
+ }
998
+ ],
999
+ "tag": "property"
1000
+ },
1001
+ {
1002
+ "id": "ag-op",
1003
+ "row": 4,
1004
+ "kind": "decide",
1005
+ "title": "Set or shift?",
1006
+ "desc": "Set selects a token on the property's scale. Shift moves a whole number of steps and clamps at either end.",
1007
+ "lines": [
1008
+ 36,
1009
+ 36
1010
+ ],
1011
+ "anchor": "- `set` or `shift`, exactly one of the two. `set` takes an e"
1012
+ },
1013
+ {
1014
+ "id": "ag-shift",
1015
+ "row": 5,
1016
+ "kind": "step",
1017
+ "title": "Shift by steps",
1018
+ "desc": "'Slightly' and 'a bit' each move 1 step; an unqualified request moves 1–2; 'much,' 'way,' and 'really' move 2–3. 'Softer' increases radius and spacing.",
1019
+ "lines": [
1020
+ 53,
1021
+ 53
1022
+ ],
1023
+ "anchor": "Magnitude words: \"slightly\" or \"a bit\" is 1 step, unqualifie"
1024
+ },
1025
+ {
1026
+ "id": "ag-set",
1027
+ "row": 5,
1028
+ "kind": "step",
1029
+ "title": "Set an exact token",
1030
+ "desc": "Set selects a token from the property's scale. A pill operation sets --radius-full. The full flag belongs to radius shifts.",
1031
+ "lines": [
1032
+ 37,
1033
+ 37
1034
+ ],
1035
+ "anchor": "- `full` (radius shifts only): admits `--radius-full` as the"
1036
+ },
1037
+ {
1038
+ "id": "ag-idiom",
1039
+ "row": 6,
1040
+ "kind": "step",
1041
+ "title": "Map common phrases to operations",
1042
+ "desc": "The table translates pill, corner, density, and border requests into exact operations.",
1043
+ "lines": [
1044
+ 39,
1045
+ 51
1046
+ ],
1047
+ "anchor": "## Idioms",
1048
+ "anchorEnd": "| thicker, thinner borders | border-width `shift: 1` or `-1`"
1049
+ },
1050
+ {
1051
+ "id": "ag-squeeze",
1052
+ "row": 7,
1053
+ "kind": "step",
1054
+ "title": "Protect controls during compaction",
1055
+ "desc": "Global compaction stops at shift: -1 because larger shifts crush controls. Further compaction targets named containers. Global expansion remains safe.",
1056
+ "lines": [
1057
+ 55,
1058
+ 59
1059
+ ],
1060
+ "anchor": "## Controls squeeze before containers",
1061
+ "anchorEnd": "So a global compaction is `shift: -1`. When the brief wants "
1062
+ },
1063
+ {
1064
+ "id": "ag-pill",
1065
+ "row": 8,
1066
+ "kind": "decide",
1067
+ "title": "Does the request create a pill?",
1068
+ "desc": "--radius-full curves into the end glyphs. Large-text pills need at least --space-8 horizontal padding.",
1069
+ "lines": [
1070
+ 61,
1071
+ 61
1072
+ ],
1073
+ "anchor": "A pill needs the room most. `--radius-full` bends the corner"
1074
+ },
1075
+ {
1076
+ "id": "ag-pillop",
1077
+ "row": 9,
1078
+ "kind": "step",
1079
+ "title": "Set pill radius and padding together",
1080
+ "desc": "The radius and padding operations share a target. The padding set follows global compaction so it wins.",
1081
+ "lines": [
1082
+ 63,
1083
+ 69
1084
+ ],
1085
+ "anchor": "```json",
1086
+ "anchorEnd": "```"
1087
+ },
1088
+ {
1089
+ "id": "ag-ladder",
1090
+ "row": 10,
1091
+ "kind": "chipset",
1092
+ "title": "Respect token scales and padding floors",
1093
+ "chips": [
1094
+ {
1095
+ "label": "Radius, space, and border-width scales",
1096
+ "lines": [
1097
+ 73,
1098
+ 73
1099
+ ],
1100
+ "anchor": "Radius runs `none, sm, md, lg, xl, 2xl, 3xl, 4xl`, with `ful"
1101
+ },
1102
+ {
1103
+ "label": "Content padding floor: --space-4",
1104
+ "lines": [
1105
+ 75,
1106
+ 75
1107
+ ],
1108
+ "anchor": "Content insets stop at `--space-4`. Below it the text sits a"
1109
+ },
1110
+ {
1111
+ "label": "Text-control padding floor: --space-6",
1112
+ "lines": [
1113
+ 77,
1114
+ 77
1115
+ ],
1116
+ "anchor": "Padding that wraps a line of type stops a rung higher, at `-"
1117
+ },
1118
+ {
1119
+ "label": "Margin shifts use the full scale",
1120
+ "lines": [
1121
+ 79,
1122
+ 79
1123
+ ],
1124
+ "anchor": "The floor guards `-padding` only. Outer space is exempt, bec"
1125
+ },
1126
+ {
1127
+ "label": "Off-subset aliases first reach a listed rung",
1128
+ "lines": [
1129
+ 81,
1130
+ 81
1131
+ ],
1132
+ "anchor": "An alias sitting off the subset spends its first step reachi"
1133
+ }
1134
+ ],
1135
+ "lines": [
1136
+ 71,
1137
+ 71
1138
+ ],
1139
+ "anchor": "## Ladders",
1140
+ "tag": "ladders and floors"
1141
+ },
1142
+ {
1143
+ "id": "ag-write",
1144
+ "row": 11,
1145
+ "kind": "step",
1146
+ "n": "1",
1147
+ "title": "Write scratch/adjust-ops.json",
1148
+ "lines": [
1149
+ 12,
1150
+ 12
1151
+ ],
1152
+ "anchor": "Write the ops file to `scratch/adjust-ops.json`."
1153
+ },
1154
+ {
1155
+ "id": "ag-shapes",
1156
+ "row": 12,
1157
+ "kind": "chipset",
1158
+ "title": "Check operation forms",
1159
+ "chips": [
1160
+ {
1161
+ "label": "Global, relative",
1162
+ "lines": [
1163
+ 20,
1164
+ 25
1165
+ ],
1166
+ "anchor": "",
1167
+ "anchorEnd": "```"
1168
+ },
1169
+ {
1170
+ "label": "Targeted, absolute",
1171
+ "lines": [
1172
+ 27,
1173
+ 31
1174
+ ],
1175
+ "anchor": "Targeted, absolute:",
1176
+ "anchorEnd": "```"
1177
+ },
1178
+ {
1179
+ "label": "The CLI ignores name",
1180
+ "lines": [
1181
+ 33,
1182
+ 33
1183
+ ],
1184
+ "anchor": "- `name`: ignored. Buffers are fixed slots, so a name names "
1185
+ }
1186
+ ],
1187
+ "lines": [
1188
+ 19,
1189
+ 19
1190
+ ],
1191
+ "anchor": "## The ops file",
1192
+ "tag": "ops file"
1193
+ },
1194
+ {
1195
+ "id": "ag-cli",
1196
+ "row": 13,
1197
+ "kind": "cli",
1198
+ "n": "2",
1199
+ "title": "Apply the geometry operations",
1200
+ "desc": "The CLI writes each changed component to component-configs/<id>/_working.json, the page's active buffer. --dry-run limits the operation to reporting.",
1201
+ "lines": [
1202
+ 13,
1203
+ 13
1204
+ ],
1205
+ "anchor": "Run `npx live-tokens adjust scratch/adjust-ops.json`. It wri",
1206
+ "command": "npx live-tokens adjust scratch/adjust-ops.json"
1207
+ },
1208
+ {
1209
+ "id": "ag-fail",
1210
+ "row": 14,
1211
+ "kind": "gate",
1212
+ "title": "The CLI rejects an operation",
1213
+ "desc": "The report names the invalid operation or missing input. The repair resolves that issue before the next run.",
1214
+ "lines": [
1215
+ 14,
1216
+ 14
1217
+ ],
1218
+ "anchor": "Read the report card: every changed alias old → new, plus sk"
1219
+ },
1220
+ {
1221
+ "id": "ag-card",
1222
+ "row": 14,
1223
+ "kind": "ok",
1224
+ "n": "3",
1225
+ "title": "The buffers hold the new geometry",
1226
+ "desc": "The report lists every outcome. --space-6 is the padding floor for buttons, badges, inputs, and tabs. Pills at that floor need a targeted lift.",
1227
+ "lines": [
1228
+ 14,
1229
+ 14
1230
+ ],
1231
+ "anchor": "Read the report card: every changed alias old → new, plus sk"
1232
+ },
1233
+ {
1234
+ "id": "ag-tell",
1235
+ "row": 15,
1236
+ "kind": "step",
1237
+ "n": "4",
1238
+ "title": "Reload and review the geometry",
1239
+ "desc": "Reloading before Save gives the editor the current buffer. Save keeps the change, and inverse operations undo it.",
1240
+ "lines": [
1241
+ 15,
1242
+ 15
1243
+ ],
1244
+ "anchor": "Tell the user to reload the page before saving. The editor k"
1245
+ },
1246
+ {
1247
+ "id": "ag-scope",
1248
+ "row": 16,
1249
+ "kind": "chipset",
1250
+ "title": "Geometry scope",
1251
+ "desc": "Geometry operations remap component aliases to existing tokens and preserve colors, fonts, tokens.css, and saved themes.",
1252
+ "chips": [
1253
+ {
1254
+ "label": "Buffers, Save, and Adopt",
1255
+ "lines": [
1256
+ 83,
1257
+ 85
1258
+ ],
1259
+ "anchor": "## Scope",
1260
+ "anchorEnd": "Every value written is an existing token; nothing new is min"
1261
+ }
1262
+ ],
1263
+ "tag": "scope"
1264
+ },
1265
+ {
1266
+ "id": "ag-ver",
1267
+ "row": 17,
1268
+ "kind": "done",
1269
+ "title": "Verify the geometry",
1270
+ "desc": "The command exits 0 with expected changes and explained skips. Controls remain readable after reload. Each reported component has a _working.json.",
1271
+ "lines": [
1272
+ 87,
1273
+ 93
1274
+ ],
1275
+ "anchor": "## Verify",
1276
+ "anchorEnd": "- To revert, run the inverse ops, or load a theme in the The"
1277
+ }
1278
+ ],
1279
+ "edges": [
1280
+ {
1281
+ "from": "ag-trig",
1282
+ "to": "ag-live"
1283
+ },
1284
+ {
1285
+ "from": "ag-live",
1286
+ "to": "ag-target"
1287
+ },
1288
+ {
1289
+ "from": "ag-target",
1290
+ "to": "ag-kind"
1291
+ },
1292
+ {
1293
+ "from": "ag-kind",
1294
+ "to": "ag-op"
1295
+ },
1296
+ {
1297
+ "from": "ag-op",
1298
+ "to": "ag-shift",
1299
+ "label": "shift"
1300
+ },
1301
+ {
1302
+ "from": "ag-op",
1303
+ "to": "ag-set",
1304
+ "label": "set"
1305
+ },
1306
+ {
1307
+ "from": "ag-shift",
1308
+ "to": "ag-idiom"
1309
+ },
1310
+ {
1311
+ "from": "ag-set",
1312
+ "to": "ag-idiom"
1313
+ },
1314
+ {
1315
+ "from": "ag-idiom",
1316
+ "to": "ag-squeeze"
1317
+ },
1318
+ {
1319
+ "from": "ag-squeeze",
1320
+ "to": "ag-pill"
1321
+ },
1322
+ {
1323
+ "from": "ag-pill",
1324
+ "to": "ag-pillop",
1325
+ "label": "pill"
1326
+ },
1327
+ {
1328
+ "from": "ag-pillop",
1329
+ "to": "ag-ladder"
1330
+ },
1331
+ {
1332
+ "from": "ag-ladder",
1333
+ "to": "ag-write"
1334
+ },
1335
+ {
1336
+ "from": "ag-write",
1337
+ "to": "ag-shapes"
1338
+ },
1339
+ {
1340
+ "from": "ag-shapes",
1341
+ "to": "ag-cli"
1342
+ },
1343
+ {
1344
+ "from": "ag-cli",
1345
+ "to": "ag-fail",
1346
+ "label": "exit 1"
1347
+ },
1348
+ {
1349
+ "from": "ag-cli",
1350
+ "to": "ag-card",
1351
+ "label": "exit 0"
1352
+ },
1353
+ {
1354
+ "from": "ag-fail",
1355
+ "to": "ag-cli",
1356
+ "label": "rerun",
1357
+ "back": true
1358
+ },
1359
+ {
1360
+ "from": "ag-card",
1361
+ "to": "ag-tell"
1362
+ },
1363
+ {
1364
+ "from": "ag-tell",
1365
+ "to": "ag-scope"
1366
+ },
1367
+ {
1368
+ "from": "ag-scope",
1369
+ "to": "ag-ver"
1370
+ },
1371
+ {
1372
+ "from": "ag-pill",
1373
+ "to": "ag-ladder",
1374
+ "label": "other geometry"
1375
+ }
1376
+ ]
1377
+ },
1378
+ "pick-component": {
1379
+ "id": "live-tokens-pick-component",
1380
+ "digest": "sha256:8a49229102d83d69",
1381
+ "title": "pick-component",
1382
+ "tagline": "Purpose determines the component. Similar-looking components serve different jobs.",
1383
+ "nodes": [
1384
+ {
1385
+ "id": "pk-trig",
1386
+ "row": 0,
1387
+ "kind": "trigger",
1388
+ "title": "Choose a component for a UX need",
1389
+ "desc": "Purpose, interaction, and content map the need to the catalogue before custom implementation.",
1390
+ "lines": [
1391
+ 2,
1392
+ 8
1393
+ ],
1394
+ "anchor": "name: live-tokens-pick-component",
1395
+ "anchorEnd": "This skill helps you choose between shipped components when "
1396
+ },
1397
+ {
1398
+ "id": "pk-cat",
1399
+ "row": 1,
1400
+ "kind": "step",
1401
+ "title": "Scan the catalogue",
1402
+ "desc": "The catalogue groups 26 shipped components into six families. Slider belongs to the Input family; CodeSnippet presents commands the reader should run.",
1403
+ "lines": [
1404
+ 12,
1405
+ 14
1406
+ ],
1407
+ "anchor": "## Catalogue",
1408
+ "anchorEnd": "Action: `Button`, `IconButton`, `InlineEditActions`. Input: "
1409
+ },
1410
+ {
1411
+ "id": "pk-reg",
1412
+ "row": 2,
1413
+ "kind": "cli",
1414
+ "title": "Inspect available components",
1415
+ "desc": "The list combines shipped and registered project components. A component ID reveals its purpose, variants, props, accepted values, and token defaults.",
1416
+ "lines": [
1417
+ 16,
1418
+ 22
1419
+ ],
1420
+ "anchor": "That line is the shipped set. A project can register compone",
1421
+ "anchorEnd": "union accepts, and its tokens with defaults; `--json` return",
1422
+ "command": "npx live-tokens components"
1423
+ },
1424
+ {
1425
+ "id": "pk-fam",
1426
+ "row": 3,
1427
+ "kind": "decide",
1428
+ "title": "Component job?",
1429
+ "desc": "The component's primary job selects a family and its deciding question.",
1430
+ "lines": [
1431
+ 14,
1432
+ 14
1433
+ ],
1434
+ "anchor": "Action: `Button`, `IconButton`, `InlineEditActions`. Input: "
1435
+ },
1436
+ {
1437
+ "id": "pk-act",
1438
+ "row": 4,
1439
+ "kind": "ask",
1440
+ "tag": "action",
1441
+ "title": "Action content?",
1442
+ "desc": "IconButton handles a clear icon action and requires ariaLabel. Button carries text. InlineEditActions supplies a consistent confirm-and-cancel pair.",
1443
+ "lines": [
1444
+ 24,
1445
+ 31
1446
+ ],
1447
+ "anchor": "## Action family: Button vs IconButton",
1448
+ "anchorEnd": "- `InlineEditActions` is the confirm-and-cancel pair that fo",
1449
+ "chips": [
1450
+ {
1451
+ "label": "Clear icon: IconButton",
1452
+ "lines": [
1453
+ 29,
1454
+ 29
1455
+ ],
1456
+ "anchor": "- `IconButton` is icon-only and square. Use it for compact, "
1457
+ },
1458
+ {
1459
+ "label": "Needs text: Button",
1460
+ "lines": [
1461
+ 28,
1462
+ 28
1463
+ ],
1464
+ "anchor": "- `Button` carries a text label, optionally with a leading o"
1465
+ },
1466
+ {
1467
+ "label": "Confirm and cancel: InlineEditActions",
1468
+ "lines": [
1469
+ 31,
1470
+ 31
1471
+ ],
1472
+ "anchor": "- `InlineEditActions` is the confirm-and-cancel pair that fo"
1473
+ }
1474
+ ]
1475
+ },
1476
+ {
1477
+ "id": "pk-sel",
1478
+ "row": 4,
1479
+ "kind": "ask",
1480
+ "tag": "single selection",
1481
+ "title": "Selection context?",
1482
+ "desc": "SegmentedControl handles 2–4 inline alternatives; TabBar handles 2–7 page panels. RadioButton suits form choices or long labels. MenuSelect contains overflowing options.",
1483
+ "lines": [
1484
+ 33,
1485
+ 47
1486
+ ],
1487
+ "anchor": "## Single-selection family: SegmentedControl vs TabBar vs Ra",
1488
+ "anchorEnd": "- **Don't pick `SegmentedControl` when option labels are lon",
1489
+ "chips": [
1490
+ {
1491
+ "label": "Page content: TabBar",
1492
+ "lines": [
1493
+ 40,
1494
+ 40
1495
+ ],
1496
+ "anchor": "| `TabBar` | Switching between *tab panels* (conten"
1497
+ },
1498
+ {
1499
+ "label": "Inline setting: SegmentedControl",
1500
+ "lines": [
1501
+ 39,
1502
+ 39
1503
+ ],
1504
+ "anchor": "| `SegmentedControl`| Inline switch between alternative *vie"
1505
+ },
1506
+ {
1507
+ "label": "Form choice: RadioButton",
1508
+ "lines": [
1509
+ 41,
1510
+ 41
1511
+ ],
1512
+ "anchor": "| `RadioButton` | Form-style selection where the user re"
1513
+ },
1514
+ {
1515
+ "label": "Many options: MenuSelect",
1516
+ "lines": [
1517
+ 42,
1518
+ 42
1519
+ ],
1520
+ "anchor": "| `MenuSelect` | A list of options, one checked; render"
1521
+ },
1522
+ {
1523
+ "label": "Labels that wrap: RadioButton",
1524
+ "lines": [
1525
+ 47,
1526
+ 47
1527
+ ],
1528
+ "anchor": "- **Don't pick `SegmentedControl` when option labels are lon"
1529
+ }
1530
+ ]
1531
+ },
1532
+ {
1533
+ "id": "pk-text",
1534
+ "row": 4,
1535
+ "kind": "ask",
1536
+ "tag": "text entry",
1537
+ "title": "Answer set?",
1538
+ "desc": "Fixed answers take selection controls; open answers take Input. Slider suits position or two bounds; numeric Input suits values users prefer to type. Input's error state holds validation messages.",
1539
+ "lines": [
1540
+ 49,
1541
+ 55
1542
+ ],
1543
+ "anchor": "## Text entry: Input vs the selection family",
1544
+ "anchorEnd": "- Its four variants are `default`, `focused`, `disabled`, an",
1545
+ "chips": [
1546
+ {
1547
+ "label": "Short list: selection family",
1548
+ "lines": [
1549
+ 52,
1550
+ 52
1551
+ ],
1552
+ "anchor": "- The boundary is whether you can list the answers. A short "
1553
+ },
1554
+ {
1555
+ "label": "Long list: MenuSelect",
1556
+ "lines": [
1557
+ 52,
1558
+ 52
1559
+ ],
1560
+ "anchor": "- The boundary is whether you can list the answers. A short "
1561
+ },
1562
+ {
1563
+ "label": "Open-ended: Input",
1564
+ "lines": [
1565
+ 51,
1566
+ 51
1567
+ ],
1568
+ "anchor": "- `Input` takes an answer the page cannot enumerate: a name,"
1569
+ },
1570
+ {
1571
+ "label": "Position or two bounds: Slider",
1572
+ "lines": [
1573
+ 53,
1574
+ 53
1575
+ ],
1576
+ "anchor": "- `Slider` takes a number inside a known range where the pos"
1577
+ },
1578
+ {
1579
+ "label": "Binary state: Toggle",
1580
+ "lines": [
1581
+ 54,
1582
+ 54
1583
+ ],
1584
+ "anchor": "- **Don't use it for on/off.** That is `Toggle`, and a one-f"
1585
+ }
1586
+ ]
1587
+ },
1588
+ {
1589
+ "id": "pk-con",
1590
+ "row": 4,
1591
+ "kind": "ask",
1592
+ "tag": "containers",
1593
+ "title": "Container role?",
1594
+ "desc": "Card groups content. CollapsibleSection hides secondary content. Panel fixes a stage's height. Dialog pauses page interaction for a focused task.",
1595
+ "lines": [
1596
+ 57,
1597
+ 69
1598
+ ],
1599
+ "anchor": "## Container family: Card vs CollapsibleSection vs Dialog",
1600
+ "anchorEnd": "- **Don't use `Dialog` for routine forms.** Reach for it onl",
1601
+ "chips": [
1602
+ {
1603
+ "label": "Grouped content: Card",
1604
+ "lines": [
1605
+ 66,
1606
+ 66
1607
+ ],
1608
+ "anchor": "- Default to `Card`. It's the workhorse. For full-bleed medi"
1609
+ },
1610
+ {
1611
+ "label": "Secondary content: CollapsibleSection",
1612
+ "lines": [
1613
+ 67,
1614
+ 67
1615
+ ],
1616
+ "anchor": "- Reach for `CollapsibleSection` only when the content is *l"
1617
+ },
1618
+ {
1619
+ "label": "Stage: Panel",
1620
+ "lines": [
1621
+ 68,
1622
+ 68
1623
+ ],
1624
+ "anchor": "- `Panel` is a stage, not a content container. It pins its o"
1625
+ },
1626
+ {
1627
+ "label": "Blocking task: Dialog",
1628
+ "lines": [
1629
+ 69,
1630
+ 69
1631
+ ],
1632
+ "anchor": "- **Don't use `Dialog` for routine forms.** Reach for it onl"
1633
+ }
1634
+ ]
1635
+ },
1636
+ {
1637
+ "id": "pk-msg",
1638
+ "row": 4,
1639
+ "kind": "ask",
1640
+ "tag": "messaging",
1641
+ "title": "Message role?",
1642
+ "desc": "Callout provides persistent content; Notification provides transient feedback. Tooltip supplements visible content. Badge sits inline; CornerBadge overlays another element.",
1643
+ "lines": [
1644
+ 71,
1645
+ 84
1646
+ ],
1647
+ "anchor": "## Messaging family: Callout vs Notification vs Tooltip vs B",
1648
+ "anchorEnd": "- `Badge` and `CornerBadge` differ only in positioning. `Cor",
1649
+ "chips": [
1650
+ {
1651
+ "label": "Persistent content: Callout",
1652
+ "lines": [
1653
+ 81,
1654
+ 81
1655
+ ],
1656
+ "anchor": "- `Callout` is *content*. Part of the section, written into "
1657
+ },
1658
+ {
1659
+ "label": "Transient feedback: Notification",
1660
+ "lines": [
1661
+ 82,
1662
+ 82
1663
+ ],
1664
+ "anchor": "- `Notification` is *feedback*. Appears in response to an ac"
1665
+ },
1666
+ {
1667
+ "label": "Supplementary hint: Tooltip",
1668
+ "lines": [
1669
+ 83,
1670
+ 83
1671
+ ],
1672
+ "anchor": "- `Tooltip` is for *what an element means*. **Don't use `Too"
1673
+ },
1674
+ {
1675
+ "label": "Status: Badge or CornerBadge",
1676
+ "lines": [
1677
+ 84,
1678
+ 84
1679
+ ],
1680
+ "anchor": "- `Badge` and `CornerBadge` differ only in positioning. `Cor"
1681
+ }
1682
+ ]
1683
+ },
1684
+ {
1685
+ "id": "pk-bin",
1686
+ "row": 4,
1687
+ "kind": "ask",
1688
+ "tag": "on or off",
1689
+ "title": "Binary-choice context?",
1690
+ "desc": "Toggle controls one named feature and acts immediately. SegmentedControl compares two named states. RadioButton belongs in a larger form submission.",
1691
+ "lines": [
1692
+ 95,
1693
+ 107
1694
+ ],
1695
+ "anchor": "## Toggle vs SegmentedControl vs RadioButton (for on/off)",
1696
+ "anchorEnd": "- `Toggle` flips immediately; `RadioButton` pair is for form",
1697
+ "chips": [
1698
+ {
1699
+ "label": "Same name: Toggle",
1700
+ "lines": [
1701
+ 105,
1702
+ 105
1703
+ ],
1704
+ "anchor": "- If the off and on states share a name (the feature itself)"
1705
+ },
1706
+ {
1707
+ "label": "Distinct names: SegmentedControl",
1708
+ "lines": [
1709
+ 106,
1710
+ 106
1711
+ ],
1712
+ "anchor": "- If the two states have different names you want users to c"
1713
+ },
1714
+ {
1715
+ "label": "Larger form: RadioButton pair",
1716
+ "lines": [
1717
+ 107,
1718
+ 107
1719
+ ],
1720
+ "anchor": "- `Toggle` flips immediately; `RadioButton` pair is for form"
1721
+ }
1722
+ ]
1723
+ },
1724
+ {
1725
+ "id": "pk-disp",
1726
+ "row": 4,
1727
+ "kind": "ask",
1728
+ "tag": "display",
1729
+ "title": "Display content?",
1730
+ "desc": "Table presents records; Cards present actionable items. ImageLightbox reveals detail; ProgressBar reports progress; CodeSnippet offers copyable commands or values. SideNavigation changes URLs; TabBar changes panels.",
1731
+ "lines": [
1732
+ 86,
1733
+ 93
1734
+ ],
1735
+ "anchor": "## Display family: shown, not asked",
1736
+ "anchorEnd": "- `SectionDivider` separates sections of one page. `SideNavi",
1737
+ "chips": [
1738
+ {
1739
+ "label": "Inline picture: Image",
1740
+ "lines": [
1741
+ 88,
1742
+ 88
1743
+ ],
1744
+ "anchor": "- `Image` frames a picture in the flow at one of four sizes,"
1745
+ },
1746
+ {
1747
+ "label": "Detailed picture: ImageLightbox",
1748
+ "lines": [
1749
+ 89,
1750
+ 89
1751
+ ],
1752
+ "anchor": "- `ImageLightbox` adds click-to-open at full size and takes "
1753
+ },
1754
+ {
1755
+ "label": "Records: Table",
1756
+ "lines": [
1757
+ 90,
1758
+ 90
1759
+ ],
1760
+ "anchor": "- `Table` themes your own rows and cells without owning the "
1761
+ },
1762
+ {
1763
+ "label": "Progress readout: ProgressBar",
1764
+ "lines": [
1765
+ 91,
1766
+ 91
1767
+ ],
1768
+ "anchor": "- `ProgressBar` reports progress against a labelled track. I"
1769
+ },
1770
+ {
1771
+ "label": "Copyable command or value: CodeSnippet",
1772
+ "lines": [
1773
+ 92,
1774
+ 92
1775
+ ],
1776
+ "anchor": "- `CodeSnippet` is for a single-line command or value the re"
1777
+ },
1778
+ {
1779
+ "label": "Sections: SectionDivider; URLs: SideNavigation",
1780
+ "lines": [
1781
+ 93,
1782
+ 93
1783
+ ],
1784
+ "anchor": "- `SectionDivider` separates sections of one page. `SideNavi"
1785
+ }
1786
+ ]
1787
+ },
1788
+ {
1789
+ "id": "pk-fits",
1790
+ "row": 5,
1791
+ "kind": "decide",
1792
+ "title": "Does anything in the catalogue fit?",
1793
+ "desc": "A catalogue fit limits maintenance. A catalogue gap warrants a custom component.",
1794
+ "lines": [
1795
+ 109,
1796
+ 111
1797
+ ],
1798
+ "anchor": "---",
1799
+ "anchorEnd": "If nothing in the catalogue fits (a `DatePicker`, a `Stepper"
1800
+ },
1801
+ {
1802
+ "id": "pk-place",
1803
+ "row": 6,
1804
+ "kind": "hand",
1805
+ "title": "Continue with build-page",
1806
+ "desc": "The page uses the selected component.",
1807
+ "lines": [
1808
+ 10,
1809
+ 10
1810
+ ],
1811
+ "anchor": "For composing a page once you've picked components, see **li"
1812
+ },
1813
+ {
1814
+ "id": "pk-make",
1815
+ "row": 6,
1816
+ "kind": "hand",
1817
+ "title": "Continue with create-component",
1818
+ "desc": "The custom path builds a token-driven DatePicker, Stepper, or other uncovered need.",
1819
+ "lines": [
1820
+ 111,
1821
+ 111
1822
+ ],
1823
+ "anchor": "If nothing in the catalogue fits (a `DatePicker`, a `Stepper"
1824
+ }
1825
+ ],
1826
+ "edges": [
1827
+ {
1828
+ "from": "pk-trig",
1829
+ "to": "pk-cat"
1830
+ },
1831
+ {
1832
+ "from": "pk-cat",
1833
+ "to": "pk-reg"
1834
+ },
1835
+ {
1836
+ "from": "pk-fam",
1837
+ "to": "pk-act",
1838
+ "label": "action"
1839
+ },
1840
+ {
1841
+ "from": "pk-act",
1842
+ "to": "pk-fits"
1843
+ },
1844
+ {
1845
+ "from": "pk-fam",
1846
+ "to": "pk-sel",
1847
+ "label": "selection"
1848
+ },
1849
+ {
1850
+ "from": "pk-sel",
1851
+ "to": "pk-fits"
1852
+ },
1853
+ {
1854
+ "from": "pk-fam",
1855
+ "to": "pk-con",
1856
+ "label": "container"
1857
+ },
1858
+ {
1859
+ "from": "pk-con",
1860
+ "to": "pk-fits"
1861
+ },
1862
+ {
1863
+ "from": "pk-fam",
1864
+ "to": "pk-msg",
1865
+ "label": "message"
1866
+ },
1867
+ {
1868
+ "from": "pk-msg",
1869
+ "to": "pk-fits"
1870
+ },
1871
+ {
1872
+ "from": "pk-fam",
1873
+ "to": "pk-bin",
1874
+ "label": "on/off"
1875
+ },
1876
+ {
1877
+ "from": "pk-bin",
1878
+ "to": "pk-fits"
1879
+ },
1880
+ {
1881
+ "from": "pk-fits",
1882
+ "to": "pk-place",
1883
+ "label": "catalogue fit"
1884
+ },
1885
+ {
1886
+ "from": "pk-fits",
1887
+ "to": "pk-make",
1888
+ "label": "catalogue gap"
1889
+ },
1890
+ {
1891
+ "from": "pk-fam",
1892
+ "to": "pk-text",
1893
+ "label": "text entry"
1894
+ },
1895
+ {
1896
+ "from": "pk-text",
1897
+ "to": "pk-fits"
1898
+ },
1899
+ {
1900
+ "from": "pk-fam",
1901
+ "to": "pk-disp",
1902
+ "label": "display"
1903
+ },
1904
+ {
1905
+ "from": "pk-disp",
1906
+ "to": "pk-fits"
1907
+ },
1908
+ {
1909
+ "from": "pk-reg",
1910
+ "to": "pk-fam"
1911
+ }
1912
+ ]
1913
+ },
1914
+ "build-page": {
1915
+ "id": "live-tokens-build-page",
1916
+ "digest": "sha256:9690053df3487413",
1917
+ "title": "build-page",
1918
+ "tagline": "Catalogue components cover established needs. Theme tokens drive every theme-owned value.",
1919
+ "nodes": [
1920
+ {
1921
+ "id": "bp-trig",
1922
+ "row": 0,
1923
+ "kind": "trigger",
1924
+ "title": "Build a page, route, or screen",
1925
+ "desc": "The page combines catalogue components and theme tokens, then adds routing and repaint verification.",
1926
+ "lines": [
1927
+ 2,
1928
+ 8
1929
+ ],
1930
+ "anchor": "name: live-tokens-build-page",
1931
+ "anchorEnd": "Two rules above all else:"
1932
+ },
1933
+ {
1934
+ "id": "bp-fit",
1935
+ "row": 2,
1936
+ "kind": "decide",
1937
+ "n": "1",
1938
+ "title": "Does an existing component fit?",
1939
+ "desc": "A catalogue component enters contract inspection. A catalogue gap opens the custom UI paths.",
1940
+ "lines": [
1941
+ 10,
1942
+ 10
1943
+ ],
1944
+ "anchor": "**Use a shipped component if one fits.** Import from `@motio"
1945
+ },
1946
+ {
1947
+ "id": "bp-pick",
1948
+ "row": 1,
1949
+ "kind": "hand",
1950
+ "title": "Check with pick-component",
1951
+ "desc": "pick-component tests the need against shipped and registered project components.",
1952
+ "lines": [
1953
+ 10,
1954
+ 10
1955
+ ],
1956
+ "anchor": "**Use a shipped component if one fits.** Import from `@motio"
1957
+ },
1958
+ {
1959
+ "id": "bp-use",
1960
+ "row": 3,
1961
+ "kind": "step",
1962
+ "title": "Import the existing component",
1963
+ "desc": "Shipped components come from @motion-proto/live-tokens/components/<Name>.svelte; project components come from their local path.",
1964
+ "lines": [
1965
+ 10,
1966
+ 10
1967
+ ],
1968
+ "anchor": "**Use a shipped component if one fits.** Import from `@motio"
1969
+ },
1970
+ {
1971
+ "id": "bp-custom-q",
1972
+ "row": 3,
1973
+ "kind": "decide",
1974
+ "title": "Custom UI scope?",
1975
+ "desc": "Page-local markup suits one-off layout. create-component supports reusable UI and Live Tokens editing.",
1976
+ "lines": [
1977
+ 10,
1978
+ 10
1979
+ ],
1980
+ "anchor": "**Use a shipped component if one fits.** Import from `@motio"
1981
+ },
1982
+ {
1983
+ "id": "bp-local",
1984
+ "row": 4,
1985
+ "kind": "step",
1986
+ "title": "Write page-local markup",
1987
+ "desc": "The markup follows the page's token, typography, grid, routing, and validation rules.",
1988
+ "lines": [
1989
+ 10,
1990
+ 10
1991
+ ],
1992
+ "anchor": "**Use a shipped component if one fits.** Import from `@motio"
1993
+ },
1994
+ {
1995
+ "id": "bp-make",
1996
+ "row": 4,
1997
+ "kind": "hand",
1998
+ "title": "Create the missing component",
1999
+ "desc": "create-component builds reusable, editable UI. The completed component returns here for contract inspection.",
2000
+ "lines": [
2001
+ 10,
2002
+ 10
2003
+ ],
2004
+ "anchor": "**Use a shipped component if one fits.** Import from `@motio"
2005
+ },
2006
+ {
2007
+ "id": "bp-props",
2008
+ "row": 5,
2009
+ "kind": "cli",
2010
+ "title": "Inspect the component contract",
2011
+ "desc": "The component receives declared props and accepted variant and size values. The checker flags unsupported values before the runtime drops them.",
2012
+ "lines": [
2013
+ 10,
2014
+ 10
2015
+ ],
2016
+ "anchor": "**Use a shipped component if one fits.** Import from `@motio",
2017
+ "command": "npx live-tokens components <id>"
2018
+ },
2019
+ {
2020
+ "id": "bp-tok",
2021
+ "row": 6,
2022
+ "kind": "step",
2023
+ "n": "2",
2024
+ "title": "Use tokens for theme-owned values",
2025
+ "desc": "Theme tokens supply colours, spacing, strokes, radii, shadows, font sizes, and font families in <style>, style=, and style: directives. Literals remain for layout dimensions.",
2026
+ "lines": [
2027
+ 11,
2028
+ 11
2029
+ ],
2030
+ "anchor": "**Use theme tokens for every value.** Every color, spacing, "
2031
+ },
2032
+ {
2033
+ "id": "bp-text",
2034
+ "row": 7,
2035
+ "kind": "step",
2036
+ "title": "Use a complete text-style token",
2037
+ "desc": "The --heading-*, --body-*, --editorial-*, --eyebrow, and --code tokens set family, size, weight, line height, and letter spacing together. Theme changes update the complete style.",
2038
+ "lines": [
2039
+ 13,
2040
+ 13
2041
+ ],
2042
+ "anchor": "For text, reach for a whole text style rather than assemblin"
2043
+ },
2044
+ {
2045
+ "id": "bp-prose-q",
2046
+ "row": 8,
2047
+ "kind": "decide",
2048
+ "title": "Container prose override?",
2049
+ "desc": "Card and CollapsibleSection style nested prose by default. Page-owned text and full-bleed media disable prose.",
2050
+ "lines": [
2051
+ 15,
2052
+ 15
2053
+ ],
2054
+ "anchor": "Text inside a `Card` or a `CollapsibleSection` is typed by"
2055
+ },
2056
+ {
2057
+ "id": "bp-prose",
2058
+ "row": 9,
2059
+ "kind": "step",
2060
+ "title": "Pass prose={false}",
2061
+ "desc": "This prop gives the page control of nested text and full-bleed media.",
2062
+ "lines": [
2063
+ 15,
2064
+ 15
2065
+ ],
2066
+ "anchor": "Text inside a `Card` or a `CollapsibleSection` is typed by"
2067
+ },
2068
+ {
2069
+ "id": "bp-grid",
2070
+ "row": 12,
2071
+ "kind": "step",
2072
+ "title": "Use the page column grid",
2073
+ "desc": "The page uses --columns-count, --columns-gutter, and --columns-max-width. Page-aligned children span 1 / -1 and redeclare repeat(var(--columns-count), 1fr) with --columns-gutter.",
2074
+ "lines": [
2075
+ 35,
2076
+ 37
2077
+ ],
2078
+ "anchor": "Pages sit inside the column grid via `--columns-count`, `--c",
2079
+ "anchorEnd": "To place children at specific page-column positions, span th"
2080
+ },
2081
+ {
2082
+ "id": "bp-wire",
2083
+ "row": 15,
2084
+ "kind": "decide",
2085
+ "title": "Does the app use LiveTokensRouter?",
2086
+ "desc": "The route follows App.svelte's pattern and lazy-loads the page. Static imports evaluate every page module at startup and leak CSS into editor routes.",
2087
+ "lines": [
2088
+ 53,
2089
+ 58
2090
+ ],
2091
+ "anchor": "## Wiring",
2092
+ "anchorEnd": "Either way use `lazy`, not a static top-level import: static"
2093
+ },
2094
+ {
2095
+ "id": "bp-router",
2096
+ "row": 16,
2097
+ "kind": "step",
2098
+ "title": "Add a lazy page entry",
2099
+ "desc": "The entry sets lazy and source. label and icon add navigation; resolve(path) handles dynamic routes. A URL-only route omits label.",
2100
+ "lines": [
2101
+ 56,
2102
+ 56
2103
+ ],
2104
+ "anchor": "- **`<LiveTokensRouter pages={...}>`** (the usual case): add"
2105
+ },
2106
+ {
2107
+ "id": "bp-overlay",
2108
+ "row": 16,
2109
+ "kind": "step",
2110
+ "title": "Wire LiveEditorOverlay manually",
2111
+ "desc": "$derived.by(() => import(...)) loads the page, and pageSources registers its source.",
2112
+ "lines": [
2113
+ 57,
2114
+ 57
2115
+ ],
2116
+ "anchor": "- **Manual `<LiveEditorOverlay>`**: dispatch with `$derived."
2117
+ },
2118
+ {
2119
+ "id": "bp-css",
2120
+ "row": 17,
2121
+ "kind": "step",
2122
+ "title": "Import site.css in each page",
2123
+ "desc": "Each page imports site.css in its script block. A main.ts import leaks page CSS into editor routes.",
2124
+ "lines": [
2125
+ 59,
2126
+ 59
2127
+ ],
2128
+ "anchor": "- Import `site.css` from each page's `<script>` block, never"
2129
+ },
2130
+ {
2131
+ "id": "bp-avoid",
2132
+ "row": 18,
2133
+ "kind": "chipset",
2134
+ "title": "Follow seven page safeguards",
2135
+ "chips": [
2136
+ {
2137
+ "label": "Theme tokens for colour and geometry",
2138
+ "lines": [
2139
+ 78,
2140
+ 78
2141
+ ],
2142
+ "anchor": "- Colour literals, and px or rem in spacing, stroke, radius,"
2143
+ },
2144
+ {
2145
+ "label": "Page grids use --columns-count",
2146
+ "lines": [
2147
+ 79,
2148
+ 79
2149
+ ],
2150
+ "anchor": "- Hardcoded page-grid counts (`repeat(10, 1fr)`). Use `repea"
2151
+ },
2152
+ {
2153
+ "label": "Component changes through the editor",
2154
+ "lines": [
2155
+ 80,
2156
+ 80
2157
+ ],
2158
+ "anchor": "- Utility classes overriding shipped components. Extend via "
2159
+ },
2160
+ {
2161
+ "label": "Label the box with a text style",
2162
+ "lines": [
2163
+ 81,
2164
+ 81
2165
+ ],
2166
+ "anchor": "- A card header as a section label in a tool UI, and a page "
2167
+ },
2168
+ {
2169
+ "label": "Public package imports",
2170
+ "lines": [
2171
+ 82,
2172
+ 82
2173
+ ],
2174
+ "anchor": "- Deep imports from `node_modules/@motion-proto/live-tokens/"
2175
+ },
2176
+ {
2177
+ "label": "Editor routes own editor mounts",
2178
+ "lines": [
2179
+ 83,
2180
+ 83
2181
+ ],
2182
+ "anchor": "- Mounting `Editor` or `ComponentEditorPage` outside their d"
2183
+ },
2184
+ {
2185
+ "label": "The package owns /live-tokens/* routes",
2186
+ "lines": [
2187
+ 84,
2188
+ 84
2189
+ ],
2190
+ "anchor": "- A page route under `/live-tokens/*`. That namespace is re"
2191
+ }
2192
+ ],
2193
+ "lines": [
2194
+ 76,
2195
+ 76
2196
+ ],
2197
+ "anchor": "## Avoid",
2198
+ "tag": "safeguards"
2199
+ },
2200
+ {
2201
+ "id": "bp-check",
2202
+ "row": 19,
2203
+ "kind": "cli",
2204
+ "title": "Check the page",
2205
+ "desc": "The checker rejects invalid components, props, imports, tokens, colours, and routes. Strict mode adds px/rem geometry, fixed page columns, raw type axes, and source-free routes.",
2206
+ "lines": [
2207
+ 88,
2208
+ 97
2209
+ ],
2210
+ "anchor": "Run the checker and fix what it reports. Repeat until it exi",
2211
+ "anchorEnd": "Warnings do not fail the run. `--strict` makes them fail, wh",
2212
+ "command": "npx live-tokens check-page src/pages/YourPage.svelte --strict --json"
2213
+ },
2214
+ {
2215
+ "id": "bp-fail",
2216
+ "row": 20,
2217
+ "kind": "gate",
2218
+ "title": "Fix page-check findings",
2219
+ "desc": "Each repair clears one reported rule before the next run.",
2220
+ "lines": [
2221
+ 88,
2222
+ 97
2223
+ ],
2224
+ "anchor": "Run the checker and fix what it reports. Repeat until it exi",
2225
+ "anchorEnd": "Warnings do not fail the run. `--strict` makes them fail, wh"
2226
+ },
2227
+ {
2228
+ "id": "bp-pass",
2229
+ "row": 20,
2230
+ "kind": "ok",
2231
+ "title": "Page check passes",
2232
+ "desc": "The page meets its component, token, import, and routing contracts.",
2233
+ "lines": [
2234
+ 88,
2235
+ 97
2236
+ ],
2237
+ "anchor": "Run the checker and fix what it reports. Repeat until it exi",
2238
+ "anchorEnd": "Warnings do not fail the run. `--strict` makes them fail, wh"
2239
+ },
2240
+ {
2241
+ "id": "bp-ver",
2242
+ "row": 21,
2243
+ "kind": "done",
2244
+ "title": "Verify the page repaints",
2245
+ "desc": "A colour change confirms repainting. Page Source confirms the route source. The columns overlay confirms that content stays within --columns-max-width.",
2246
+ "lines": [
2247
+ 103,
2248
+ 103
2249
+ ],
2250
+ "anchor": "Then in dev: change a colour in `/live-tokens/editor` and co"
2251
+ },
2252
+ {
2253
+ "id": "bp-laws",
2254
+ "row": 10,
2255
+ "kind": "step",
2256
+ "title": "The purpose of a layout",
2257
+ "desc": "The page shows one thing; each mark that is not content must earn its place. Separate with the smallest difference that separates: space, then a hairline rule, then a second surface. Content, labels, and scaffolding each take their own token. On a tool page the stage takes the space and controls take the smallest size that still works.",
2258
+ "reference": "references/layout-sources.md",
2259
+ "lines": [
2260
+ 19,
2261
+ 29
2262
+ ],
2263
+ "anchor": "**The purpose of a layout.** The page shows one thing. All o",
2264
+ "anchorEnd": "`references/layout-sources.md` names the sources for these l"
2265
+ },
2266
+ {
2267
+ "id": "bp-bands",
2268
+ "row": 11,
2269
+ "kind": "step",
2270
+ "title": "Name the bands by their job",
2271
+ "desc": "Name each band by its job: what the user looks at, types into, presses. A tool page runs stage, inputs, then one toolbar along the bottom edge. Separate bands with space and a rule; stretch a band's boxes to one height so their bottom edges make one line.",
2272
+ "reference": "references/layout-sources.md",
2273
+ "lines": [
2274
+ 31,
2275
+ 33
2276
+ ],
2277
+ "anchor": "Decide the bands before the columns. Read the page top to bo",
2278
+ "anchorEnd": "Separate bands with space and a rule, `padding-top: var(--sp"
2279
+ },
2280
+ {
2281
+ "id": "bp-contain",
2282
+ "row": 13,
2283
+ "kind": "step",
2284
+ "title": "Containers by job",
2285
+ "desc": "Panel is a stage. Card is a titled block of content, typed by its own tokens; compact drops a size. A box in a tool UI is a bare compact Card labelled from a text style. A toolbar is a flex row of small buttons with no card around it.",
2286
+ "reference": "references/layout-sources.md",
2287
+ "lines": [
2288
+ 39,
2289
+ 44
2290
+ ],
2291
+ "anchor": "### Containers by job",
2292
+ "anchorEnd": "- A toolbar is a flex row of small buttons on the band's bot"
2293
+ },
2294
+ {
2295
+ "id": "bp-density",
2296
+ "row": 14,
2297
+ "kind": "step",
2298
+ "title": "Density",
2299
+ "desc": "size=\"small\" in toolbars and compose rows; fullWidth comes off in a row. A custom wrapper forwards size. Text in a card body inherits the card's size unless typed. MenuSelect renders open; a picker toggles it from a Button.",
2300
+ "reference": "references/layout-sources.md",
2301
+ "lines": [
2302
+ 46,
2303
+ 51
2304
+ ],
2305
+ "anchor": "### Density",
2306
+ "anchorEnd": "- `MenuSelect` renders its list open. For a picker, toggle i"
2307
+ }
2308
+ ],
2309
+ "edges": [
2310
+ {
2311
+ "from": "bp-trig",
2312
+ "to": "bp-pick"
2313
+ },
2314
+ {
2315
+ "from": "bp-pick",
2316
+ "to": "bp-fit"
2317
+ },
2318
+ {
2319
+ "from": "bp-fit",
2320
+ "to": "bp-use",
2321
+ "label": "catalogue fit"
2322
+ },
2323
+ {
2324
+ "from": "bp-fit",
2325
+ "to": "bp-custom-q",
2326
+ "label": "catalogue gap"
2327
+ },
2328
+ {
2329
+ "from": "bp-custom-q",
2330
+ "to": "bp-local",
2331
+ "label": "one-off page"
2332
+ },
2333
+ {
2334
+ "from": "bp-custom-q",
2335
+ "to": "bp-make",
2336
+ "label": "reusable or editable"
2337
+ },
2338
+ {
2339
+ "from": "bp-use",
2340
+ "to": "bp-props"
2341
+ },
2342
+ {
2343
+ "from": "bp-make",
2344
+ "to": "bp-props"
2345
+ },
2346
+ {
2347
+ "from": "bp-local",
2348
+ "to": "bp-tok"
2349
+ },
2350
+ {
2351
+ "from": "bp-tok",
2352
+ "to": "bp-text"
2353
+ },
2354
+ {
2355
+ "from": "bp-text",
2356
+ "to": "bp-prose-q"
2357
+ },
2358
+ {
2359
+ "from": "bp-prose-q",
2360
+ "to": "bp-prose",
2361
+ "label": "page text or full bleed"
2362
+ },
2363
+ {
2364
+ "from": "bp-wire",
2365
+ "to": "bp-router",
2366
+ "label": "LiveTokensRouter"
2367
+ },
2368
+ {
2369
+ "from": "bp-wire",
2370
+ "to": "bp-overlay",
2371
+ "label": "manual overlay"
2372
+ },
2373
+ {
2374
+ "from": "bp-router",
2375
+ "to": "bp-css"
2376
+ },
2377
+ {
2378
+ "from": "bp-overlay",
2379
+ "to": "bp-css"
2380
+ },
2381
+ {
2382
+ "from": "bp-css",
2383
+ "to": "bp-avoid"
2384
+ },
2385
+ {
2386
+ "from": "bp-avoid",
2387
+ "to": "bp-check"
2388
+ },
2389
+ {
2390
+ "from": "bp-props",
2391
+ "to": "bp-tok"
2392
+ },
2393
+ {
2394
+ "from": "bp-check",
2395
+ "to": "bp-fail",
2396
+ "label": "exit 1"
2397
+ },
2398
+ {
2399
+ "from": "bp-check",
2400
+ "to": "bp-pass",
2401
+ "label": "exit 0"
2402
+ },
2403
+ {
2404
+ "from": "bp-pass",
2405
+ "to": "bp-ver"
2406
+ },
2407
+ {
2408
+ "from": "bp-fail",
2409
+ "to": "bp-check",
2410
+ "label": "rerun",
2411
+ "back": true
2412
+ },
2413
+ {
2414
+ "from": "bp-prose-q",
2415
+ "to": "bp-laws",
2416
+ "label": "default handling"
2417
+ },
2418
+ {
2419
+ "from": "bp-prose",
2420
+ "to": "bp-laws"
2421
+ },
2422
+ {
2423
+ "from": "bp-laws",
2424
+ "to": "bp-bands"
2425
+ },
2426
+ {
2427
+ "from": "bp-bands",
2428
+ "to": "bp-grid"
2429
+ },
2430
+ {
2431
+ "from": "bp-grid",
2432
+ "to": "bp-contain"
2433
+ },
2434
+ {
2435
+ "from": "bp-contain",
2436
+ "to": "bp-density"
2437
+ },
2438
+ {
2439
+ "from": "bp-density",
2440
+ "to": "bp-wire"
2441
+ }
2442
+ ]
2443
+ },
2444
+ "create-component": {
2445
+ "id": "live-tokens-create-component",
2446
+ "digest": "sha256:d4d3024f29d6580e",
2447
+ "title": "create-component",
2448
+ "tagline": "A runtime/editor pair exposes full token editing in the CUSTOM catalogue.",
2449
+ "nodes": [
2450
+ {
2451
+ "id": "cc-trig",
2452
+ "row": 0,
2453
+ "kind": "trigger",
2454
+ "title": "Create or expose an editable component",
2455
+ "desc": "A runtime/editor pair adds token editing to a new or existing Svelte component after the catalogue check.",
2456
+ "lines": [
2457
+ 2,
2458
+ 8
2459
+ ],
2460
+ "anchor": "name: live-tokens-create-component",
2461
+ "anchorEnd": "The end state is a runtime Svelte file, an editor Svelte fil"
2462
+ },
2463
+ {
2464
+ "id": "cc-read",
2465
+ "row": 1,
2466
+ "kind": "step",
2467
+ "title": "Study the closest shipped example",
2468
+ "desc": "Shipped patterns include Card parts, Badge variants, Button states, Dialog structure, and SegmentedControl linked siblings. Custom files live in src/system/components/.",
2469
+ "lines": [
2470
+ 10,
2471
+ 22
2472
+ ],
2473
+ "anchor": "## Worked examples ship inside the package",
2474
+ "anchorEnd": "Shipped editors live in `src/editor/component-editor/` becau"
2475
+ },
2476
+ {
2477
+ "id": "cc-run",
2478
+ "row": 3,
2479
+ "kind": "step",
2480
+ "n": "1",
2481
+ "title": "Write the runtime file",
2482
+ "desc": "Every editable CSS variable lives in :global(:root) with a theme-token default. The plugin seeds default.json from this block.",
2483
+ "lines": [
2484
+ 24,
2485
+ 26
2486
+ ],
2487
+ "anchor": "## The recipe",
2488
+ "anchorEnd": "**Runtime file**, `src/system/components/MyWidget.svelte`. D"
2489
+ },
2490
+ {
2491
+ "id": "cc-ed",
2492
+ "row": 4,
2493
+ "kind": "step",
2494
+ "n": "2",
2495
+ "title": "Write the editor file",
2496
+ "desc": "The module declares the component ID, one states map per VariantGroup, and the flat allTokens union. The markup renders those groups in ComponentEditorBase.",
2497
+ "lines": [
2498
+ 27,
2499
+ 27
2500
+ ],
2501
+ "anchor": "**Editor file**, `src/system/components/MyWidgetEditor.svelt"
2502
+ },
2503
+ {
2504
+ "id": "cc-ext",
2505
+ "row": 5,
2506
+ "kind": "chipset",
2507
+ "title": "Choose the applicable extensions",
2508
+ "desc": "The linked-sibling reference covers shared variant values; the intrinsics reference covers structural controls. Every component reads the sketch reference.",
2509
+ "chips": [
2510
+ {
2511
+ "label": "Shared variants: linked siblings",
2512
+ "lines": [
2513
+ 169,
2514
+ 169
2515
+ ],
2516
+ "anchor": "- `references/linked-siblings.md`: variants that share base "
2517
+ },
2518
+ {
2519
+ "label": "Structural controls: intrinsics",
2520
+ "lines": [
2521
+ 170,
2522
+ 170
2523
+ ],
2524
+ "anchor": "- `references/intrinsics.md`: structural or display choices "
2525
+ },
2526
+ {
2527
+ "label": "Sketch mode, required",
2528
+ "lines": [
2529
+ 171,
2530
+ 171
2531
+ ],
2532
+ "anchor": "- `references/sketch-mode.md`: joining the sketch layer. **E"
2533
+ }
2534
+ ],
2535
+ "lines": [
2536
+ 165,
2537
+ 171
2538
+ ],
2539
+ "anchor": "## Extensions",
2540
+ "anchorEnd": "- `references/sketch-mode.md`: joining the sketch layer. **E",
2541
+ "tag": "extension references"
2542
+ },
2543
+ {
2544
+ "id": "cc-reg",
2545
+ "row": 6,
2546
+ "kind": "step",
2547
+ "n": "3",
2548
+ "title": "Register through bootLiveTokens",
2549
+ "desc": "bootLiveTokens receives a unique component ID and registers it after editor initialization. Manually mounted apps call registerComponent immediately before mount.",
2550
+ "lines": [
2551
+ 28,
2552
+ 45
2553
+ ],
2554
+ "anchor": "**Register** by passing the component to `bootLiveTokens` in",
2555
+ "anchorEnd": "`bootLiveTokens` calls `registerComponent` for you after its"
2556
+ },
2557
+ {
2558
+ "id": "cc-desc",
2559
+ "row": 7,
2560
+ "kind": "step",
2561
+ "n": "4",
2562
+ "title": "Describe the component's purpose",
2563
+ "desc": "A leading HTML comment names the job and limits; the catalogue pairs it with ID, variants, and props. componentDirs adds other source directories.",
2564
+ "lines": [
2565
+ 46,
2566
+ 46
2567
+ ],
2568
+ "anchor": "**Say what it is for.** The runtime file's leading HTML comm"
2569
+ },
2570
+ {
2571
+ "id": "cc-sk",
2572
+ "row": 8,
2573
+ "kind": "step",
2574
+ "n": "5",
2575
+ "title": "Join the sketch layer",
2576
+ "desc": "Project components choose a reserved class by size and declare five --sketch-* values. The class requires normal flow, visible overflow, and free pseudo-elements. Package components register PartSpec.",
2577
+ "lines": [
2578
+ 47,
2579
+ 47
2580
+ ],
2581
+ "anchor": "**Join the sketch layer.** The effect draws a fixed set of p"
2582
+ },
2583
+ {
2584
+ "id": "cc-gate",
2585
+ "row": 9,
2586
+ "kind": "cli",
2587
+ "n": "6",
2588
+ "title": "Run the strict component check",
2589
+ "desc": "Strict mode turns warnings into failures; JSON adds stable rule IDs. An ID scopes one component; the default scope covers src/system/components/.",
2590
+ "lines": [
2591
+ 48,
2592
+ 54
2593
+ ],
2594
+ "anchor": "**Gate on the checker.** Run it, fix every error, and run it",
2595
+ "anchorEnd": "If it rejects a suffix, do not invent a new name for the rol",
2596
+ "command": "npx live-tokens check-component <id> --strict --json"
2597
+ },
2598
+ {
2599
+ "id": "cc-disc",
2600
+ "row": 2,
2601
+ "kind": "chipset",
2602
+ "title": "Apply the token rules",
2603
+ "desc": "These rules govern every token in the runtime and editor files.",
2604
+ "chips": [
2605
+ {
2606
+ "label": "Token names",
2607
+ "lines": [
2608
+ 57,
2609
+ 81
2610
+ ],
2611
+ "anchor": "## Token discipline",
2612
+ "anchorEnd": ""
2613
+ },
2614
+ {
2615
+ "label": "Allowed suffixes",
2616
+ "lines": [
2617
+ 71,
2618
+ 92
2619
+ ],
2620
+ "anchor": "### Suffix vocabulary",
2621
+ "anchorEnd": "compete. A suffix outside that list fails `check-component`."
2622
+ },
2623
+ {
2624
+ "label": "Common failures",
2625
+ "lines": [
2626
+ 97,
2627
+ 103
2628
+ ],
2629
+ "anchor": "### Rules that bite",
2630
+ "anchorEnd": "- **Text aliases.** Neutral scale is `--text-primary` / `--t"
2631
+ },
2632
+ {
2633
+ "label": "Typography groupKey",
2634
+ "lines": [
2635
+ 104,
2636
+ 114
2637
+ ],
2638
+ "anchor": "- **Typography `groupKey` on multi-slot components must incl",
2639
+ "anchorEnd": "The helper strips the `--<component>-` prefix and those segm"
2640
+ },
2641
+ {
2642
+ "label": "State model for stateful components",
2643
+ "lines": [
2644
+ 116,
2645
+ 159
2646
+ ],
2647
+ "anchor": "## State model",
2648
+ "anchorEnd": "```"
2649
+ },
2650
+ {
2651
+ "label": "Editor copy",
2652
+ "lines": [
2653
+ 142,
2654
+ 146
2655
+ ],
2656
+ "anchor": "## User-facing copy",
2657
+ "anchorEnd": "Custom chrome inside an editor snippet is rare, since `Compo"
2658
+ },
2659
+ {
2660
+ "label": "Public import paths",
2661
+ "lines": [
2662
+ 148,
2663
+ 163
2664
+ ],
2665
+ "anchor": "## Public imports only",
2666
+ "anchorEnd": "**Never deep-import `node_modules/@motion-proto/live-tokens/"
2667
+ },
2668
+ {
2669
+ "label": "Toggle example",
2670
+ "lines": [
2671
+ 19,
2672
+ 19
2673
+ ],
2674
+ "anchor": "- Every rule below in the fewest lines: `Toggle`. Component "
2675
+ }
2676
+ ],
2677
+ "tag": "global rules"
2678
+ },
2679
+ {
2680
+ "id": "cc-fail",
2681
+ "row": 10,
2682
+ "kind": "gate",
2683
+ "title": "Fix component-check findings",
2684
+ "desc": "Each pass repairs one rule group. A rejected suffix takes the role name from a shipped component that paints the same element.",
2685
+ "lines": [
2686
+ 48,
2687
+ 54
2688
+ ],
2689
+ "anchor": "**Gate on the checker.** Run it, fix every error, and run it",
2690
+ "anchorEnd": "If it rejects a suffix, do not invent a new name for the rol"
2691
+ },
2692
+ {
2693
+ "id": "cc-ver",
2694
+ "row": 10,
2695
+ "kind": "ok",
2696
+ "n": "7",
2697
+ "title": "Static contract passes",
2698
+ "desc": "The checker confirms file layout, token names, state order, imports, registration, editor/runtime parity, and valid theme-backed defaults or declared intrinsics.",
2699
+ "lines": [
2700
+ 173,
2701
+ 175
2702
+ ],
2703
+ "anchor": "## Verification checklist",
2704
+ "anchorEnd": "Step 6 of the recipe is the static gate: `npx live-tokens ch",
2705
+ "chips": [
2706
+ {
2707
+ "label": "Verification checklist",
2708
+ "lines": [
2709
+ 55,
2710
+ 55
2711
+ ],
2712
+ "anchor": "**Verify** with the checklist at the bottom of this file, th"
2713
+ }
2714
+ ]
2715
+ },
2716
+ {
2717
+ "id": "cc-test",
2718
+ "row": 11,
2719
+ "kind": "step",
2720
+ "title": "Run the registry contract test",
2721
+ "desc": "The test checks registration, unique variables, runtime declarations, default.json seeds, opacity floors, and alias round trips. checkRegistryEntry covers custom components; builtInRegistry covers shipped components.",
2722
+ "lines": [
2723
+ 177,
2724
+ 177
2725
+ ],
2726
+ "anchor": "**Then run the registry contract test.** `checkRegistryEntry"
2727
+ },
2728
+ {
2729
+ "id": "cc-intr",
2730
+ "row": 13,
2731
+ "kind": "step",
2732
+ "title": "Run the intrinsics contract test",
2733
+ "desc": "The test verifies that every intrinsic and variant has matching, allowed runtime and editor defaults.",
2734
+ "lines": [
2735
+ 179,
2736
+ 179
2737
+ ],
2738
+ "anchor": "**If your component declares `intrinsics`, the intrinsics co"
2739
+ },
2740
+ {
2741
+ "id": "cc-intr-q",
2742
+ "row": 12,
2743
+ "kind": "decide",
2744
+ "title": "Does the component declare intrinsics?",
2745
+ "desc": "Components with structural or display choices run the intrinsics contract test.",
2746
+ "lines": [
2747
+ 179,
2748
+ 179
2749
+ ],
2750
+ "anchor": "**If your component declares `intrinsics`, the intrinsics co"
2751
+ },
2752
+ {
2753
+ "id": "cc-man",
2754
+ "row": 14,
2755
+ "kind": "step",
2756
+ "title": "Verify the component at runtime",
2757
+ "desc": "The runtime review at /live-tokens/components checks the CUSTOM entry, token controls, default derivation, persistence, Reset, clean boot, and sketch mode. Linked components also verify linked-block sync.",
2758
+ "lines": [
2759
+ 181,
2760
+ 189
2761
+ ],
2762
+ "anchor": "Finally navigate to `/live-tokens/components` and confirm th",
2763
+ "anchorEnd": "- [ ] Switch Sketch mode on in the editor and walk the check"
2764
+ },
2765
+ {
2766
+ "id": "cc-place",
2767
+ "row": 15,
2768
+ "kind": "hand",
2769
+ "title": "Continue with build-page",
2770
+ "desc": "build-page places the completed component on a page.",
2771
+ "lines": [
2772
+ 55,
2773
+ 55
2774
+ ],
2775
+ "anchor": "**Verify** with the checklist at the bottom of this file, th"
2776
+ }
2777
+ ],
2778
+ "edges": [
2779
+ {
2780
+ "from": "cc-trig",
2781
+ "to": "cc-read"
2782
+ },
2783
+ {
2784
+ "from": "cc-read",
2785
+ "to": "cc-disc"
2786
+ },
2787
+ {
2788
+ "from": "cc-disc",
2789
+ "to": "cc-run"
2790
+ },
2791
+ {
2792
+ "from": "cc-run",
2793
+ "to": "cc-ed"
2794
+ },
2795
+ {
2796
+ "from": "cc-ed",
2797
+ "to": "cc-ext"
2798
+ },
2799
+ {
2800
+ "from": "cc-ext",
2801
+ "to": "cc-reg"
2802
+ },
2803
+ {
2804
+ "from": "cc-reg",
2805
+ "to": "cc-desc"
2806
+ },
2807
+ {
2808
+ "from": "cc-desc",
2809
+ "to": "cc-sk"
2810
+ },
2811
+ {
2812
+ "from": "cc-sk",
2813
+ "to": "cc-gate"
2814
+ },
2815
+ {
2816
+ "from": "cc-ver",
2817
+ "to": "cc-test"
2818
+ },
2819
+ {
2820
+ "from": "cc-test",
2821
+ "to": "cc-intr-q"
2822
+ },
2823
+ {
2824
+ "from": "cc-intr-q",
2825
+ "to": "cc-intr",
2826
+ "label": "has intrinsics"
2827
+ },
2828
+ {
2829
+ "from": "cc-intr-q",
2830
+ "to": "cc-man",
2831
+ "label": "token values only"
2832
+ },
2833
+ {
2834
+ "from": "cc-intr",
2835
+ "to": "cc-man"
2836
+ },
2837
+ {
2838
+ "from": "cc-man",
2839
+ "to": "cc-place"
2840
+ },
2841
+ {
2842
+ "from": "cc-gate",
2843
+ "to": "cc-fail",
2844
+ "label": "exit 1"
2845
+ },
2846
+ {
2847
+ "from": "cc-gate",
2848
+ "to": "cc-ver",
2849
+ "label": "exit 0"
2850
+ },
2851
+ {
2852
+ "from": "cc-fail",
2853
+ "to": "cc-gate",
2854
+ "label": "rerun",
2855
+ "back": true
2856
+ }
2857
+ ]
2858
+ },
2859
+ "check-compliance": {
2860
+ "id": "live-tokens-check-compliance",
2861
+ "digest": "sha256:312e48bb6fc90fa4",
2862
+ "title": "check-compliance",
2863
+ "tagline": "A read-only report explains project compliance and repair cost, then hands findings to fix-findings.",
2864
+ "nodes": [
2865
+ {
2866
+ "id": "cc2-trig",
2867
+ "row": 0,
2868
+ "kind": "trigger",
2869
+ "title": "Review design-system compliance",
2870
+ "desc": "The report covers migrations, components, usage, checker findings, and repair cost. fix-findings applies repairs; the editor handles single tokens.",
2871
+ "lines": [
2872
+ 2,
2873
+ 3
2874
+ ],
2875
+ "anchor": "name: live-tokens-check-compliance",
2876
+ "anchorEnd": "description: Check an existing @motion-proto/live-tokens pro"
2877
+ },
2878
+ {
2879
+ "id": "cc2-why",
2880
+ "row": 1,
2881
+ "kind": "step",
2882
+ "title": "Keep the audit read-only",
2883
+ "desc": "The command supplies every fact. The review explains each finding and estimates its repair cost.",
2884
+ "lines": [
2885
+ 8,
2886
+ 8
2887
+ ],
2888
+ "anchor": "The answer to \"check this project\" is a report. Every fact i"
2889
+ },
2890
+ {
2891
+ "id": "cc2-run",
2892
+ "row": 2,
2893
+ "kind": "cli",
2894
+ "n": "1",
2895
+ "title": "Generate the compliance report",
2896
+ "desc": "The report reads files and exits 0 even when it finds problems.",
2897
+ "lines": [
2898
+ 12,
2899
+ 12
2900
+ ],
2901
+ "anchor": "Run `npx live-tokens report --json`. It always exits 0: it i",
2902
+ "command": "npx live-tokens report --json"
2903
+ },
2904
+ {
2905
+ "id": "cc2-upgrade",
2906
+ "row": 3,
2907
+ "kind": "gate",
2908
+ "title": "Upgrade to get the report command",
2909
+ "desc": "Upgrading @motion-proto/live-tokens restores the command. The next run generates the report.",
2910
+ "lines": [
2911
+ 12,
2912
+ 12
2913
+ ],
2914
+ "anchor": "Run `npx live-tokens report --json`. It always exits 0: it i"
2915
+ },
2916
+ {
2917
+ "id": "cc2-sections",
2918
+ "row": 3,
2919
+ "kind": "chipset",
2920
+ "title": "Read each report section",
2921
+ "desc": "Each section names a project fact and the repair cost for each actionable finding.",
2922
+ "lines": [
2923
+ 19,
2924
+ 30
2925
+ ],
2926
+ "anchor": "## The report's sections",
2927
+ "anchorEnd": "| `findings.pages`, `findings.components` | Both checkers' f",
2928
+ "chips": [
2929
+ {
2930
+ "label": "migrations",
2931
+ "lines": [
2932
+ 23,
2933
+ 23
2934
+ ],
2935
+ "anchor": "| `migrations` | Whether `tokens.css` is behind the installe"
2936
+ },
2937
+ {
2938
+ "label": "components[].unread",
2939
+ "lines": [
2940
+ 24,
2941
+ 24
2942
+ ],
2943
+ "anchor": "| `components[].unread` | Tokens a component declares that n"
2944
+ },
2945
+ {
2946
+ "label": "components[].registered",
2947
+ "lines": [
2948
+ 25,
2949
+ 25
2950
+ ],
2951
+ "anchor": "| `components[].registered` | A component file with no `boot"
2952
+ },
2953
+ {
2954
+ "label": "components[].described",
2955
+ "lines": [
2956
+ 26,
2957
+ 26
2958
+ ],
2959
+ "anchor": "| `components[].described` | Whether the runtime file has th"
2960
+ },
2961
+ {
2962
+ "label": "usage.byPage",
2963
+ "lines": [
2964
+ 27,
2965
+ 27
2966
+ ],
2967
+ "anchor": "| `usage.byPage` | Which catalogue component each page rende"
2968
+ },
2969
+ {
2970
+ "label": "usage.unusedShipped",
2971
+ "lines": [
2972
+ 28,
2973
+ 28
2974
+ ],
2975
+ "anchor": "| `usage.unusedShipped` | Shipped components no page renders"
2976
+ },
2977
+ {
2978
+ "label": "usage.customUnregistered / usage.customUnused",
2979
+ "lines": [
2980
+ 29,
2981
+ 29
2982
+ ],
2983
+ "anchor": "| `usage.customUnregistered`, `usage.customUnused` | The pro"
2984
+ },
2985
+ {
2986
+ "label": "findings.pages / findings.components",
2987
+ "lines": [
2988
+ 30,
2989
+ 30
2990
+ ],
2991
+ "anchor": "| `findings.pages`, `findings.components` | Both checkers' f"
2992
+ }
2993
+ ]
2994
+ },
2995
+ {
2996
+ "id": "cc2-drill-q",
2997
+ "row": 4,
2998
+ "kind": "decide",
2999
+ "title": "Does a finding need more evidence?",
3000
+ "desc": "A component or token-scale query supplies additional evidence for classification.",
3001
+ "lines": [
3002
+ 17,
3003
+ 17
3004
+ ],
3005
+ "anchor": "`npx live-tokens components <id>` and `npx live-tokens token"
3006
+ },
3007
+ {
3008
+ "id": "cc2-drill",
3009
+ "row": 5,
3010
+ "kind": "cli",
3011
+ "title": "Inspect the component or scale",
3012
+ "desc": "components <id> lists props, values, and tokens. tokens --family <name> lists one scale. Both commands accept --json.",
3013
+ "lines": [
3014
+ 17,
3015
+ 17
3016
+ ],
3017
+ "anchor": "`npx live-tokens components <id>` and `npx live-tokens token",
3018
+ "command": "npx live-tokens components <id> --json\nnpx live-tokens tokens --family <name> --json"
3019
+ },
3020
+ {
3021
+ "id": "cc2-read",
3022
+ "row": 6,
3023
+ "kind": "step",
3024
+ "n": "2",
3025
+ "title": "Classify each fix",
3026
+ "desc": "Mechanical fixes follow a fixed mapping; judgement calls require a semantic choice. The classification names the choice and any visible shift.",
3027
+ "lines": [
3028
+ 32,
3029
+ 35
3030
+ ],
3031
+ "anchor": "## Mechanical or judgement",
3032
+ "anchorEnd": "- **Judgement**: a colour literal mapped by the role it play"
3033
+ },
3034
+ {
3035
+ "id": "cc2-deliberate-q",
3036
+ "row": 7,
3037
+ "kind": "decide",
3038
+ "title": "Deliberate finding?",
3039
+ "desc": "Overlays, project-owned layout sizes, artwork, and vendored CSS require an intent check.",
3040
+ "lines": [
3041
+ 37,
3042
+ 39
3043
+ ],
3044
+ "anchor": "## Deliberate findings",
3045
+ "anchorEnd": "A translucent overlay on an app shell, or a layout size the "
3046
+ },
3047
+ {
3048
+ "id": "cc2-deliberate",
3049
+ "row": 8,
3050
+ "kind": "step",
3051
+ "n": "3",
3052
+ "title": "Name the narrowest config entry",
3053
+ "desc": "The review names a rule-severity entry or file exclusion and identifies the narrower choice. The user controls the config change.",
3054
+ "lines": [
3055
+ 37,
3056
+ 39
3057
+ ],
3058
+ "anchor": "## Deliberate findings",
3059
+ "anchorEnd": "A translucent overlay on an app shell, or a layout size the "
3060
+ },
3061
+ {
3062
+ "id": "cc2-report",
3063
+ "row": 9,
3064
+ "kind": "chipset",
3065
+ "n": "4",
3066
+ "title": "Report six sections with counts",
3067
+ "desc": "The summary lists files for current errors, orders fixes by the fix-findings sequence, and marks each as mechanical or judgement.",
3068
+ "lines": [
3069
+ 41,
3070
+ 48
3071
+ ],
3072
+ "anchor": "## Summary",
3073
+ "anchorEnd": "Recommended fixes, in the order **live-tokens-fix-findings**",
3074
+ "chips": [
3075
+ {
3076
+ "label": "Pending migrations",
3077
+ "lines": [
3078
+ 43,
3079
+ 43
3080
+ ],
3081
+ "anchor": "Migrations pending, and the one command that clears them."
3082
+ },
3083
+ {
3084
+ "label": "Current build errors",
3085
+ "lines": [
3086
+ 44,
3087
+ 44
3088
+ ],
3089
+ "anchor": "What fails the build now: errors by rule, with the files."
3090
+ },
3091
+ {
3092
+ "label": "--strict warnings",
3093
+ "lines": [
3094
+ 45,
3095
+ 45
3096
+ ],
3097
+ "anchor": "What `--strict` would add: warnings by rule."
3098
+ },
3099
+ {
3100
+ "label": "Components",
3101
+ "lines": [
3102
+ 46,
3103
+ 46
3104
+ ],
3105
+ "anchor": "Components: unread tokens, unregistered, undescribed."
3106
+ },
3107
+ {
3108
+ "label": "Usage",
3109
+ "lines": [
3110
+ 47,
3111
+ 47
3112
+ ],
3113
+ "anchor": "Usage: what each page renders, and what is used nowhere."
3114
+ },
3115
+ {
3116
+ "label": "Recommended fixes",
3117
+ "lines": [
3118
+ 48,
3119
+ 48
3120
+ ],
3121
+ "anchor": "Recommended fixes, in the order **live-tokens-fix-findings**"
3122
+ }
3123
+ ]
3124
+ },
3125
+ {
3126
+ "id": "cc2-done",
3127
+ "row": 10,
3128
+ "kind": "hand",
3129
+ "title": "Hand off the fixes",
3130
+ "desc": "The hand-off offers live-tokens-fix-findings for the full set or a selected subset. The audit preserves project files.",
3131
+ "lines": [
3132
+ 50,
3133
+ 50
3134
+ ],
3135
+ "anchor": "End with the hand-off: \"Run live-tokens-fix-findings to appl"
3136
+ }
3137
+ ],
3138
+ "edges": [
3139
+ {
3140
+ "from": "cc2-trig",
3141
+ "to": "cc2-why"
3142
+ },
3143
+ {
3144
+ "from": "cc2-why",
3145
+ "to": "cc2-run"
3146
+ },
3147
+ {
3148
+ "from": "cc2-run",
3149
+ "to": "cc2-upgrade",
3150
+ "label": "unknown command"
3151
+ },
3152
+ {
3153
+ "from": "cc2-upgrade",
3154
+ "to": "cc2-run",
3155
+ "label": "retry",
3156
+ "back": true
3157
+ },
3158
+ {
3159
+ "from": "cc2-run",
3160
+ "to": "cc2-sections",
3161
+ "label": "report ready"
3162
+ },
3163
+ {
3164
+ "from": "cc2-read",
3165
+ "to": "cc2-deliberate-q"
3166
+ },
3167
+ {
3168
+ "from": "cc2-deliberate-q",
3169
+ "to": "cc2-deliberate",
3170
+ "label": "possible exception"
3171
+ },
3172
+ {
3173
+ "from": "cc2-deliberate-q",
3174
+ "to": "cc2-report",
3175
+ "label": "ordinary finding"
3176
+ },
3177
+ {
3178
+ "from": "cc2-deliberate",
3179
+ "to": "cc2-report"
3180
+ },
3181
+ {
3182
+ "from": "cc2-report",
3183
+ "to": "cc2-done"
3184
+ },
3185
+ {
3186
+ "from": "cc2-sections",
3187
+ "to": "cc2-drill-q"
3188
+ },
3189
+ {
3190
+ "from": "cc2-drill-q",
3191
+ "to": "cc2-drill",
3192
+ "label": "needs evidence"
3193
+ },
3194
+ {
3195
+ "from": "cc2-drill-q",
3196
+ "to": "cc2-read",
3197
+ "label": "enough evidence"
3198
+ },
3199
+ {
3200
+ "from": "cc2-drill",
3201
+ "to": "cc2-read"
3202
+ }
3203
+ ]
3204
+ },
3205
+ "fix-findings": {
3206
+ "id": "live-tokens-fix-findings",
3207
+ "digest": "sha256:f31b82cb3aefa302",
3208
+ "title": "fix-findings",
3209
+ "tagline": "The repair loop clears the largest error group first, repeats by rule, and ends with strict validation.",
3210
+ "nodes": [
3211
+ {
3212
+ "id": "ff-trig",
3213
+ "row": 0,
3214
+ "kind": "trigger",
3215
+ "title": "Fix design-system findings",
3216
+ "desc": "The loop groups existing findings by rule, applies each recipe, and reruns both checkers. check-compliance supplies the read-only report.",
3217
+ "lines": [
3218
+ 2,
3219
+ 3
3220
+ ],
3221
+ "anchor": "name: live-tokens-fix-findings",
3222
+ "anchorEnd": "description: Bring an existing @motion-proto/live-tokens pro"
3223
+ },
3224
+ {
3225
+ "id": "ff-why",
3226
+ "row": 1,
3227
+ "kind": "step",
3228
+ "title": "Enforce page and component contracts",
3229
+ "desc": "The page checker enforces catalogue components, declared props, and tokenized CSS. The component checker enforces semantic token names and theme-backed defaults. Passing files repaint.",
3230
+ "lines": [
3231
+ 8,
3232
+ 10
3233
+ ],
3234
+ "anchor": "Two checkers hold a project to its design system. `check-pag",
3235
+ "anchorEnd": "This skill is the loop for code that already exists. When th"
3236
+ },
3237
+ {
3238
+ "id": "ff-run",
3239
+ "row": 2,
3240
+ "kind": "cli",
3241
+ "n": "1",
3242
+ "title": "Collect all findings",
3243
+ "desc": "Each finding includes a stable rule, file, and line. A page path scopes check-page; a component ID scopes check-component.",
3244
+ "lines": [
3245
+ 14,
3246
+ 19
3247
+ ],
3248
+ "anchor": "Run both checkers with `--json`. Each finding carries a stab",
3249
+ "anchorEnd": "`check-page src/pages/Home.svelte` and `check-component <id>",
3250
+ "command": "npx live-tokens check-page --json\nnpx live-tokens check-component --json"
3251
+ },
3252
+ {
3253
+ "id": "ff-blocked",
3254
+ "row": 3,
3255
+ "kind": "gate",
3256
+ "title": "Upgrade to get the checkers",
3257
+ "desc": "A package upgrade supplies the checkers. migrate --check plans token changes; migrate --write applies them. --tokens identifies a custom tokens.css path.",
3258
+ "lines": [
3259
+ 19,
3260
+ 19
3261
+ ],
3262
+ "anchor": "`check-page src/pages/Home.svelte` and `check-component <id>"
3263
+ },
3264
+ {
3265
+ "id": "ff-empty",
3266
+ "row": 3,
3267
+ "kind": "ok",
3268
+ "title": "Default checks pass",
3269
+ "desc": "Strict validation now reveals advisory warnings.",
3270
+ "lines": [
3271
+ 23,
3272
+ 23
3273
+ ],
3274
+ "anchor": "Run once with `--strict` and report what it adds, so the use"
3275
+ },
3276
+ {
3277
+ "id": "ff-loop",
3278
+ "row": 4,
3279
+ "kind": "step",
3280
+ "n": "2",
3281
+ "title": "Group findings by rule",
3282
+ "desc": "Errors precede warnings. The largest rule group gives one recipe the widest effect.",
3283
+ "lines": [
3284
+ 20,
3285
+ 20
3286
+ ],
3287
+ "anchor": "Group by rule. Take errors before warnings, and the rule wit"
3288
+ },
3289
+ {
3290
+ "id": "ff-never",
3291
+ "row": 5,
3292
+ "kind": "chipset",
3293
+ "title": "Protect three constraints",
3294
+ "desc": "--off applies to one working run. Severity changes require user approval. Repairs use existing tokens and report every visible shift.",
3295
+ "chips": [
3296
+ {
3297
+ "label": "Keep project rule severity",
3298
+ "lines": [
3299
+ 29,
3300
+ 29
3301
+ ],
3302
+ "anchor": "- **Silence a rule to pass.** `--off=<rule>` is for a single"
3303
+ },
3304
+ {
3305
+ "label": "Map to existing tokens",
3306
+ "lines": [
3307
+ 30,
3308
+ 30
3309
+ ],
3310
+ "anchor": "- **Mint a token.** A literal with no token behind it is rem"
3311
+ },
3312
+ {
3313
+ "label": "Report visible changes",
3314
+ "lines": [
3315
+ 31,
3316
+ 31
3317
+ ],
3318
+ "anchor": "- **Change what the page looks like without saying so.** Mos"
3319
+ }
3320
+ ]
3321
+ },
3322
+ {
3323
+ "id": "ff-recipe",
3324
+ "row": 6,
3325
+ "kind": "decide",
3326
+ "n": "3",
3327
+ "title": "Finding type?",
3328
+ "desc": "One recipe covers the group: page colours by role, geometry by scale, or the rule table. Unknown components enter a separate hand-off.",
3329
+ "lines": [
3330
+ 21,
3331
+ 21
3332
+ ],
3333
+ "anchor": "Apply that rule's recipe to every finding in the group: colo"
3334
+ },
3335
+ {
3336
+ "id": "ff-colour",
3337
+ "row": 7,
3338
+ "kind": "step",
3339
+ "title": "Map page colours by role",
3340
+ "desc": "Each page literal maps by job: text, fill, stroke, scrim, tint, transparency, or gradient. Role tokens then move coherently with the theme.",
3341
+ "lines": [
3342
+ 33,
3343
+ 48
3344
+ ],
3345
+ "anchor": "## Colour by role, never by hue",
3346
+ "anchorEnd": "A `var(--x, #fff)` fallback is not a finding. A named colour"
3347
+ },
3348
+ {
3349
+ "id": "ff-geom",
3350
+ "row": 7,
3351
+ "kind": "step",
3352
+ "title": "Map geometry to its scale",
3353
+ "desc": "Theme-owned geometry maps to token scales: nearest-step spacing, matching stroke and corner tokens, whole-shadow replacements, and tokenized geometry inside calc(). Layout sizing remains intact.",
3354
+ "lines": [
3355
+ 50,
3356
+ 62
3357
+ ],
3358
+ "anchor": "## Geometry by scale",
3359
+ "anchorEnd": "While in the file, motion values take `--duration-*` and `--"
3360
+ },
3361
+ {
3362
+ "id": "ff-rest",
3363
+ "row": 7,
3364
+ "kind": "chipset",
3365
+ "title": "Apply the rule's recipe",
3366
+ "desc": "The table supplies one repair for every finding in the current rule group.",
3367
+ "lines": [
3368
+ 64,
3369
+ 81
3370
+ ],
3371
+ "anchor": "## Every other rule",
3372
+ "anchorEnd": "| `invalid-id`, `missing-file`, `missing-root-block`, `no-to",
3373
+ "chips": [
3374
+ {
3375
+ "label": "unknown-token",
3376
+ "lines": [
3377
+ 68,
3378
+ 68
3379
+ ],
3380
+ "anchor": "| `unknown-token` | A typo or a rename. Search `tokens.css` "
3381
+ },
3382
+ {
3383
+ "label": "raw-text-axis",
3384
+ "lines": [
3385
+ 69,
3386
+ 69
3387
+ ],
3388
+ "anchor": "| `raw-text-axis` | Set the whole axis set from one text sty"
3389
+ },
3390
+ {
3391
+ "label": "unknown-prop, unknown-prop-value",
3392
+ "lines": [
3393
+ 71,
3394
+ 72
3395
+ ],
3396
+ "anchor": "| `unknown-prop` | The component drops it at runtime. `npx l",
3397
+ "anchorEnd": "| `unknown-prop-value` | Pick a value from the union the mes"
3398
+ },
3399
+ {
3400
+ "label": "hardcoded-columns",
3401
+ "lines": [
3402
+ 73,
3403
+ 73
3404
+ ],
3405
+ "anchor": "| `hardcoded-columns` | `repeat(var(--columns-count), 1fr)` "
3406
+ },
3407
+ {
3408
+ "label": "site-css-in-main, missing-source, reserved-route",
3409
+ "lines": [
3410
+ 74,
3411
+ 76
3412
+ ],
3413
+ "anchor": "| `site-css-in-main` | Delete the import from `main.ts` and ",
3414
+ "anchorEnd": "| `reserved-route` | Move the route out of `/live-tokens/*`;"
3415
+ },
3416
+ {
3417
+ "label": "deep-import",
3418
+ "lines": [
3419
+ 77,
3420
+ 77
3421
+ ],
3422
+ "anchor": "| `deep-import` | Import from `@motion-proto/live-tokens` or"
3423
+ },
3424
+ {
3425
+ "label": "Token naming",
3426
+ "lines": [
3427
+ 78,
3428
+ 78
3429
+ ],
3430
+ "anchor": "| `unknown-suffix`, `state-after-property`, `disabled-is-ter"
3431
+ },
3432
+ {
3433
+ "label": "Component defaults",
3434
+ "lines": [
3435
+ 79,
3436
+ 79
3437
+ ],
3438
+ "anchor": "| `color-literal`, `unknown-token-ref`, `default-not-token` "
3439
+ },
3440
+ {
3441
+ "label": "Editor/runtime mismatches",
3442
+ "lines": [
3443
+ 80,
3444
+ 80
3445
+ ],
3446
+ "anchor": "| `phantom-editor-token`, `phantom-link` | The editor names "
3447
+ },
3448
+ {
3449
+ "label": "Component wiring",
3450
+ "lines": [
3451
+ 81,
3452
+ 81
3453
+ ],
3454
+ "anchor": "| `invalid-id`, `missing-file`, `missing-root-block`, `no-to"
3455
+ }
3456
+ ]
3457
+ },
3458
+ {
3459
+ "id": "ff-hand",
3460
+ "row": 7,
3461
+ "kind": "hand",
3462
+ "title": "Resolve the unknown component",
3463
+ "desc": "pick-component finds a catalogue match. create-component builds a new editable component.",
3464
+ "lines": [
3465
+ 70,
3466
+ 70
3467
+ ],
3468
+ "anchor": "| `unknown-component` | Not in the catalogue. Read **live-to"
3469
+ },
3470
+ {
3471
+ "id": "ff-gate",
3472
+ "row": 8,
3473
+ "kind": "decide",
3474
+ "n": "4",
3475
+ "title": "Rerun result?",
3476
+ "desc": "Repairable findings return to the grouped loop. An unmatched literal enters the report with its rationale.",
3477
+ "lines": [
3478
+ 22,
3479
+ 22
3480
+ ],
3481
+ "anchor": "Run again. New findings can appear as old ones clear: a toke"
3482
+ },
3483
+ {
3484
+ "id": "ff-strict",
3485
+ "row": 9,
3486
+ "kind": "cli",
3487
+ "n": "5",
3488
+ "title": "Run strict validation",
3489
+ "desc": "--strict exposes advisory warnings after the default checks pass.",
3490
+ "lines": [
3491
+ 23,
3492
+ 23
3493
+ ],
3494
+ "anchor": "Run once with `--strict` and report what it adds, so the use",
3495
+ "command": "npx live-tokens check-page --strict --json\nnpx live-tokens check-component --strict --json"
3496
+ },
3497
+ {
3498
+ "id": "ff-warn",
3499
+ "row": 10,
3500
+ "kind": "decide",
3501
+ "title": "Clear strict warnings now?",
3502
+ "desc": "The user selects the warnings to repair. Accepted warnings return to the rule loop and both validation stages.",
3503
+ "lines": [
3504
+ 23,
3505
+ 23
3506
+ ],
3507
+ "anchor": "Run once with `--strict` and report what it adds, so the use"
3508
+ },
3509
+ {
3510
+ "id": "ff-clean",
3511
+ "row": 10,
3512
+ "kind": "ok",
3513
+ "title": "Strict checks pass",
3514
+ "desc": "Both strict checks exit 0.",
3515
+ "lines": [
3516
+ 23,
3517
+ 23
3518
+ ],
3519
+ "anchor": "Run once with `--strict` and report what it adds, so the use"
3520
+ },
3521
+ {
3522
+ "id": "ff-report",
3523
+ "row": 11,
3524
+ "kind": "step",
3525
+ "title": "Report changes by rule",
3526
+ "desc": "The final report gives each rule's count and visible shifts, explains unresolved findings, records approved config entries, and includes both commands with exit codes.",
3527
+ "lines": [
3528
+ 83,
3529
+ 85
3530
+ ],
3531
+ "anchor": "## Report",
3532
+ "anchorEnd": "Say what changed by rule, one line per rule with the count a"
3533
+ },
3534
+ {
3535
+ "id": "ff-script",
3536
+ "row": 12,
3537
+ "kind": "step",
3538
+ "title": "Gate builds with check:design",
3539
+ "desc": "Scaffolded projects include check:design. Other projects add both checkers to that script and run it before vite build.",
3540
+ "lines": [
3541
+ 25,
3542
+ 25
3543
+ ],
3544
+ "anchor": "A project scaffolded by `create` has a `check:design` script"
3545
+ },
3546
+ {
3547
+ "id": "ff-ver",
3548
+ "row": 13,
3549
+ "kind": "done",
3550
+ "title": "Verify that every touched file repaints",
3551
+ "desc": "A surface-colour and spacing change in /live-tokens/editor tests every touched file. Any repaint failure becomes a reported checker gap.",
3552
+ "lines": [
3553
+ 87,
3554
+ 89
3555
+ ],
3556
+ "anchor": "## Verify",
3557
+ "anchorEnd": "Open `/live-tokens/editor` in dev and change a surface colou"
3558
+ }
3559
+ ],
3560
+ "edges": [
3561
+ {
3562
+ "from": "ff-trig",
3563
+ "to": "ff-why"
3564
+ },
3565
+ {
3566
+ "from": "ff-why",
3567
+ "to": "ff-run"
3568
+ },
3569
+ {
3570
+ "from": "ff-run",
3571
+ "to": "ff-blocked",
3572
+ "label": "unknown command"
3573
+ },
3574
+ {
3575
+ "from": "ff-blocked",
3576
+ "to": "ff-run",
3577
+ "label": "retry",
3578
+ "back": true
3579
+ },
3580
+ {
3581
+ "from": "ff-run",
3582
+ "to": "ff-loop",
3583
+ "label": "findings"
3584
+ },
3585
+ {
3586
+ "from": "ff-run",
3587
+ "to": "ff-empty",
3588
+ "label": "clean"
3589
+ },
3590
+ {
3591
+ "from": "ff-empty",
3592
+ "to": "ff-strict"
3593
+ },
3594
+ {
3595
+ "from": "ff-loop",
3596
+ "to": "ff-never"
3597
+ },
3598
+ {
3599
+ "from": "ff-never",
3600
+ "to": "ff-recipe"
3601
+ },
3602
+ {
3603
+ "from": "ff-recipe",
3604
+ "to": "ff-colour",
3605
+ "label": "page colour"
3606
+ },
3607
+ {
3608
+ "from": "ff-recipe",
3609
+ "to": "ff-geom",
3610
+ "label": "geometry"
3611
+ },
3612
+ {
3613
+ "from": "ff-recipe",
3614
+ "to": "ff-rest",
3615
+ "label": "other rule"
3616
+ },
3617
+ {
3618
+ "from": "ff-recipe",
3619
+ "to": "ff-hand",
3620
+ "label": "unknown component"
3621
+ },
3622
+ {
3623
+ "from": "ff-colour",
3624
+ "to": "ff-gate"
3625
+ },
3626
+ {
3627
+ "from": "ff-geom",
3628
+ "to": "ff-gate"
3629
+ },
3630
+ {
3631
+ "from": "ff-rest",
3632
+ "to": "ff-gate"
3633
+ },
3634
+ {
3635
+ "from": "ff-hand",
3636
+ "to": "ff-gate"
3637
+ },
3638
+ {
3639
+ "from": "ff-gate",
3640
+ "to": "ff-loop",
3641
+ "label": "repairable findings",
3642
+ "back": true
3643
+ },
3644
+ {
3645
+ "from": "ff-gate",
3646
+ "to": "ff-report",
3647
+ "label": "unmatched literal"
3648
+ },
3649
+ {
3650
+ "from": "ff-gate",
3651
+ "to": "ff-strict",
3652
+ "label": "clean"
3653
+ },
3654
+ {
3655
+ "from": "ff-strict",
3656
+ "to": "ff-warn",
3657
+ "label": "warnings"
3658
+ },
3659
+ {
3660
+ "from": "ff-strict",
3661
+ "to": "ff-clean",
3662
+ "label": "clean"
3663
+ },
3664
+ {
3665
+ "from": "ff-warn",
3666
+ "to": "ff-loop",
3667
+ "label": "clear now",
3668
+ "back": true
3669
+ },
3670
+ {
3671
+ "from": "ff-warn",
3672
+ "to": "ff-report",
3673
+ "label": "defer repairs"
3674
+ },
3675
+ {
3676
+ "from": "ff-clean",
3677
+ "to": "ff-report"
3678
+ },
3679
+ {
3680
+ "from": "ff-report",
3681
+ "to": "ff-script"
3682
+ },
3683
+ {
3684
+ "from": "ff-script",
3685
+ "to": "ff-ver"
3686
+ }
3687
+ ]
3688
+ }
3689
+ };
3690
+
3691
+ export const skillKeys = Object.keys(skillTrees);