From 894cca7fac80b4e579e96f60ef74d231fe7c8bbf Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 30 Jun 2026 23:20:00 +0000 Subject: [PATCH 1/4] fix(configurator): stop full-viewport sizing from clipping inside WP admin w-screen/h-screen are correct for the standalone hosted configurator, which owns the whole page. Embedded in WP admin, the app mounts inside the host's normal document flow, so the viewport-sized root overflowed past the right edge of the content area while the host's own layout chrome left a visible gap above and to the left of it. Switch to w-full/h-full when running embedded, matching the existing isEmbedded() boundary already used for persistence. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SQXr34nocCi1jrGcp5TPVm --- configurator/src/App.svelte | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/configurator/src/App.svelte b/configurator/src/App.svelte index e855442f..4f82c1dc 100644 --- a/configurator/src/App.svelte +++ b/configurator/src/App.svelte @@ -8,7 +8,7 @@ import PreviewPanel from './components/shell/PreviewPanel.svelte'; import DomainPanel from './components/DomainPanel.svelte'; import { fa } from './lib/codec'; - import { loadInitialOverrides, injectLivePreview, saveOverrides } from './lib/persistence'; + import { loadInitialOverrides, injectLivePreview, saveOverrides, isEmbedded } from './lib/persistence'; import { domainOf } from './lib/domains'; import tokensRaw from './data/api-index.generated.json'; import CommandPalette from './components/CommandPalette.svelte'; @@ -31,6 +31,13 @@ return map; } + // Embedded hosts (e.g. the WP admin page) mount us into a sized container in + // normal document flow, not the document body — w-screen/h-screen would then + // size to the viewport while still being offset by the host's own layout + // chrome, overflowing past its right edge. Standalone keeps viewport units + // since it owns the whole page. + const embedded = isEmbedded(); + // Core state let overrides = $state>(loadInitialOverrides()); let past = $state[]>([]); @@ -232,7 +239,7 @@ }); -
+
Date: Tue, 30 Jun 2026 23:25:35 +0000 Subject: [PATCH 2/4] fix(configurator): use host-presence check for embedded layout sizing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isEmbedded() specifically checks REST persistence availability (window.slashedApp.rest.url), but a host can mount window.slashedApp without configuring rest (it's optional in the type). Layout sizing needs the broader boundary loadInitialOverrides() already uses — any host-mounted boot — so add hasWpBoot() and use that for the w-full/h-full vs w-screen/h-screen decision instead. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SQXr34nocCi1jrGcp5TPVm --- configurator/src/App.svelte | 8 +++++--- configurator/src/lib/persistence.ts | 10 ++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/configurator/src/App.svelte b/configurator/src/App.svelte index 4f82c1dc..12738160 100644 --- a/configurator/src/App.svelte +++ b/configurator/src/App.svelte @@ -8,7 +8,7 @@ import PreviewPanel from './components/shell/PreviewPanel.svelte'; import DomainPanel from './components/DomainPanel.svelte'; import { fa } from './lib/codec'; - import { loadInitialOverrides, injectLivePreview, saveOverrides, isEmbedded } from './lib/persistence'; + import { loadInitialOverrides, injectLivePreview, saveOverrides, hasWpBoot } from './lib/persistence'; import { domainOf } from './lib/domains'; import tokensRaw from './data/api-index.generated.json'; import CommandPalette from './components/CommandPalette.svelte'; @@ -35,8 +35,10 @@ // normal document flow, not the document body — w-screen/h-screen would then // size to the viewport while still being offset by the host's own layout // chrome, overflowing past its right edge. Standalone keeps viewport units - // since it owns the whole page. - const embedded = isEmbedded(); + // since it owns the whole page. Uses hasWpBoot() (any host), not + // isEmbedded() (REST persistence specifically) — a host can mount us + // without configuring REST. + const embedded = hasWpBoot(); // Core state let overrides = $state>(loadInitialOverrides()); diff --git a/configurator/src/lib/persistence.ts b/configurator/src/lib/persistence.ts index 29449e7f..ea5e2c16 100644 --- a/configurator/src/lib/persistence.ts +++ b/configurator/src/lib/persistence.ts @@ -184,6 +184,16 @@ export function isEmbedded(): boolean { return Boolean(wpBoot()?.rest?.url); } +/** + * Whether a host (e.g. the WP admin page) mounted us into its own container, + * regardless of whether REST persistence is configured. This is the same + * boundary loadInitialOverrides() uses, and the one layout sizing needs — + * a host can supply window.slashedApp without `rest` per its typing. + */ +export function hasWpBoot(): boolean { + return Boolean(wpBoot()); +} + export function loadInitialOverrides(): Record { if (typeof window === "undefined") return {}; From dbdf898f0fec4677da43b43231bc03b7abdc0bc2 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 01:01:17 +0000 Subject: [PATCH 3/4] fix(configurator): stop faking a 50-950 ramp for status colors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Status families (success/warning/info/danger) have no numeric palette scale in the framework — core/tokens.css only derives -subtle, -muted, and -strong variants for them, unlike brand families which get a real 11-step -50..-950 ramp. The Status colors section was unconditionally rendering an 11-swatch strip using the brand mix-curve formula anyway, implying tokens like --sf-color-success-500 that don't exist. Replace it with a preview of the four values that actually exist (resolved color, subtle, muted, strong), resolved from the real CSS custom properties via the existing themed-probe resolver instead of re-implementing the formulas in JS. Also add brief in-panel notes clarifying two behaviors that are correct-but-surprising, not bugs: brand tints/shades intentionally mix toward Base/Text so editing those shifts every family's ramp, and Base's ramp uses the same lightness steps in light and dark mode so it looks similar until you add chroma. And upgrade palette swatches' hover state from a slow native title tooltip to an instant floating label with the token name. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SQXr34nocCi1jrGcp5TPVm --- .../src/components/panels/ColorsPanel.svelte | 74 +++++++++++++------ 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/configurator/src/components/panels/ColorsPanel.svelte b/configurator/src/components/panels/ColorsPanel.svelte index 5031d616..392c66e4 100644 --- a/configurator/src/components/panels/ColorsPanel.svelte +++ b/configurator/src/components/panels/ColorsPanel.svelte @@ -174,6 +174,17 @@ // through its own ramp (see paletteSwatch); the rest use the mix curve. const PALETTE_COLOR_KEYS = [...BRAND_COLOR_KEYS, "base"]; + // Status families (success/warning/info/danger) have NO numeric 50-950 + // ramp in the framework — only these four derived values actually exist + // (core/tokens.css: resolved color + -subtle/-muted alpha washes + a + // light-dark() -strong shift). Mirror that here instead of faking a ramp. + const STATUS_VARIANTS: { label: string; expr: (key: string) => string }[] = [ + { label: "Base", expr: (k) => `var(--sf-color-${k})` }, + { label: "Subtle", expr: (k) => `var(--sf-color-${k}-subtle)` }, + { label: "Muted", expr: (k) => `var(--sf-color-${k}-muted)` }, + { label: "Strong", expr: (k) => `var(--sf-color-${k}-strong)` }, + ]; + const ALL_SOURCES: ColorSource[] = [...BRAND_SOURCES, ...STATUS_SOURCES]; // Live semantic-color preview shown at the very top of the panel. Each tile @@ -439,6 +450,15 @@ } +{#snippet swatchTip(name: string)} + + {name} +{/snippet} +
@@ -489,7 +509,9 @@ {#if showBrandSources}

- OKLCH source values — all 200+ derived color steps are computed automatically. + OKLCH source values — all 200+ derived color steps are computed automatically. Tints (50–400) mix toward Base + (the "Surface" color) and shades (600–950) mix toward Text (driven by Neutral) — so editing Base or Neutral below + will shift every family's tints/shades too. That's expected, not a bug.

{#each BRAND_PAIRS as [light, dark] (light.name)} @@ -534,10 +556,10 @@ {#each SWATCH_STEPS as step (step)} {@const resolved = paletteSwatch(light.colorKey, lightSrcVal, step, lSurface, lText)}
+ >{@render swatchTip(`${light.colorKey}-${step}`)}
{/each}
@@ -547,10 +569,10 @@ {#each SWATCH_STEPS as step (step)} {@const resolved = paletteSwatch(light.colorKey, darkSrcVal, step, dSurface, dText)}
+ >{@render swatchTip(`${light.colorKey}-${step}`)}
{/each} @@ -558,6 +580,8 @@ {#if light.colorKey === "base"}

Absolute lightness ramp — each step pins L and inherits chroma + hue from the source. Not the brand mix curve. + Light and dark use the same L per step, so at the default near-zero chroma the two rows look almost + identical — add chroma above to see them diverge by hue.

{/if} {/if} @@ -708,29 +732,37 @@ Dark: auto-derived ({derivedDark}) {/if} - +
- {#each [ - ["L", "light", sourceValue(light), getLightSurface(), getLightText()], - ["D", "dark", isAutoMode - ? deriveDarkFromLight(sourceValue(light), light.colorKey) - : (dark ? (sourceValue(dark)) : (sourceValue(light))), getDarkSurface(), getDarkText()] - ] as [tag, side, srcVal, sfc, txt] (tag)} +
+ +
+ {#each STATUS_VARIANTS as v (v.label)} + {v.label} + {/each} +
+
+ {#each [["L", "light"], ["D", "dark"]] as [tag, side] (tag)}
{tag}
- {#each SWATCH_STEPS as step (step)} - {@const resolved = computePaletteSwatch(srcVal as string, step, sfc as string, txt as string)} + {#each STATUS_VARIANTS as v (v.label)} + {@const resolved = paintTheme(v.expr(light.colorKey), side as "light" | "dark", "")}
+ title={`${light.colorKey}-${v.label.toLowerCase()} (${side}) — ${resolved}`} + >{@render swatchTip(`${light.colorKey}-${v.label.toLowerCase()}`)}
{/each}
{/each} +

+ Status colors don't ride the brand mix curve — only the resolved color plus subtle/muted alpha washes and + a strong shade are derived. There's no -50…-950 ramp for success/warning/info/danger. +

{/each} @@ -899,10 +931,10 @@
{Math.round(val)}
+ >{@render swatchTip(`primary-${step}`)}
{step} {/each} @@ -938,10 +970,10 @@ {#each SWATCH_STEPS as step (step)} {@const swatch = computePaletteSwatch(miniSrc, step, _miniSurface, _miniText)}
+ >{@render swatchTip(`${colorKey}-${step}`)} {/each} From 5aae9503293182721fb27b37afdf2ea3fea22403 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 01:05:50 +0000 Subject: [PATCH 4/4] fix(configurator): hide swatch hover label from assistive tech MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit opacity-0/group-hover:opacity-100 only visually hides the floating tooltip label — it stayed in the accessibility tree, so screen readers would announce duplicate text for every palette swatch. Mark it aria-hidden since it's a purely decorative visual affordance (the title attribute already carries the accessible name). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01SQXr34nocCi1jrGcp5TPVm --- configurator/src/components/panels/ColorsPanel.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/configurator/src/components/panels/ColorsPanel.svelte b/configurator/src/components/panels/ColorsPanel.svelte index 392c66e4..83073a13 100644 --- a/configurator/src/components/panels/ColorsPanel.svelte +++ b/configurator/src/components/panels/ColorsPanel.svelte @@ -455,6 +455,7 @@ appear and easy to miss on these small swatches, so pair it with an instant floating label. --> {/snippet}