@motion-proto/live-tokens 0.55.0 → 0.56.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.claude/skills/{live-tokens-adjust-shape-space → live-tokens-adjust-geometry}/SKILL.md +5 -5
  2. package/.claude/skills/live-tokens-build-page/SKILL.md +2 -0
  3. package/.claude/skills/live-tokens-create-component/SKILL.md +20 -357
  4. package/.claude/skills/live-tokens-create-component/references/fixed-overlays.md +3 -0
  5. package/.claude/skills/live-tokens-create-component/references/intrinsics.md +58 -0
  6. package/.claude/skills/live-tokens-create-component/references/linked-siblings.md +70 -0
  7. package/.claude/skills/live-tokens-generate-theme/SKILL.md +79 -98
  8. package/.claude/skills/live-tokens-generate-theme/references/named-themes.md +18 -0
  9. package/.claude/skills/live-tokens-pair-fonts/SKILL.md +88 -0
  10. package/.claude/skills/live-tokens-pick-component/SKILL.md +4 -1
  11. package/CHANGELOG.md +108 -0
  12. package/README.md +20 -7
  13. package/bin/cli.mjs +41 -3
  14. package/bin/set-fonts.mjs +280 -0
  15. package/dist-plugin/adjust/index.d.cts +2 -2
  16. package/dist-plugin/adjust/index.d.ts +2 -2
  17. package/dist-plugin/fontPairing/index.cjs +411 -0
  18. package/dist-plugin/fontPairing/index.d.cts +109 -0
  19. package/dist-plugin/fontPairing/index.d.ts +109 -0
  20. package/dist-plugin/fontPairing/index.js +320 -0
  21. package/dist-plugin/generateColorsAndType/index.d.cts +2 -2
  22. package/dist-plugin/generateColorsAndType/index.d.ts +2 -2
  23. package/dist-plugin/index-4N-Orzzi.d.cts +3 -0
  24. package/dist-plugin/index-4N-Orzzi.d.ts +3 -0
  25. package/dist-plugin/{index-DpTIRZ2H.d.cts → themeTypes-BAqtv4XO.d.cts} +1 -3
  26. package/dist-plugin/{index-DpTIRZ2H.d.ts → themeTypes-BAqtv4XO.d.ts} +1 -3
  27. package/package.json +3 -2
  28. package/src/editor/core/fonts/applyFontPairing.ts +157 -0
  29. package/src/editor/core/fonts/fontPairing.ts +13 -6
  30. package/src/editor/core/fonts/googleFontsUrl.ts +123 -0
  31. package/src/editor/core/fonts/weightCoverage.ts +92 -0
  32. package/src/editor/docs/content/themes-workflow.md +39 -0
  33. package/src/editor/docs/content.generated.ts +1 -1
  34. package/src/editor/ui/ColorEditPanel.svelte +16 -1
  35. package/src/editor/ui/PaletteEditor.svelte +124 -31
  36. package/src/editor/ui/ProjectFontsSection.svelte +24 -36
  37. package/src/editor/ui/palette/OverridesPanel.svelte +5 -2
  38. package/src/editor/ui/palette/PaletteBase.svelte +13 -0
@@ -1,22 +1,24 @@
1
1
  ---
2
2
  name: live-tokens-generate-theme
3
- description: Generate a complete live-tokens color theme from a natural-language mood brief by choosing 10 OKLCH seeds and running the packaged generator, which enforces AA contrast automatically. Use when the user asks for a color theme, color scheme, or palette by mood, vibe, season, holiday, or hue — make me a bright and cheerful theme, give me a dark and moody night theme, a St. Patrick's Day theme with green and gold, a Christmas theme, a red-based theme, make it warmer, more contrast, a calmer palette. Changes color assignments only, never fonts. Not for editing a single token (use the editor) or building pages (see live-tokens-build-page).
3
+ description: Generate a complete live-tokens theme (color, type, and geometry) from a natural-language mood brief. Chooses 10 OKLCH seeds and runs the packaged generator, which enforces AA contrast, then carries the same brief into a font pairing and a geometry through the sibling skills. Use whenever the user asks for a theme, a look, a vibe, a brand feel, a color scheme, or a palette, by mood, season, holiday, or hue, even if they only mention color: make me a bright and cheerful theme, a dark moody night theme, a St. Patrick's Day theme in green and gold, a Christmas look, something red-based, warmer, more contrast, calmer. Not for a single token (use the editor), for type alone (live-tokens-pair-fonts), or for geometry alone (live-tokens-adjust-geometry).
4
4
  ---
5
5
 
6
6
  # Generating a theme from a mood brief
7
7
 
8
- You translate the brief into 10 OKLCH seed colors plus a scheme; the CLI does everything else (curve assembly, AA contrast enforcement with auto-correction, writing the theme, opening it). Never hand-author theme JSON and never edit the data tree directly. Seeds in, valid theme out.
8
+ A theme is three decisions made from one brief: color, type, and geometry. This skill owns the color decision directly and delegates the other two, so the whole look comes from the same reading of the brief. Never hand-author theme JSON and never edit the data tree directly.
9
9
 
10
10
  ## Workflow
11
11
 
12
- 1. Translate the brief into a seed file using the framework below. Write it to a temp path (not the project tree), e.g. `/tmp/theme-brief.json`.
13
- 2. Run `npx live-tokens generate-theme /tmp/theme-brief.json`. It writes `themes/<slug>.json`, opens that theme, and prints a contrast report card. Exit 1 means unmet floors.
14
- 3. Read the report. Auto-corrections are fine (the engine adjusted text curves to hit the floors). Unmet floors mean the seeds themselves are unworkable; each failure line says which seed to adjust (usually raise the seed's lightness or cut chroma). Fix the brief and re-run — same name, same file, it overwrites.
15
- 4. Tell the user to look at the running app. Offer refinements ("warmer", "more contrast", "less saturated") as seed adjustments to the same brief, re-run.
12
+ 1. Read the brief once and name its voice in a sentence: the mood, the hue family, the scheme, and the type and geometry that mood implies. Everything below keys off that sentence.
13
+ 2. Translate the brief into a seed file using the framework below. Write it to `scratch/theme-brief.json`.
14
+ 3. Run `npx live-tokens generate-theme scratch/theme-brief.json`. It writes `themes/<slug>.json`, opens that theme, and prints a contrast report. Auto-corrections are fine. Unmet floors (exit 1) mean the seeds themselves are unworkable; each failure line names the seed to change, usually by raising its lightness or cutting its chroma. Fix the brief and re-run; the same name overwrites. Regeneration replaces that theme's whole color state, including palette edits made in the editor since the last run, so say so once when iterating.
15
+ 4. Invoke **live-tokens-pair-fonts** with the same voice. Skip only when the user asked for colors specifically and said to leave the type alone.
16
+ 5. Invoke **live-tokens-adjust-geometry** with the geometry the voice implies (table below). Skip when the voice implies nothing about geometry.
17
+ 6. Tell the user to look at the running app, and that type and geometry sit in the unsaved buffer until they save the open theme. Offer refinements as edits to the same brief.
16
18
 
17
- Flags: `--dry-run` prints the report without writing; `--no-activate` writes the theme without opening it. Opening a theme never changes what the site ships: only Adopt, in the editor, does that.
19
+ Order matters only for safety, and the order above is safe: the color generator carries the live buffers forward into the new theme file, so a color re-roll after fonts and geometry keeps both.
18
20
 
19
- Warn once per session when iterating: regeneration replaces the whole color state of that theme, including any manual palette tweaks made in the editor after the last generation.
21
+ Flags: `--dry-run` prints the report without writing; `--no-activate` writes without opening. Opening a theme never changes what the site ships. Only Adopt, in the editor, does that.
20
22
 
21
23
  ## The brief
22
24
 
@@ -28,140 +30,119 @@ Warn once per session when iterating: regeneration replaces the whole color stat
28
30
  "Brand": { "l": 0.62, "c": 0.17, "h": 145 },
29
31
  "Accent": { "l": 0.80, "c": 0.15, "h": 95 },
30
32
  "Special": { "l": 0.60, "c": 0.19, "h": 300 },
31
- "Canvas": { "l": 0.97, "c": 0.01, "h": 120 },
33
+ "Canvas": { "l": 0.93, "c": 0.04, "h": 120 },
32
34
  "Neutral": { "l": 0.55, "c": 0.012, "h": 140 },
33
35
  "Alternate": { "l": 0.58, "c": 0.009, "h": 60 },
34
36
  "Info": { "l": 0.60, "c": 0.15, "h": 255 },
35
37
  "Success": { "l": 0.60, "c": 0.16, "h": 150 },
36
38
  "Warning": { "l": 0.75, "c": 0.15, "h": 85 },
37
39
  "Danger": { "l": 0.58, "c": 0.20, "h": 25 }
38
- },
39
- "harmony": { "mode": "analogous" }
40
+ }
40
41
  }
