From 57dbcecf38fbca6ae8564bcbb4bbe418e9f088ee Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Jul 2026 09:42:02 +0000 Subject: [PATCH] feat(configurator): add light mode toggle to Studio shell chrome First of a 3-part light-mode rollout (split for reviewability). Adds the theme system and applies it to the app shell: header (with the new Sun/Moon toggle), sidebar nav, status bar, preview panel toolbar, command palette, and domain panel tab bar. Dark remains the default appearance; panels/inputs get their light-mode pass in the follow-up PRs in this stack. - New `lib/theme.svelte.ts`: reactive theme state, defaults to (and live-follows) `prefers-color-scheme` until the user explicitly toggles, then persists to localStorage. - `main.ts` binds the theme to the mount root before Svelte mounts, so the first paint is never wrong-themed. - Tailwind v4 class-based `dark:` variant (`@custom-variant dark`) in `app.css`, kept separate from the framework's own `[data-theme]` (which only governs the dogfooded `--sf-*` tokens). - Includes a couple of small hardening/accessibility fixes surfaced by review bots on the original combined PR: aria-label/aria-pressed on the toggle, light-mode backgrounds for the save-state error/saved chips, a feature-detect fallback for the deprecated MediaQueryList.addListener API, and a missing dark: pairing on a command-palette badge. --- configurator/src/App.svelte | 14 ++-- configurator/src/app.css | 5 ++ .../src/components/CommandPalette.svelte | 32 ++++---- .../src/components/DomainPanel.svelte | 12 +-- .../src/components/shell/PreviewPanel.svelte | 40 +++++----- .../src/components/shell/SidebarNav.svelte | 8 +- .../src/components/shell/StatusBar.svelte | 8 +- .../src/components/shell/StudioHeader.svelte | 55 +++++++++----- configurator/src/lib/theme.svelte.ts | 74 +++++++++++++++++++ configurator/src/main.ts | 4 + 10 files changed, 176 insertions(+), 76 deletions(-) create mode 100644 configurator/src/lib/theme.svelte.ts diff --git a/configurator/src/App.svelte b/configurator/src/App.svelte index 9a256f18..b7495586 100644 --- a/configurator/src/App.svelte +++ b/configurator/src/App.svelte @@ -261,7 +261,7 @@ onclick={() => { mobileView = view; }} aria-pressed={mobileView === view} class={`flex-1 flex items-center justify-center gap-1.5 py-2.5 text-[11px] font-bold transition-colors cursor-pointer ${ - mobileView === view ? "text-indigo-300 bg-indigo-500/10" : "text-slate-500 hover:text-slate-300" + mobileView === view ? "text-indigo-700 dark:text-indigo-300 bg-indigo-500/10" : "text-slate-500 hover:text-slate-700 dark:hover:text-slate-300" }`} > {label} @@ -271,7 +271,7 @@ {/snippet} -
+
-
+
{@render foldToggleButton("controls", SlidersHorizontal, "Controls")} {@render foldToggleButton("preview", Eye, "Preview")}
@@ -309,10 +309,10 @@ -
+
-
- +
+ {DOMAIN_LABELS[domain] ?? domain} {#if domainOverridesCount > 0} @@ -320,7 +320,7 @@ onclick={handleResetDomain} data-testid="reset-category" title={`Reset ${domainOverridesCount} override${domainOverridesCount !== 1 ? "s" : ""} in ${DOMAIN_LABELS[domain] ?? domain}`} - class="flex items-center gap-1 px-1.5 py-0.5 rounded-md text-[9px] font-bold text-slate-500 hover:text-rose-400 hover:bg-rose-500/10 transition-colors cursor-pointer shrink-0" + class="flex items-center gap-1 px-1.5 py-0.5 rounded-md text-[9px] font-bold text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 hover:bg-rose-500/10 transition-colors cursor-pointer shrink-0" > Reset {domainOverridesCount} diff --git a/configurator/src/app.css b/configurator/src/app.css index f1d8c73c..75b41109 100644 --- a/configurator/src/app.css +++ b/configurator/src/app.css @@ -1 +1,6 @@ @import "tailwindcss"; + +/* Studio chrome theme — toggled by lib/theme.svelte.ts via a `.dark` class + on the mount root, independent of the framework's own [data-theme] + (which only governs the dogfooded --sf-* tokens loaded below). */ +@custom-variant dark (&:where(.dark, .dark *)); diff --git a/configurator/src/components/CommandPalette.svelte b/configurator/src/components/CommandPalette.svelte index 60aa4caa..53165285 100644 --- a/configurator/src/components/CommandPalette.svelte +++ b/configurator/src/components/CommandPalette.svelte @@ -76,9 +76,9 @@ onmousedown={(e) => { if (e.target === e.currentTarget) onClose(); }} > -
+
-
+
@@ -87,42 +87,42 @@ bind:value={query} onkeydown={handleKeydown} placeholder="Search tokens… (e.g. radius, primary, duration)" - class="flex-1 bg-transparent text-[13px] text-slate-100 placeholder-slate-600 outline-none" + class="flex-1 bg-transparent text-[13px] text-slate-900 dark:text-slate-100 placeholder-slate-400 dark:placeholder-slate-600 outline-none" /> - Esc + Esc
{#if query.trim() && results.length === 0} -
No tokens matching "{query}"
+
No tokens matching "{query}"
{:else if !query.trim()} -
Type to search tokens by name or description
+
Type to search tokens by name or description
{:else} {#each results as r, i (r.token.name)}
-
+
-
+
-
+
-
+
{#each (["fluid", "desktop", "tablet", "mobile"] as const) as w (w)}
-
+
@@ -665,7 +665,7 @@ ${BODIES[template]}