From 6fcc245a806f8416e6a75d2b2af7d7a2c0c6555b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Jul 2026 05:38:22 +0000 Subject: [PATCH] fix(configurator): lumlocker locked preview follows live sources, not stock defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Locked column re-derived its swatches from `overrides[src] ?? hardcoded default`, so any brand in Auto dark mode (no dark override — the default) previewed its lock from the stock blue sources: an orange brand showed blue "locked" swatches, which is impossible (the lock only pins L, never hue). Build the locked expression from the live tokens instead, mirroring the framework's core/themes.css formula exactly — including the `var(--source-dark, var(--source-light))` fallback — and resolve it per-theme through the preview iframe like the Current column. Overrides, auto-derived dark sources and the light fallback all flow through. Also NaN-guards the --sf-lumlocker read. Verified: with primary light set to oklch(0.62 0.19 45) and dark on auto, the locked D swatch resolves to oklch(0.65 0.171 45) — same hue, pinned L — where it previously showed the default blue. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_013BRVgKfEYocPCHz9KU96FN --- .../src/components/panels/ColorsPanel.svelte | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/configurator/src/components/panels/ColorsPanel.svelte b/configurator/src/components/panels/ColorsPanel.svelte index 6f3ac986..45f9f621 100644 --- a/configurator/src/components/panels/ColorsPanel.svelte +++ b/configurator/src/components/panels/ColorsPanel.svelte @@ -234,7 +234,10 @@ // Per-gradient structured editors, seeded lazily from the defaults above. let gradientEdits = $state>({}); - let lumlockerL = $derived(parseFloat(overrides["--sf-lumlocker"] ?? "0.65")); + let lumlockerL = $derived.by(() => { + const v = parseFloat(overrides["--sf-lumlocker"] ?? "0.65"); + return Number.isFinite(v) ? v : 0.65; + }); function parseLinearGradient(css: string): { space: string; kind: string; angle: number; dir: string; stop1: string; stop2: string } | null { const m = css.match(/^linear-gradient\(\s*in\s+(\S+)\s+(to\s+\w+|\d+deg)\s*,(.+)\)$/i); @@ -280,13 +283,16 @@ onSet(g.name, composeGradient(g)); } - // LumLocker swatch preview: mirrors the framework's oklch(from L c h). - // Works for any valid CSS color input, not just oklch() literals. + // LumLocker swatch preview — mirrors the framework's lock formula in + // core/themes.css EXACTLY, referencing the live source tokens (so overrides, + // the auto-derived dark sources and the dark→light fallback all flow through + // instead of re-deriving from panel-side defaults, which showed the stock + // blue for auto-dark brands). Resolved per-theme via the preview iframe. function lockedColor(colorKey: string, side: "light" | "dark"): string { - const srcName = `--sf-color-${colorKey}-source-${side}`; - const src = overrides[srcName] ?? BRAND_SOURCES.find(s => s.name === srcName)?.default ?? ""; - if (!src) return ""; - return `oklch(from ${src} ${lumlockerL} c h)`; + const srcVar = side === "dark" + ? `var(--sf-color-${colorKey}-source-dark, var(--sf-color-${colorKey}-source-light))` + : `var(--sf-color-${colorKey}-source-light)`; + return `oklch(from ${srcVar} ${lumlockerL} c h)`; } // Track which color rows are in Auto dark mode. @@ -667,7 +673,7 @@ {lk.label} {side === "light" ? "L" : "D"} - + {/each} {/each}