41
42
  ```
42
43
 
43
- All 10 seeds are required. A seed may also be a `"#rrggbb"` hex string (converted for you). OKLCH: `l` 0–1 perceptual lightness, `c` chroma (0 grey, ~0.37 max), `h` hue degrees. `name` becomes the theme file slug, tidied to lowercase with hyphens and stripped of any leading underscore (those names are reserved); `"default"` is refused (protected package theme). `harmony` is an optional record of your reasoning; the seeds are ground truth. `canvasGradient` is an optional boolean — see "The canvas sky" below before setting it.
44
+ All 10 seeds are required; a seed may also be a `"#rrggbb"` string. OKLCH: `l` is 0 to 1 lightness, `c` is chroma (0 grey, about 0.37 max), `h` is hue in degrees. `name` becomes the file slug; `"default"` is refused. `canvasGradient` is an optional boolean, see below.
44
45
 
45
- Roles: **Brand** is the dominant chromatic identity; **Accent** the supporting color; **Special** the rare expressive tertiary; **Canvas** is the page background verbatim; **Neutral** drives all neutral surfaces and body text; **Alternate** is the second near-grey family; the four status colors are conventional signals.
46
+ Roles: **Brand** is the dominant chromatic identity; **Accent** the supporting color; **Special** the rare expressive tertiary; **Canvas** is the page background verbatim; **Neutral** drives neutral surfaces and body text; **Alternate** is the second near-grey family; the four statuses are conventional signals.
46
47
 
47
48
  ## Chroma budget: color is inversely proportional to area
48
49
 
49
50
  | Tier | Palettes | Chroma |
50
51
  |---|---|---|
51
- | Ground (~60% of every screen) | Canvas, Neutral, Alternate | C 0.005–0.03 |
52
- | Dominant chromatic (~30%) | Brand | C 0.10–0.20 |
53
- | Garnish (~10%) | Accent, Special | may exceed Brand; at most one at full saturation |
54
- | Conditional | Info, Success, Warning, Danger | C 0.12–0.19 |
52
+ | Ground (about 60% of every screen) | Canvas, Neutral, Alternate | C 0.005 to 0.03 |
53
+ | Dominant chromatic (about 30%) | Brand | C 0.10 to 0.20 |
54
+ | Garnish (about 10%) | Accent, Special | may exceed Brand; at most one at full saturation |
55
+ | Conditional | Info, Success, Warning, Danger | C 0.12 to 0.19 |
55
56
 
56
- A good theme reads as 3–4 hue families on screen, never 10. Neutral and Alternate stay near-grey, tinted toward the theme (Neutral near Brand's hue; Alternate offset +15–60° or as a warm/cool counterpoint), never pure C = 0.
57
+ A good theme reads as 3 or 4 hue families on screen, never 10. Neutral and Alternate stay near-grey but tinted toward the theme (Neutral near Brand's hue; Alternate offset 15 to 60 degrees, or a warm/cool counterpoint), never pure C = 0.
57
58
 
58
59
  ## Per-role bands
59
60
 
60
61
  | Seed | Light scheme | Dark scheme | Hue |
61
62
  |---|---|---|---|
62
- | Canvas | L 0.92–0.98, C 0.02–0.06 (see below) | L 0.15–0.28, C 0.01–0.05 | brand hue or its harmony slot |
63
- | Neutral / Alternate | L ≈ 0.55, C 0.008–0.02 | same | see chroma budget |
64
- | Brand | L 0.45–0.62, C 0.12–0.20 | L 0.70–0.83, C cut by ~⅓ | the brief's identity hue |
65
- | Accent | harmony slot, or ΔL ≥ 0.25 from Brand when the mode collapses hue distance | lighten/desaturate like Brand | harmony slot |
66
- | Special | most expressive; default = Brand hue +60° at ~65% of Brand's C | same transform | harmony slot |
67
- | Info | shared status L (0.55–0.65 light) | lighten like Brand | H 230–260 |
68
- | Success | shared status L | same | H 140–155 |
69
- | Warning | L ≥ 0.75 (vivid yellow must be light) | same | H 70–90 |
70
- | Danger | shared status L, C 0.15–0.20 | same | H 20–30 |
71
-
72
- - **The canvas carries the theme's identity — commit to it.** The page background is the largest area on screen and the strongest differentiator between themes; a timid canvas makes every theme look the same. Below C ≈ 0.015 at L ≥ 0.95 a tint is imperceptible: that near-white is a deliberate choice for clean/minimal briefs, never the default. Three escalating levels of commitment, matched to the brief:
73
- 1. *Tinted paper* (most UI briefs): a tint you can actually see — C 0.02–0.06, dropping L to 0.92–0.95 where the hue needs room.
74
- 2. *Colored ground* (expressive briefs): L 0.85–0.92 at C 0.05–0.10 — the page is unmistakably mint, parchment, sky.
75
- 3. *Full-color ground* (holiday and statement briefs): the canvas IS the theme color — a red Christmas page (0.40–0.48, C 0.12–0.16, H 25) with green and gold on it, an orange Halloween page. Keep canvas L ≤ ~0.48 or ≥ ~0.85 so text has somewhere to go; the contrast gate enforces legibility either way. A saturated background is a legitimate choice, not something to correct.
76
- - **Gamut note for light canvases**: blue tints cap very low at high L (H 264 at L 0.95 barely reaches C 0.03) — for a blue-leaning canvas, lower L instead of fighting the ceiling; yellow/green/cream hues tint generously at high L.
77
- - **When generating a set of themes, make the canvases pairwise distinct.** Any two Canvas seeds should differ noticeably in hue (at visible chroma) or in L. Two light themes both near (0.97, 0.01, anything) read as the same theme with different buttons. Dark canvases differentiate the same way: deep indigo, plum, and near-black violet are three different nights.
78
- - **Dark scheme is a transform, not just a dark Canvas**: every chromatic seed lightens to L ≈ 0.75–0.85 and drops about a third of its chroma. Saturated color vibrates on dark grounds.
79
- - **Equal lightness = equal weight**: give the four statuses one shared L; do the same for Brand vs Accent when they should balance.
80
- - Status hues never rotate with the harmony; only their L/C adapt to the mood.
63
+ | Canvas | L 0.92 to 0.98, C 0.02 to 0.06 | L 0.15 to 0.28, C 0.01 to 0.05 | Brand's hue or its harmony slot |
64
+ | Neutral, Alternate | L about 0.55, C 0.008 to 0.02 | same | per the chroma budget |
65
+ | Brand | L 0.45 to 0.62, C 0.12 to 0.20 | L 0.70 to 0.83, C cut by a third | the brief's identity hue |
66
+ | Accent | harmony slot, or at least 0.25 L from Brand when the mode collapses hue distance | lighten and desaturate like Brand | harmony slot |
67
+ | Special | most expressive; default Brand hue +60 at about 65% of Brand's C | same transform | harmony slot |
68
+ | Info | shared status L (0.55 to 0.65 light) | lighten like Brand | H 230 to 260 |
69
+ | Success | shared status L | same | H 140 to 155 |
70
+ | Warning | L 0.75 or higher (vivid yellow must be light) | same | H 70 to 90 |
71
+ | Danger | shared status L, C 0.15 to 0.20 | same | H 20 to 30 |
72
+
73
+ **The canvas carries the theme's identity, so commit to it.** The page background is the largest area on screen and the strongest difference between themes; a timid canvas makes every theme look the same. Below C 0.015 at L 0.95 a tint is imperceptible, which makes near-white a deliberate choice for clean or minimal briefs and never the default. Three levels of commitment:
74
+
75
+ 1. *Tinted paper* (most UI briefs): C 0.02 to 0.06, with L down to 0.92 where the hue needs room.
76
+ 2. *Colored ground* (expressive briefs): L 0.85 to 0.92 at C 0.05 to 0.10. The page is unmistakably mint, parchment, sky.
77
+ 3. *Full-color ground* (holiday and statement briefs): the canvas is the theme color, like a red Christmas page with green and gold on it. Keep canvas L at or below 0.48 or at or above 0.85 so text has somewhere to go; the contrast gate enforces legibility either way.
78
+
79
+ Also:
80
+
81
+ - Blue tints cap very low at high L (H 264 at L 0.95 barely reaches C 0.03): lower L for a blue canvas rather than fighting the ceiling. Yellow, green, and cream tint generously at high L.
82
+ - When generating a set of themes, make the canvases pairwise distinct in hue or in L. Two light themes both near (0.97, 0.01) read as one theme with different buttons.
83
+ - A dark scheme is a transform, not just a dark canvas: every chromatic seed lightens to L 0.75 to 0.85 and drops about a third of its chroma, because saturated color vibrates on dark grounds.
84
+ - Equal lightness reads as equal weight: give the four statuses one shared L, and do the same for Brand and Accent when they should balance.
85
+ - Status hues never rotate with the harmony; only their L and C adapt to the mood.
81
86
 
82
87
  ## Mood dials
83
88
 
84
- Empirically: pleasantness rises with lightness (strongly) and saturation (weakly); energy/arousal rises with saturation; drama/dominance rises with dark + saturated.
89
+ Pleasantness rises with lightness (strongly) and saturation (weakly); energy rises with saturation; drama rises with dark plus saturated.
85
90
 
86
91
  | Brief says | Dials |
87
92
  |---|---|
88
- | cheerful, bright, playful | light scheme; Brand/Accent L 0.7–0.9, C 0.15–0.22; warm hues 40–140 (yellow is the strongest joy hue) |
89
- | calm, serene, soft | light scheme; C 0.03–0.08 on everything chromatic; cool hues 140–260 |
90
- | energetic, bold | C ≥ 0.18 at L 0.55–0.65; red/orange/magenta |
91
- | dark, moody, dramatic, luxurious | dark scheme; Canvas L 0.15–0.25; purple, deep blue, crimson; keep working accents light per the dark transform, reserve dark-saturated color for one or two moments |
92
- | professional, trustworthy | blue 230–265, C 0.08–0.15; everything else muted |
93
- | warm / cool | hues 20–110 (plus pink 290–360) / hues 140–290 |
93
+ | cheerful, bright, playful | light; Brand and Accent L 0.7 to 0.9, C 0.15 to 0.22; warm hues 40 to 140 (yellow is the strongest joy hue) |
94
+ | calm, serene, soft | light; C 0.03 to 0.08 on everything chromatic; cool hues 140 to 260 |
95
+ | energetic, bold | C 0.18 or more at L 0.55 to 0.65; red, orange, magenta |
96
+ | dark, moody, dramatic, luxurious | dark; Canvas L 0.15 to 0.25; purple, deep blue, crimson; working accents stay light per the dark transform, with dark saturated color saved for one or two moments |
97
+ | professional, trustworthy | blue 230 to 265, C 0.08 to 0.15; everything else muted |
98
+ | warm / cool | hues 20 to 110 plus pink 290 to 360 / hues 140 to 290 |
94
99
 
95
- Avoid mid-lightness yellow-green (H 100–120 at L 0.5–0.7, C ≈ 0.1) unless the brief asks for olive/toxic.
100
+ Avoid mid-lightness yellow-green (H 100 to 120 at L 0.5 to 0.7, C about 0.1) unless the brief asks for olive or toxic.
96
101
 
97
- ## Gamut guardrails (don't request impossible seeds)
102
+ ## Gamut guardrails
98
103
 
99
- - Dark saturated yellow does not exist: H 90 at L 0.4 caps at C ≈ 0.08 and reads olive. Vivid yellow needs L ≥ 0.8. Brown = dark low-chroma orange.
100
- - Vivid light blue does not exist: H 264 at L 0.9 caps at C ≈ 0.05. Rich blue lives at L 0.40–0.55.
101
- - Teal/sky cap at C ≈ 0.15; a "vivid teal" is C 0.13–0.15.
102
- - Peak chroma anchors: red H20 C 0.25 @ L 0.63; orange H60 C 0.18 @ L 0.76; yellow H90 C 0.18 @ L 0.86; green H140 C 0.28 @ L 0.88; blue H264 C 0.28 @ L 0.50; magenta H320 C 0.31 @ L 0.65.
104
+ The engine clamps to gamut regardless; these keep your intent achievable rather than silently muted.
103
105
 
104
- The engine gamut-clamps regardless; these rules keep your *intent* achievable rather than silently muted.
106
+ - Dark saturated yellow does not exist: H 90 at L 0.4 caps at C 0.08 and reads olive. Vivid yellow needs L 0.8 or more. Brown is dark low-chroma orange.
107
+ - Vivid light blue does not exist: H 264 at L 0.9 caps at C 0.05. Rich blue lives at L 0.40 to 0.55.
108
+ - Teal and sky cap at C 0.15.
109
+ - Peak chroma anchors: red H20 C 0.25 at L 0.63; orange H60 C 0.18 at L 0.76; yellow H90 C 0.18 at L 0.86; green H140 C 0.28 at L 0.88; blue H264 C 0.28 at L 0.50; magenta H320 C 0.31 at L 0.65.
105
110
 
106
- ## Choosing the harmony mode
111
+ ## Harmony
107
112
 
108
- Modes and hue offsets from Brand: complementary +180; split-complementary +150/+210; triadic +120/+240; tetradic +60/+180/+240; square +90 steps; compound +30/+180/+210; analogous ±30; monochromatic same hue.
113
+ Hue offsets from Brand: complementary +180; split-complementary +150/+210; triadic +120/+240; tetradic +60/+180/+240; square +90 steps; compound +30/+180/+210; analogous plus or minus 30; monochromatic same hue.
109
114
 
110
- - Vague or single-adjective brief → monochromatic or analogous; differentiate Accent from Brand by L/C, not hue. Polished-UI default: Accent = Brand's hue at ~45% chroma, Special = +60° at ~65% chroma.
111
- - Brief names two colors → measure their hue gap and pick the matching mode (green + gold ≈ 60–90° → analogous/compound).
112
- - Drama or maximum contrast → complementary/triadic/tetradic; then never pair max-chroma text with a near-black ground; tone one side down.
115
+ - A vague or single-adjective brief takes monochromatic or analogous, with Accent separated from Brand by L and C rather than hue. The polished-UI default: Accent at Brand's hue and about 45% of its chroma, Special at +60 and about 65%.
116
+ - A brief naming two colors: measure their hue gap and pick the matching mode (green plus gold is 60 to 90 degrees, so analogous or compound).
117
+ - Drama or maximum contrast: complementary, triadic, or tetradic, and then tone one side down, since max-chroma text on a near-black ground vibrates.
113
118
 
114
- ## The canvas sky (optional page-background gradient)
119
+ ## Canvas sky and shadows
115
120
 
116
- The brief may set `"canvasGradient": true` to render the page background as a
117
- vertical gradient built from the Canvas ramp. The engine picks the stops itself
118
- (two ramp steps from the Canvas anchor on the scheme's safe side), so the
119
- choice you make is only *whether*, never *how*.
121
+ `"canvasGradient": true` renders the page background as a vertical gradient from the Canvas ramp. Default off. Turn it on only when the brief evokes atmosphere (sky, night, dusk, glow, underwater) or asks for a gradient outright; keep it off for crisp, flat, minimal, or corporate briefs and whenever in doubt, because a sky on every theme stops meaning anything. It needs a committed canvas (level 2 or 3); at the ramp edge the engine skips it and says so. Say why you enabled it in one line.
120
122
 
121
- Default **off**. Turn it on only when the brief evokes atmosphere: sky, night,
122
- dusk, dawn, sunset, glow, candlelight, underwater, depth — or when the user
123
- asks for a gradient outright. Keep it off for crisp, clean, flat, minimal, or
124
- corporate briefs, and whenever in doubt. Most themes should not have one; a sky
125
- that appears on every generated theme stops meaning anything. When you enable
126
- it, say why in one sentence when reporting back ("night brief → canvas sky").
123
+ Shadow opacity derives from Canvas lightness and re-derives on every run, so there is nothing to choose. When shadows read heavy or muddy, raise the Canvas seed's L.
127
124
 
128
- A sky needs a committed canvas: when the Canvas seed anchors at the ramp edge
129
- (near-white light canvas, near-black dark one) there is no room on the safe
130
- side and the engine skips the sky, saying so in the report. An atmospheric
131
- brief that wants one should already be at canvas commitment level 2–3.
125
+ ## Named themes
132
126
 
133
- ## Shadow weight follows the canvas
127
+ A holiday or season brief is a statement brief: commitment level 2 or 3, with the named color on the ground rather than only on the buttons. Read `references/named-themes.md` for the OKLCH anchors of Christmas, Halloween, St. Patrick's, Ocean, Sunset, Autumn, and Spring before seeding one.
134
128
 
135
- The engine sets the opacity of the `--shadow-*` scale from the Canvas seed's
136
- lightness and carries each shadow's geometry and color through untouched. A
137
- near-black shadow at 0.9 opacity is what a dark ground needs to show any
138
- shadow at all, and it punches a hole in paper, so opacity holds at 0.9 up to
139
- canvas L 0.5 and eases to 0.2 by L 0.9. There is nothing to choose: the report
140
- prints the derived value, and every regeneration re-derives it, so a brief
141
- iterated from a light canvas to a dark one gets its weight back.
129
+ ## Geometry from the voice
142
130
 
143
- When the user says the shadows read heavy, muddy, or dirty, the canvas is the
144
- lever. Raise the Canvas seed's lightness and the shadows lighten with it. A
145
- one-off override lives in the editor's Shadows tab and survives regeneration
146
- in everything except opacity.
131
+ The geometry lives in radius, padding, gap, and border width, and `live-tokens-adjust-geometry` knows the mechanics. Hand it the intent:
147
132
 
148
- ## Named themes (canonical OKLCH anchors)
149
-
150
- Holiday briefs are statement briefs — default to commitment level 2–3 above, not cream. The named colors go on the *ground*, not just the buttons.
151
-
152
- - **Christmas**: red (0.53, 0.21, 22) + green (0.46, 0.11, 155) + gold (0.77, 0.14, 91). Strongest form: a full red canvas (0.42, 0.14, 25) with green Brand and gold Accent on it (dark scheme). Softer form: evergreen canvas (0.35, 0.07, 160) or deep cream (0.95, 0.04, 85). Never a 50/50 red-green split — one owns the ground, the other highlights.
153
- - **Halloween**: pumpkin (0.70, 0.20, 46) + purple (0.51, 0.21, 313) + poison green (0.73, 0.20, 137). Strongest form: an orange canvas (0.45, 0.13, 55) with violet and poison-green accents; alternative: near-black violet canvas with pumpkin Brand. Dark scheme either way.
154
- - **St. Patrick's**: green (0.51, 0.13, 152) Brand, gold Accent, white/beige neutrals.
155
- - **Ocean**: deep blue (0.35, 0.08, 237) vs aqua (0.78, 0.12, 214); H 180–240.
156
- - **Sunset**: hues 90 → 320 through red, L falling 0.85 → 0.40.
157
- - **Autumn / Fall**: leaves and dry grass — canvas warm tan/parchment (0.87, 0.06, 80), rust Brand (0.55, 0.15, 40), golden Accent (0.75, 0.15, 85), moss/olive Special (0.55, 0.10, 120), warm brown neutrals H 50–70. Deep red H 25 welcome. **Spring**: pastels L 0.85–0.95, C 0.04–0.10, greens 130–150 / pinks 0–20, mint canvas.
133
+ | Voice | Geometry |
134
+ |---|---|
135
+ | playful, friendly, soft | rounder, a step airier; pill buttons when the brief is warm |
136
+ | luxurious, elegant, editorial | sharper corners, airier padding, thin borders |
137
+ | technical, dense, systematic | tighter spacing, small radius, square corners on containers |
138
+ | calm, minimal | leave geometry alone unless the brief says otherwise |
158
139
 
159
- ## Scope
140
+ ## What each step writes
160
141
 
161
- Fonts are never touched (they carry forward from the live look, as do component aliases and shadow geometry; shadow opacity is derived, see above). Swatch gradients (`--gradient-1..4`) carry forward when user-tuned; if they are absent or still stock, the engine rebuilds them from the new theme's families — you never author them. Radius is out of scope for generation. Shipping the theme stays a human action: Adopt, in the editor.
142
+ Color writes `themes/<slug>.json` and opens it. Type and geometry write the unsaved buffers, which the page already runs. One Save in the editor keeps all three; Adopt ships them. Component aliases and gradients carry forward from the live look into a generated theme; user-tuned gradients survive, stock ones rebuild from the new families.
162
143
 
163
144
  ## Verify
164
145
 
165
- - The CLI exits 0 and the report card shows every check ✓ (auto-corrected is fine).
166
- - The app (dev server running) shows the new theme after a reload; the editor's Theme panel names it.
167
- - If the user wants the previous look back, the CLI output names the theme that was open; load it from the Theme panel.
146
+ - The color CLI exits 0 with every check passing (auto-corrected is fine), and the two sibling skills report what they changed.
147
+ - The app (dev server running) shows the whole look after a reload, and the editor's Theme panel names the theme.
148
+ - To return to the previous look, load the theme the CLI output named from the Theme panel; that discards the buffers too.
@@ -0,0 +1,18 @@
1
+ # Named themes: canonical OKLCH anchors
2
+
3
+ Read this when the brief names a holiday, a season, or a natural scene. These
4
+ are starting points, not seeds to copy: apply the chroma budget, per-role bands,
5
+ and canvas commitment rules from SKILL.md on top of them.
6
+
7
+ Holiday briefs are statement briefs. Default to canvas commitment level 2 or 3,
8
+ never cream, and put the named color on the ground, not only on the buttons.
9
+
10
+ | Brief | Anchors (L, C, H) | Strongest form |
11
+ |---|---|---|
12
+ | Christmas | red (0.53, 0.21, 22), green (0.46, 0.11, 155), gold (0.77, 0.14, 91) | red canvas (0.42, 0.14, 25), green Brand, gold Accent, dark scheme. Softer: evergreen canvas (0.35, 0.07, 160) or deep cream (0.95, 0.04, 85). One of red or green owns the ground; never a 50/50 split. |
13
+ | Halloween | pumpkin (0.70, 0.20, 46), purple (0.51, 0.21, 313), poison green (0.73, 0.20, 137) | orange canvas (0.45, 0.13, 55) with violet and poison-green accents, or near-black violet canvas with pumpkin Brand. Dark scheme either way. |
14
+ | St. Patrick's | green (0.51, 0.13, 152) | green Brand, gold Accent, white or beige neutrals. |
15
+ | Ocean | deep blue (0.35, 0.08, 237), aqua (0.78, 0.12, 214) | hues held to 180 to 240. |
16
+ | Sunset | hues 90 to 320 through red | L falls 0.85 to 0.40 across the sweep. |
17
+ | Autumn | parchment canvas (0.87, 0.06, 80), rust Brand (0.55, 0.15, 40), gold Accent (0.75, 0.15, 85), moss Special (0.55, 0.10, 120) | warm brown neutrals H 50 to 70; deep red H 25 welcome. |
18
+ | Spring | pastels L 0.85 to 0.95, C 0.04 to 0.10 | greens 130 to 150, pinks 0 to 20, mint canvas. |
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: live-tokens-pair-fonts
3
+ description: Choose and apply a Google Fonts pairing for a live-tokens theme by binding families to the --font-display, --font-sans, --font-serif and --font-mono stacks. Use whenever the user asks to pair fonts, pick a typeface, change or set the fonts, or describes type by voice: give me a font pairing, what font should the headings use, make the type more editorial, friendlier, more technical, more elegant, a serif for headings, a display font for this theme, less generic type, match the fonts to the theme. Also invoked by live-tokens-generate-theme for the type half of a whole look. Changes type only, never color. Not for a single token (use the editor) or for color (see live-tokens-generate-theme).
4
+ ---
5
+
6
+ # Pairing fonts for a theme
7
+
8
+ You choose the families; the CLI verifies each against Google Fonts, builds the URL from the weights the family actually has, and writes the result into the unsaved buffer. Never hand-author font JSON and never edit the data tree directly. Google Fonts is the pool because it is freely licensable and loads by URL; other sources go in by hand through the editor's Project fonts section.
9
+
10
+ ## Workflow
11
+
12
+ 1. Choose the pairing with the framework below and write a brief to `scratch/font-brief.json`.
13
+ 2. Run `npx live-tokens set-fonts scratch/font-brief.json`. It prints each stack that moved, each family's real weights and URL, and the weights your typography tokens ask for that the family lacks.
14
+ 3. Read the report. A weight gap is a quality note: name it and offer an alternative only if it matters (a body face without 400 or 700 matters; a display face without 300 does not). A family not on Google Fonts fails the run; fix the spelling and re-run.
15
+ 4. Tell the user to reload and look, and that the edit is unsaved until they save the open theme.
16
+
17
+ State your reasoning when you propose the pairing: each face's form model and the matrix verdict, in one sentence, so the user can argue with the argument rather than only the result.
18
+
19
+ Flags: `--dry-run` reports without writing. `--no-verify` skips the network and requires an explicit URL per family; use it only offline with a URL in hand.
20
+
21
+ ## The brief
22
+
23
+ ```json
24
+ { "display": "Fraunces", "body": "Nunito Sans" }
25
+ ```
26
+
27
+ Every slot is optional and an omitted slot is left exactly as it is. `display` is `--font-display`, `body` is `--font-sans`; `serif` and `mono` exist when a theme needs them. A slot may be `{ "name": "...", "url": "..." }` to pin an exact URL. Spell families as Google does; the CLI reports the canonical spelling back.
28
+
29
+ ## Choose the body face first
30
+
31
+ The body face is the anchor. It carries most of the words, and text faces survive small sizes where display faces do not. Pick it against the brief, then pick the display face against it. A body face must have regular, bold, and italic; low to moderate stroke contrast; open apertures; and a large x-height. A face failing any of these is a display face whatever its name says. Single-weight families are fine for `display` and disqualifying for `body`.
32
+
33
+ ## The font matrix: the decision rule
34
+
35
+ Classify each candidate on two layers. The **skeleton** is its form model; the **flesh** is its stroke contrast and serif treatment.
36
+
37
+ | Form model | Construction | Reads as |
38
+ |---|---|---|
39
+ | **Dynamic** | diagonal stress, open apertures, written origin | open, warm, humane, timeless |
40
+ | **Rational** | vertical stress, closed apertures, drawn not written | orderly, reserved, elegant, authoritative |
41
+ | **Geometric** | monolinear, circle-and-line | technical, modern, systematic, sober |
42
+
43
+ - **Same skeleton, different flesh: reliable.** Helvetica and Bodoni are both rational, one a linear sans and one a contrasting serif.
44
+ - **Same flesh, different skeleton: the failure case.** The two look alike on the surface and fight underneath. This is why two arbitrary sans-serifs so often clash.
45
+ - **Far apart on both: works, deliberately.** An unmistakable difference reads as a decision.
46
+
47
+ Many faces sit between columns. When one straddles, say so and lean on the voice table and the x-height check instead.
48
+
49
+ ## Voice
50
+
51
+ | Brief says | Type voice |
52
+ |---|---|
53
+ | editorial, literary, considered | dynamic serif display over a humanist sans body |
54
+ | elegant, luxurious, formal | rational high-contrast serif display; keep the body quiet |
55
+ | friendly, warm, approachable | dynamic sans on both sides, or a soft serif display |
56
+ | technical, systematic, precise | geometric or neo-grotesque sans; a mono for code |
57
+ | playful, informal | an expressive display face over a plain workhorse body |
58
+ | serious, institutional, trustworthy | rational sans body, rational serif display |
59
+ | quiet, minimal, unbranded | one superfamily across both slots |
60
+
61
+ Match the type to the same brief the color came from. A warm autumn palette under a cold geometric sans reads as two projects.
62
+
63
+ ## Shortcuts
64
+
65
+ These find an adequate pairing fast and skip the reasoning; use them when the brief is vague or the type should stay quiet.
66
+
67
+ - **A superfamily.** Google Fonts families with both sans and serif siblings: Alegreya, Ancizar, IBM Plex, Inria, Merriweather, Noto, PT, Roboto, Source.
68
+ - **One family across weights.**
69
+ - **Same designer or foundry.**
70
+ - **Serif display over sans body** when nothing else decides it.
71
+
72
+ ## Watch for
73
+
74
+ - **x-height parity.** Both faces are set from one size scale, so a small-x-height display face over a large-x-height body face gives a heading that looks weaker than its own body text. This is the one visual check that matters on screen; make it on the rendered page.
75
+ - **Print faces at small sizes.** Delicate serifs and high stroke contrast turn to mud below 16px.
76
+ - **Every family is a request.** Two is the target; three needs a reason.
77
+ - **Sets of themes:** no two share a display face or a body face.
78
+
79
+ ## Scope
80
+
81
+ Type only. Color, component aliases, shape, and the type scale are untouched: `set-fonts` moves families between stacks and nothing else, writing only the unsaved colors-and-type buffer. Save the theme to keep it, Adopt to ship it.
82
+
83
+ ## Verify
84
+
85
+ - The CLI exits 0 and names each stack that moved, before and after.
86
+ - Each URL reflects the family's real weights: a range for a variable family, an enumeration for a static one, a bare URL for a single-weight face.
87
+ - The app shows the new type after a reload, and the editor's Fonts section lists both families with their fallbacks intact.
88
+ - To revert, run the inverse brief, or load the open theme again to discard the buffer.
@@ -11,7 +11,7 @@ For composing a page once you've picked components, see [[live-tokens-build-page
11
11
 
12
12
  ## Catalogue
13
13
 
14
- Action: `Button`, `IconButton`. Input: `Input`. Selection: `SegmentedControl`, `TabBar`, `RadioButton`, `MenuSelect`, `Toggle`. Containers: `Card`, `CollapsibleSection`, `Dialog`. Messaging: `Callout`, `Notification`, `Tooltip`, `Badge`, `CornerBadge`. Display: `Table`, `Image`, `ImageLightbox`, `ProgressBar`, `SectionDivider`, `SideNavigation`, `CodeSnippet`.
14
+ Action: `Button`, `IconButton`, `InlineEditActions`. Input: `Input`. Selection: `SegmentedControl`, `TabBar`, `RadioButton`, `MenuSelect`, `Toggle`. Containers: `Card`, `CollapsibleSection`, `Dialog`, `Panel`. Messaging: `Callout`, `Notification`, `Tooltip`, `Badge`, `CornerBadge`. Display: `Table`, `Image`, `ImageLightbox`, `ProgressBar`, `SectionDivider`, `SideNavigation`, `CodeSnippet`.
15
15
 
16
16
  `CodeSnippet` is for a single-line command or value the user is meant to copy and paste back into a terminal (install commands, generated keys, ids). Click-to-copy with a brief "Copied" popover. Use it whenever your page asks the reader to *run* something, rather than just *read* it.
17
17
 
@@ -22,6 +22,7 @@ Both trigger an action and share the same six variants (primary, secondary, outl
22
22
  - `Button` carries a text label, optionally with a leading or trailing icon. Use it whenever the action needs a word to be unambiguous.
23
23
  - `IconButton` is icon-only and square. Use it for compact, space-constrained actions whose meaning is obvious from the glyph alone (toolbar controls, close/edit/delete affordances, card overflow menus). It has no text slot, so an `ariaLabel` is required for accessibility.
24
24
  - **Don't reach for `IconButton` when the icon's meaning isn't self-evident.** A labelled `Button` (or a `Button` with an icon) avoids the guessing game.
25
+ - `InlineEditActions` is the confirm-and-cancel pair that follows an inline edit (rename a row, edit a value in place). Use it rather than two loose `IconButton`s so every inline edit on the page resolves the same way.
25
26
 
26
27
  ## Single-selection family: SegmentedControl vs TabBar vs RadioButton vs MenuSelect
27
28
 
@@ -46,9 +47,11 @@ All four pick one option from a set. The right one depends on **option count**,
46
47
  | `Card` | Inline, always open | Default container for grouped content |
47
48
  | `CollapsibleSection` | Inline, toggleable | Progressive disclosure inside a longer page |
48
49
  | `Dialog` | Modal, blocks page | Confirmations, focused tasks the page can't continue around |
50
+ | `Panel` | Inline, fixed stage | A demo or preview surface whose height must not reflow |
49
51
 
50
52
  - Default to `Card`. It's the workhorse.
51
53
  - Reach for `CollapsibleSection` only when the content is *legitimately secondary* (advanced users open it; most skip). Don't use collapse as a styling choice when the content matters.
54
+ - `Panel` is a stage, not a content container. It pins its own height so what it shows can resize without moving the page, which is what a component preview or a live example needs and what article content does not. Content goes in `Card`.
52
55
  - **Don't use `Dialog` for routine forms.** Reach for it only when the page cannot meaningfully continue until the user decides (destructive confirmations, payment, sign-in). Routine forms go inline in a `Card`.
53
56
 
54
57
  ## Messaging family: Callout vs Notification vs Tooltip vs Badge
package/CHANGELOG.md CHANGED
@@ -1,5 +1,113 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.56.1 — Release fix
4
+
5
+ ### Fixed
6
+
7
+ - **The 0.56.0 publish failed in CI and never reached npm.** `scripts/lib/presetFonts.mjs`
8
+ loaded the compiled font-pairing engine at import time, and a vitest suite
9
+ imports that module for its `PRESET_FONTS` table; CI runs the tests before
10
+ it builds the plugin. The engine now loads inside `stampPresetFonts`, the
11
+ one caller that needs it. Everything listed under 0.56.0 ships here.
12
+
13
+ ## 0.56.0 — Type is a first-class half of a theme
14
+
15
+ ### Added
16
+
17
+ - **`live-tokens set-fonts` and the `live-tokens-pair-fonts` skill.** A theme's
18
+ type can now be chosen the way its color already could: describe the voice you
19
+ want and get a verified Google Fonts pairing bound to `--font-display`,
20
+ `--font-sans`, `--font-serif` and `--font-mono`. The skill carries the
21
+ reasoning (anchor on the body face, classify both candidates by form model,
22
+ apply the font matrix, gate every candidate on screen legibility); the CLI
23
+ does the verifying and the writing. Like `adjust`, it edits the unsaved
24
+ colors-and-type buffer, so a retype is an edit you keep by saving the open
25
+ theme. Color, component aliases, `tokens.css` and `fonts.css` are untouched.
26
+ - **URLs are negotiated from the family's real weights, not guessed.** A 200
27
+ from the Google Fonts API never proved weight coverage: the API silently drops
28
+ enumerated weights a family lacks and only rejects a *range* its axis cannot
29
+ serve. `set-fonts` now takes a census from the returned CSS and builds the
30
+ narrowest URL that delivers everything the family has: a range for a variable
31
+ family, an enumeration for a static one, a bare URL for a single-weight
32
+ display face. It also reports the weights your typography tokens ask for and
33
+ the family does not have.
34
+ - **`generate-theme` owns the whole look.** A theme is three decisions made
35
+ from one brief: color, type, and geometry. The skill reads the brief once,
36
+ names its voice, seeds the color, then invokes `live-tokens-pair-fonts` and
37
+ `live-tokens-adjust-geometry` with the same voice, so a moody night theme
38
+ arrives with type and corners to match rather than with the previous look's.
39
+ The three CLIs stay separate, so any one decision retunes without re-rolling
40
+ the others.
41
+
42
+ ### Changed (breaking)
43
+
44
+ - **`live-tokens-adjust-shape-space` is now `live-tokens-adjust-geometry`.**
45
+ Same skill, same `adjust` verb; the name now matches the heading the token
46
+ suffix vocabulary already uses for radius, padding, gap, and border width.
47
+ Re-run `npx live-tokens setup-claude --force` to pick it up, then delete
48
+ `.claude/skills/live-tokens-adjust-shape-space/` by hand: `setup-claude`
49
+ never removes a directory, and the stale copy would keep triggering under
50
+ the old name.
51
+
52
+ ### Changed
53
+
54
+ - **Skills are leaner and read the package instead of copying it.** The
55
+ create-component skill no longer inlines the shipped `Toggle` (the copy had
56
+ drifted 174 lines from its source); it points at the files in
57
+ `node_modules` and moves the linked-siblings, intrinsics, and fixed-overlay
58
+ material into reference files read on demand. The theme skill moved its
59
+ holiday anchors the same way. Every skill body is now under 250 lines.
60
+ - **The picker catalogue lists `Panel` and `InlineEditActions`.** Both shipped
61
+ without an entry, so the picker could never recommend them.
62
+ - **`npm run check:skills` gates the bundle.** It asserts the catalogue names
63
+ every shipped component, every reference file is pointed at and present,
64
+ every CLI verb a skill mentions exists, every skill has a sample prompt, and
65
+ no skill pastes a long file inline.
66
+
67
+ ### Fixed
68
+
69
+ - **Adding a Google font by name in the editor no longer persists a dead URL.**
70
+ The by-name field built a `wght@100..900` range URL and never checked it, so
71
+ any family without that exact variable axis (every static family, every
72
+ single-weight display face) was saved with a URL the API answers 400 to. It
73
+ now runs the same negotiation `set-fonts` does, reports the family's real
74
+ weights, and fails loudly for a family that is not on Google Fonts.
75
+
76
+ ## 0.55.1 — The anchored step is the base color
77
+
78
+ ### Fixed
79
+
80
+ - **Editing the anchored palette step edits the base color.** The anchor pins
81
+ the lightness, saturation and hue curves through one step, so that swatch was
82
+ already rendering the base color. Clicking it opened a per-step override,
83
+ which forked the two apart: the ramp moved, the family's base swatch and hex
84
+ did not, and the palette stopped passing through the base color the anchor
85
+ guarantees it passes through. That step now opens the base color itself. The
86
+ whole ramp re-derives, a bound harmony axis follows the hue, and the header
87
+ swatch moves with it. Both swatches carry the selection ring together and the
88
+ panel titles itself "Base Color > 500", so the pairing is stated rather than
89
+ inferred. An override left on the anchored step by an earlier build is cleared
90
+ when the step is opened, inside the edit session, so Cancel and undo both put
91
+ it back. Every other step keeps its own override, unchanged.
92
+
93
+ ### Changed
94
+
95
+ - **The color editor docks under the row it edits.** It rendered at the bottom
96
+ of the family block, past the curve editors and the Text, Surfaces and Borders
97
+ section, far enough from the swatch that opened it to read as unrelated
98
+ chrome. It now opens directly beneath the ramp for a palette step, or beneath
99
+ the derived row that owns the step, with a caret on the step's own column. The
100
+ caret is placed on the grid rather than positioned, so it stays under its
101
+ swatch at any width. Showing and hiding use the disclosure motion the
102
+ expandable sections already use.
103
+
104
+ - **Selection reads by weight.** The editor's chrome is greyscale by design, so
105
+ a selected swatch is marked by a heavy ring drawn inside it: a white band
106
+ between two dark keylines, which survives both ends of a ramp running white to
107
+ black where a single-tone ring disappears at one end. The ring is inset rather
108
+ than an outline, which would close the gutter between neighbouring swatches,
109
+ or a thicker border, which would resize the swatch inside its own grid.
110
+
3
111
  ## 0.55.0 — A focused palette arrives ready
4
112
 
5
113
  ### Changed
package/README.md CHANGED
@@ -17,7 +17,7 @@ The editor is dev-only. Production builds get plain CSS variables and the compon
17
17
  - **Themes.** A theme is a whole look in one file: colors and type plus a config for every component, stored by value. Loading one changes a single pointer file, and nothing your site ships changes until you Adopt. Export a theme and import it into another project to restore the look in one step.
18
18
  - **Seven example looks.** Autumn, Halloween, Midnight Study, Ocean, Royal Velvet, Spring Meadow, and Sunset each ship as a full theme: colors, a Google Fonts pairing, and a shape personality of radius, padding, gap, and border-width aliases. They ship inside the package, so trying one needs no local files. Load Motion Proto to return to the default. Saving over a preset writes a local copy that shadows the shipped one; delete the copy and the shipped version returns.
19
19
  - **Vite plugin.** Hosts the `/api/live-tokens/{colors-and-type,component-configs,themes}/*` routes the editor reads and writes through. The single namespace keeps these routes clear of anything your app serves under `/api`.
20
- - **Claude Code skills.** Five bundled skills that drive the package from plain English. See [Claude Code skills](#claude-code-skills).
20
+ - **Claude Code skills.** Six bundled skills that drive the package from plain English. See [Claude Code skills](#claude-code-skills).
21
21
 
22
22
  ## Install
23
23
 
@@ -267,13 +267,14 @@ npx @motion-proto/live-tokens <command>
267
267
  | `check-component <id>` | Validate a component's runtime, editor, and registration against the authoring contract. |
268
268
  | `generate-theme <brief.json> [--no-activate] [--dry-run] [--carry-from <name>]` | Build a full theme from a 10-seed OKLCH brief, enforce AA contrast, write `themes/<slug>.json`, and open it. |
269
269
  | `adjust <ops.json> [--dry-run]` | Move radius, padding, gap, and border-width aliases along their token scales. |
270
+ | `set-fonts <brief.json> [--dry-run] [--no-verify]` | Bind Google Fonts families to the theme's font stacks, verified against the API. |
270
271
  | `migrate [--check] [--write] [--tokens <path>]` | Reconcile the project with the installed package: additive `tokens.css` migrations, the pre-0.48 data-tree move, and a report on source references to the routes that moved in 0.35.0. |
271
272
 
272
273
  Once installed in a project, the same commands are available as `npx live-tokens <command>`.
273
274
 
274
275
  ## Claude Code skills
275
276
 
276
- The package bundles five Claude Code skills. They encode the conventions this README cannot carry in full: which component fits a need, how a page is wired, what a valid theme looks like in OKLCH, and how shape and space move along the token scales. Each triggers from an ordinary request, so there are no slash commands to learn.
277
+ The package bundles six Claude Code skills. They encode the conventions this README cannot carry in full: which component fits a need, how a page is wired, what a valid theme looks like in OKLCH, how two typefaces sit together, and how geometry moves along the token scales. Each triggers from an ordinary request, so there are no slash commands to learn.
277
278
 
278
279
  ### Install
279
280
 
@@ -303,13 +304,25 @@ The skill composes the page from shipped components, styles every value with `va
303
304
 
304
305
  Ask for a look: "a dark, moody night theme", "a St Patrick's Day theme in green and gold", "warmer", "more contrast", "calmer".
305
306
 
306
- The skill translates the brief into ten OKLCH seeds (Brand, Accent, Special, Canvas, Neutral, Alternate, Info, Success, Warning, Danger) plus a light or dark scheme, then runs `npx live-tokens generate-theme <brief.json>`. The CLI assembles the curves, enforces AA contrast on derived text tokens and auto-corrects where it can, writes `themes/<slug>.json`, opens it, and prints a contrast report. Exit 1 means the seeds themselves are unworkable, and each failure line names the seed to change.
307
+ A theme is three decisions made from one brief: color, type, and geometry. The skill owns color and delegates the other two to `live-tokens-pair-fonts` and `live-tokens-adjust-geometry`, so the whole look comes from the same reading of the brief.
307
308
 
308
- Most of the skill is the judgment the generator cannot supply: a chroma budget scaled to how much screen area each palette covers, per-role lightness and hue bands for each scheme, gamut guardrails against impossible seeds, harmony modes, the optional canvas gradient, shadow weight for the canvas, and OKLCH anchors for named colors.
309
+ For color it translates the brief into ten OKLCH seeds (Brand, Accent, Special, Canvas, Neutral, Alternate, Info, Success, Warning, Danger) plus a light or dark scheme, then runs `npx live-tokens generate-theme <brief.json>`. The CLI assembles the curves, enforces AA contrast on derived text tokens and auto-corrects where it can, writes `themes/<slug>.json`, opens it, and prints a contrast report. Exit 1 means the seeds themselves are unworkable, and each failure line names the seed to change.
309
310
 
310
- Scope: colors only. Fonts, gradients, and component aliases carry forward from the open theme, or from `--carry-from <name>`. `--dry-run` prints the report without writing; `--no-activate` writes without opening. Opening a theme never changes what your site ships; Adopt does. Regenerating replaces that theme's whole color state, including palette edits made in the editor since the last run.
311
+ Most of the skill is the judgment the generator cannot supply: a chroma budget scaled to how much screen area each palette covers, per-role lightness and hue bands for each scheme, gamut guardrails against impossible seeds, harmony modes, the optional canvas gradient, and a voice-to-shape table for the shape step. OKLCH anchors for named holidays and seasons live in a reference file the skill reads on demand.
311
312
 
312
- ### `live-tokens-adjust-shape-space`
313
+ Color lands in the theme file; type and shape land in the unsaved buffers, and one Save keeps all three. `--dry-run` prints the report without writing; `--no-activate` writes without opening. Opening a theme never changes what your site ships; Adopt does. Regenerating replaces that theme's whole color state, including palette edits made in the editor since the last run, and carries the live buffers forward, so re-rolling color after setting fonts and shape keeps both.
314
+
315
+ ### `live-tokens-pair-fonts`
316
+
317
+ Ask for type: "pair some fonts for this theme", "what font should the headings use?", "make the type more editorial", "something friendlier", "a serif for headings".
318
+
319
+ The skill chooses the families and runs `npx live-tokens set-fonts <brief.json>`, which binds each one to `--font-display`, `--font-sans`, `--font-serif`, or `--font-mono`. Every family is checked against the Google Fonts API before it is written, and the URL is built from the weights that family actually has: a range for a variable font, an enumeration for a static one, a bare URL for a single-weight display face. The report names the weights your typography tokens ask for and the family does not carry.
320
+
321
+ The judgment is the skill's half. It anchors on the body face, because that is most of the words on the page and text faces survive small sizes where display faces do not. It classifies both candidates by form model (dynamic, rational, geometric) and applies the font matrix: two faces sharing a skeleton under different surfaces pair reliably, two faces sharing a surface over different skeletons fight, and two faces far apart on both read as a decision. It also carries the screen test a body face has to pass, a voice table from brief to type, and the Google Fonts superfamilies for when the type should stay quiet.
322
+
323
+ Scope: type only, and never color. Edits land in the colors-and-type `_working.json` buffer, so save the open theme to keep them. `--dry-run` reports without writing.
324
+
325
+ ### `live-tokens-adjust-geometry`
313
326
 
314
327
  Ask for shape or space: "make the buttons pill shaped", "sharper corners on the cards", "space it out", "tighter", "thinner borders".
315
328
 
@@ -323,7 +336,7 @@ Edits land in each affected component's `_working.json` buffer, which is what th
323
336
 
324
337
  Ask for something the catalogue lacks: "author a Rating component", "make my Chip component editable in the editor".
325
338
 
326
- The skill covers the four-step recipe: the runtime `.svelte` file with its `:global(:root)` token block, the editor `.svelte` file exporting `allTokens` and its variant groups, the `registerComponent()` call, and the catalogue entry that keeps `live-tokens-pick-component` current. It carries the naming scheme, the token suffix vocabulary, the state model (component states such as selected and disabled are separate from interaction states such as hover), linked siblings, the public-imports rule, and the shipped `Toggle` as a worked example from runtime file to registration.
339
+ The skill covers the recipe: the runtime `.svelte` file with its `:global(:root)` token block, the editor `.svelte` file exporting `allTokens` and its variant groups, the `registerComponent()` call, and the catalogue entry that keeps `live-tokens-pick-component` current. It carries the naming scheme, the token suffix vocabulary, the state model (component states such as selected and disabled are separate from interaction states such as hover), and the public-imports rule, and points at the shipped `Toggle` in `node_modules` as the worked example. Linked siblings, intrinsics, and the fixed-overlay portal rule sit in reference files the skill reads only when a component needs them.
327
340
 
328
341
  Verify the result:
329
342