From c3e57a44c6369b76bfb23de9fce015984eb35ab5 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Jul 2026 09:59:09 +0000 Subject: [PATCH 1/2] feat(configurator): surface the .sf-surface-bg macro tokens in controls The --sf-surface-bg-* input set (color/image/overlay/size/position/ repeat/attachment/animation) landed in core but was never wired into the configurator's control panels. It routed inconsistently too: --sf-surface-bg-color fell into the colors domain (matched 'color') while its siblings fell into layout (matched '-bg-'), so the group was split across two panels and had no dedicated knobs anywhere. - Route the whole surface-bg group to the macros domain (add 'surface-bg' to domain-patterns), so it groups with the sibling .sf-surface macro tokens and appears in the Macros 'All tokens' tab. - Add a 'Background surface' control section to MacrosPanel: text inputs for the free-form tokens (color/image/overlay/animation) and preset button grids for the enumerable ones (size/position/repeat/ attachment), plus a live .sf-surface-bg preview. All-tokens and cheatsheet already picked these up via the api sync. --- .../src/components/panels/MacrosPanel.svelte | 98 +++++++++++++++++++ configurator/src/data/domain-patterns.json | 2 +- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/configurator/src/components/panels/MacrosPanel.svelte b/configurator/src/components/panels/MacrosPanel.svelte index bcb14431..90cc4c72 100644 --- a/configurator/src/components/panels/MacrosPanel.svelte +++ b/configurator/src/components/panels/MacrosPanel.svelte @@ -33,6 +33,30 @@ { label: "↖ top-left", value: "to top left" }, ]; + // Enumerable presets for the .sf-surface-bg named-background macro. The first + // value in each list is the token default, so clicking it resets the override. + const SURFACE_BG_SIZE = ["cover", "contain", "auto", "100% 100%"]; + const SURFACE_BG_POSITION = ["center", "top", "bottom", "left", "right", "top left"]; + const SURFACE_BG_REPEAT = ["no-repeat", "repeat", "repeat-x", "repeat-y", "space", "round"]; + const SURFACE_BG_ATTACHMENT = ["scroll", "fixed", "local"]; + + // Free-form surface-bg tokens (color/image/overlay/animation) — plain text + // inputs, since their values are arbitrary CSS (urls, gradients, keyframes). + const SURFACE_BG_TEXT = [ + { label: "Color", token: "--sf-surface-bg-color", placeholder: "transparent" }, + { label: "Image", token: "--sf-surface-bg-image", placeholder: 'url("/hero.avif")' }, + { label: "Overlay", token: "--sf-surface-bg-overlay", placeholder: "var(--sf-scrim-gradient)" }, + { label: "Animation", token: "--sf-surface-bg-animation", placeholder: "sf-pan 40s linear infinite" }, + ]; + + // Enumerable surface-bg tokens rendered as preset button grids. + const SURFACE_BG_ENUM = [ + { label: "Size", token: "--sf-surface-bg-size", opts: SURFACE_BG_SIZE }, + { label: "Position", token: "--sf-surface-bg-position", opts: SURFACE_BG_POSITION }, + { label: "Repeat", token: "--sf-surface-bg-repeat", opts: SURFACE_BG_REPEAT }, + { label: "Attachment", token: "--sf-surface-bg-attachment", opts: SURFACE_BG_ATTACHMENT }, + ]; + // Prose spacing knobs. Defaults reference space tokens — the numbers below are // the approximate resolved rem values, used only as the slider's idle position. const PROSE_SPACE = [ @@ -52,6 +76,7 @@ let showScrim = $state(false); let showProse = $state(false); let showContentIntrinsic = $state(false); + let showSurfaceBg = $state(false); let lineClamp = $derived(num("--sf-line-clamp", 3)); let flowSpace = $derived(num("--sf-flow-space", 0.5, "rem")); @@ -61,6 +86,15 @@ let scrimDir = $derived(overrides["--sf-scrim-direction"] ?? "to top"); let mediaRadius = $derived(num("--sf-prose-media-radius", 6, "px")); + // Live preview composition for .sf-surface-bg — mirrors the macro's layering + // (overlay above image) so the swatch reflects what ships. + let surfaceBgImage = $derived(overrides["--sf-surface-bg-image"] ?? "none"); + let surfaceBgOverlay = $derived(overrides["--sf-surface-bg-overlay"] ?? "none"); + let surfaceBgColor = $derived(overrides["--sf-surface-bg-color"] ?? "transparent"); + let surfaceBgSize = $derived(overrides["--sf-surface-bg-size"] ?? "cover"); + let surfaceBgPosition = $derived(overrides["--sf-surface-bg-position"] ?? "center"); + let surfaceBgRepeat = $derived(overrides["--sf-surface-bg-repeat"] ?? "no-repeat"); + function proseVal(t: typeof PROSE_SPACE[0]): number { const raw = overrides[t.token]; if (!raw) return t.def; @@ -367,6 +401,70 @@ {/if} +
+ + +
+ + {#if showSurfaceBg} +

+ --sf-surface-bg-* — input set composed by the .sf-surface-bg + macro into a single reusable background (color, image, overlay, sizing and animation). +

+ + {#each SURFACE_BG_TEXT as t (t.token)} +
+
{t.label}
+ { + const v = (e.target as HTMLInputElement).value.trim(); + v ? onSet(t.token, v) : onReset(t.token); + }} + class="flex-1 min-w-0 bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded px-1.5 py-1 text-[9px] font-mono text-slate-700 dark:text-slate-300 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500" + /> + {#if t.token in overrides} + + {/if} +
+ {/each} + + {#each SURFACE_BG_ENUM as g (g.token)} +
+
{g.label}
+
+ {#each g.opts as opt (opt)} + + {/each} +
+
+ {/each} + +
+ .sf-surface-bg preview +
+ {/if} +
+

All tokens tab — edit every prose, scrim and surface token directly. diff --git a/configurator/src/data/domain-patterns.json b/configurator/src/data/domain-patterns.json index cd9452ee..39c57052 100644 --- a/configurator/src/data/domain-patterns.json +++ b/configurator/src/data/domain-patterns.json @@ -1,5 +1,5 @@ { - "macros": ["prose", "flow-space", "line-clamp", "aspect", "scroll-shadow", "scrim", "surface-color", "content-intrinsic", "corner-scoop", "overlap-"], + "macros": ["prose", "flow-space", "line-clamp", "aspect", "scroll-shadow", "scrim", "surface-color", "surface-bg", "content-intrinsic", "corner-scoop", "overlap-"], "colors": ["color", "contrast", "palette-mix", "lumlocker", "gradient"], "typography": ["font", "--sf-text", "leading", "tracking", "weight", "body-font", "heading-font", "body-em", "body-text", "heading-text", "display-l-", "display-m-", From 93d280a88f2839136d2aeebc0b0fb06fa0f713f8 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Jul 2026 10:23:18 +0000 Subject: [PATCH 2/2] fix(configurator): address review on surface-bg controls - Persist the raw input value for the surface-bg free-form fields and only use the trimmed value to decide set-vs-reset, so space-separated CSS shorthands (e.g. animation) can be composed without trailing spaces being stripped on every keystroke. Matches the accepted EffectsPanel pattern. - Include background-attachment and animation in the .sf-surface-bg preview swatch so it composes the same properties the macro does. --- configurator/src/components/panels/MacrosPanel.svelte | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/configurator/src/components/panels/MacrosPanel.svelte b/configurator/src/components/panels/MacrosPanel.svelte index 90cc4c72..18d3f641 100644 --- a/configurator/src/components/panels/MacrosPanel.svelte +++ b/configurator/src/components/panels/MacrosPanel.svelte @@ -93,7 +93,9 @@ let surfaceBgColor = $derived(overrides["--sf-surface-bg-color"] ?? "transparent"); let surfaceBgSize = $derived(overrides["--sf-surface-bg-size"] ?? "cover"); let surfaceBgPosition = $derived(overrides["--sf-surface-bg-position"] ?? "center"); - let surfaceBgRepeat = $derived(overrides["--sf-surface-bg-repeat"] ?? "no-repeat"); + let surfaceBgRepeat = $derived(overrides["--sf-surface-bg-repeat"] ?? "no-repeat"); + let surfaceBgAttachment = $derived(overrides["--sf-surface-bg-attachment"] ?? "scroll"); + let surfaceBgAnimation = $derived(overrides["--sf-surface-bg-animation"] ?? "none"); function proseVal(t: typeof PROSE_SPACE[0]): number { const raw = overrides[t.token]; @@ -427,8 +429,8 @@ value={overrides[t.token] ?? ""} placeholder={t.placeholder} oninput={(e) => { - const v = (e.target as HTMLInputElement).value.trim(); - v ? onSet(t.token, v) : onReset(t.token); + const v = (e.target as HTMLInputElement).value; + v.trim() ? onSet(t.token, v) : onReset(t.token); }} class="flex-1 min-w-0 bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded px-1.5 py-1 text-[9px] font-mono text-slate-700 dark:text-slate-300 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500" /> @@ -458,7 +460,7 @@

.sf-surface-bg preview