From e7b7a9d92bb4e34cacf23d0452e88225ce4a3339 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 15:27:54 +0000 Subject: [PATCH 1/3] fix(configurator): stop the card backdrop flattening the button size-scale font MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Components preview wrapped every button row in well() = .sf-card. The framework's `.sf-card .sf-btn { --sf-btn-font-size--size: var( --sf-card-btn-font-size, var(--sf-text-s)) }` rule pins any nested button's label to text-s, so in the Size-scale demo all rungs rendered the SAME label font while only padding/min-height grew — the per-rung font ladder (the whole point of that section) was invisible, most obviously at narrow widths where the fluid text steps all collapsed toward text-s. Give the button rows a non-card framed surface (frame()) so each rung renders its true --sf-text-{rung} label size. Cards keep .sf-card. Measured at 412px: labels now scale xs 10.3 → s 12.9 → m 16.2 → l 20.3 → xl 25.5px (was a flat 12.9px across all rungs). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GnwqYHgHNAWPbsUJWUeY1w --- configurator/src/lib/preview/sections.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/configurator/src/lib/preview/sections.ts b/configurator/src/lib/preview/sections.ts index b3303278..26791c0d 100644 --- a/configurator/src/lib/preview/sections.ts +++ b/configurator/src/lib/preview/sections.ts @@ -331,8 +331,17 @@ const BTN_SIZES: [string, string][] = [["xs", "XS"], ["s", "S"], ["", "M"], ["l" export function components(): string { const btn = (mods: string, label: string) => ``; + // Backdrop for the button rows. NOT a .sf-card: the framework's + // `.sf-card .sf-btn { --sf-btn-font-size--size: var(--sf-card-btn-font-size, + // var(--sf-text-s)) }` rule pins every nested button's label to text-s, which + // would flatten the per-rung font ladder in the Size-scale demo (padding and + // min-height still grow, but the label wouldn't). A plain framed surface lets + // each rung render its true font-size. (Cards below intentionally keep .sf-card.) + const frame = (content: string) => + `
${content}
`; + // Families — every shipped colour family as a fill button. - const families = well(cluster( + const families = frame(cluster( ...BTN_FAMILIES.map((f) => btn(`sf-btn--${f}`, f[0].toUpperCase() + f.slice(1))), )); @@ -343,11 +352,11 @@ export function components(): string { btn(`sf-btn--${family} sf-btn--outline`, "Outline"), ...(withGradient ? [btn(`sf-btn--${family} sf-btn--gradient`, "Gradient")] : []), ); - const styles = well(stack("s", styleRow("primary", true), styleRow("neutral", false))); + const styles = frame(stack("s", styleRow("primary", true), styleRow("neutral", false))); // Size scale — the hero. Aligned to a common baseline so the per-rung // font-size / padding / min-height ladder reads as a clear staircase. - const sizes = well( + const sizes = frame( `
${BTN_SIZES.map( ([s, label]) => btn(`sf-btn--primary${s ? ` sf-btn--${s}` : ""}`, label), ).join("")}
`, @@ -358,7 +367,7 @@ export function components(): string { // (dashed) where it actually fills its width. // align-items:flex-start so buttons size to content — only --block (explicit // 100%) and the block-cq inside the 16rem query container actually stretch. - const widths = well(`
+ const widths = frame(`
@@ -368,7 +377,7 @@ export function components(): string {
`); // States — default, disabled, loading. - const states = well(cluster( + const states = frame(cluster( btn("sf-btn--primary", "Default"), ``, ``, From 9c95ed02ee4069a6bc368455ed0f3194d4c3d2d5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 15:47:15 +0000 Subject: [PATCH 2/3] chore(demos): rebuild full-api demos for v0.7.15 version stamp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Verify all generated artifacts" gate flagged demos/full-api-demo*.html and demos/ultimate-override.css as stale: the v0.7.15 release bump updated package.json but did not regenerate the committed demos, whose version banner still read v0.7.14. Regenerated via `node demos/generate.mjs` — the only change is the v0.7.14 → v0.7.15 stamp. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GnwqYHgHNAWPbsUJWUeY1w --- demos/full-api-demo-with-overrides.html | 2 +- demos/full-api-demo.html | 2 +- demos/ultimate-override.css | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/demos/full-api-demo-with-overrides.html b/demos/full-api-demo-with-overrides.html index 74d1bb83..91840f70 100644 --- a/demos/full-api-demo-with-overrides.html +++ b/demos/full-api-demo-with-overrides.html @@ -122,7 +122,7 @@

SLASHED Full API Demo

-

v0.7.14 · full bundle from jsDelivr CDN · 328 classes · 756 tokens (265 configurable)

+

v0.7.15 · full bundle from jsDelivr CDN · 328 classes · 756 tokens (265 configurable)

ultimate-override.css is ACTIVE. Every value below is recomputed from perturbed knob tokens — toggle it off in the toolbar, or compare against the un-overridden page.
diff --git a/demos/full-api-demo.html b/demos/full-api-demo.html index ff2e4e88..cb2fbb77 100644 --- a/demos/full-api-demo.html +++ b/demos/full-api-demo.html @@ -121,7 +121,7 @@

SLASHED Full API Demo

-

v0.7.14 · full bundle from jsDelivr CDN · 328 classes · 756 tokens (265 configurable)

+

v0.7.15 · full bundle from jsDelivr CDN · 328 classes · 756 tokens (265 configurable)

Baseline render with default tokens. Use the toolbar to switch theme, toggle the ultimate override live, or replay motion. The always-on override variant is full-api-demo-with-overrides.html.
diff --git a/demos/ultimate-override.css b/demos/ultimate-override.css index 19f831b7..b2cc2d2d 100644 --- a/demos/ultimate-override.css +++ b/demos/ultimate-override.css @@ -1,6 +1,6 @@ /* ============================================================================ ULTIMATE OVERRIDE — generated by demos/generate.mjs (do not edit by hand) - SLASHED v0.7.14 + SLASHED v0.7.15 Perturbs every CONFIGURABLE (role: "knob") token to a valid, visibly-distinct value so full-api-demo.html can prove each one is wired end to end. From f61b96a715f0161907cf549f4c1e855910282202 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 15:53:17 +0000 Subject: [PATCH 3/3] refactor(configurator): move frame() helper to specimen.ts beside well() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address CodeRabbit nitpick on PR #601: frame() solved the same category of problem as the exported well() (a bordered-surface wrapper) but was local to components(). Extract it to specimen.ts as a shared, exported helper so the two wrapper helpers stay colocated and reusable. Behaviour-preserving — identical markup and styling. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01GnwqYHgHNAWPbsUJWUeY1w --- configurator/src/lib/preview/sections.ts | 12 ++++-------- configurator/src/lib/preview/specimen.ts | 8 ++++++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/configurator/src/lib/preview/sections.ts b/configurator/src/lib/preview/sections.ts index 26791c0d..273aa5bd 100644 --- a/configurator/src/lib/preview/sections.ts +++ b/configurator/src/lib/preview/sections.ts @@ -3,7 +3,7 @@ // configurable token/class is visible and reacts live to the configurator. // Foundations enumerate the real API via ./catalog; components are curated. -import { page, section, specimen, tag, cluster, grid, stack, well, esc } from "./specimen"; +import { page, section, specimen, tag, cluster, grid, stack, well, frame, esc } from "./specimen"; import { familySteps, classesOfKind, tokensMatching, tokensByGroup, classesByKind } from "./catalog"; const RAMP_STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]; @@ -331,14 +331,10 @@ const BTN_SIZES: [string, string][] = [["xs", "XS"], ["s", "S"], ["", "M"], ["l" export function components(): string { const btn = (mods: string, label: string) => ``; - // Backdrop for the button rows. NOT a .sf-card: the framework's - // `.sf-card .sf-btn { --sf-btn-font-size--size: var(--sf-card-btn-font-size, - // var(--sf-text-s)) }` rule pins every nested button's label to text-s, which + // Button rows use frame() (a non-card backdrop), NOT well(): the framework's + // `.sf-card .sf-btn` rule pins a nested button's label to --sf-text-s, which // would flatten the per-rung font ladder in the Size-scale demo (padding and - // min-height still grow, but the label wouldn't). A plain framed surface lets - // each rung render its true font-size. (Cards below intentionally keep .sf-card.) - const frame = (content: string) => - `
${content}
`; + // min-height still grow, but the label wouldn't). Cards below keep .sf-card. // Families — every shipped colour family as a fill button. const families = frame(cluster( diff --git a/configurator/src/lib/preview/specimen.ts b/configurator/src/lib/preview/specimen.ts index e3989a0d..a7aedf8d 100644 --- a/configurator/src/lib/preview/specimen.ts +++ b/configurator/src/lib/preview/specimen.ts @@ -63,3 +63,11 @@ export function grid(min: number, ...items: string[]): string { export function well(content: string): string { return `
${content}
`; } + +/** A plain bordered surface backdrop — like `well`, but NOT a `.sf-card`. + * Use for rows of `.sf-btn`: the framework's `.sf-card .sf-btn` rule pins a + * nested button's label to `--sf-text-s`, which would flatten a per-size + * button font ladder; a non-card frame lets each button render its true size. */ +export function frame(content: string): string { + return `
${content}
`; +}