Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions configurator/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}`}
>
<Icon class="w-3.5 h-3.5" /> {label}
Expand All @@ -271,7 +271,7 @@
</button>
{/snippet}

<div class="{embedded ? 'w-full h-full' : 'w-screen h-screen'} flex flex-col overflow-hidden bg-[#0a0a0f] text-slate-200 font-sans">
<div class="{embedded ? 'w-full h-full' : 'w-screen h-screen'} flex flex-col overflow-hidden bg-slate-50 dark:bg-[#0a0a0f] text-slate-800 dark:text-slate-200 font-sans">
<!-- Top header bar -->
<StudioHeader
{overridesCount}
Expand All @@ -290,7 +290,7 @@
<!-- Mobile fold toggle: switch between the controls panel and the live
preview. Lives right under the header (not at the bottom) so it's
visible without scrolling and doesn't compete with the status bar. -->
<div class="md:hidden flex items-stretch border-b border-white/8 bg-[#0d0d14] shrink-0">
<div class="md:hidden flex items-stretch border-b border-black/8 dark:border-white/8 bg-slate-50 dark:bg-[#0d0d14] shrink-0">
{@render foldToggleButton("controls", SlidersHorizontal, "Controls")}
{@render foldToggleButton("preview", Eye, "Preview")}
</div>
Expand All @@ -309,18 +309,18 @@
<!-- Left domain panel — fills remaining row width on mobile (the icon
rail above already claims its own space, so w-full here would mean
100% of the whole row and overflow past it), fixed 360px on desktop -->
<div class={`flex-1 min-w-0 md:flex-none md:w-[360px] bg-[#0c0c15] border-r border-white/8 flex-col min-h-0 ${mobileView === "preview" ? "hidden md:flex" : "flex"}`}>
<div class={`flex-1 min-w-0 md:flex-none md:w-[360px] bg-slate-50 dark:bg-[#0c0c15] border-r border-black/8 dark:border-white/8 flex-col min-h-0 ${mobileView === "preview" ? "hidden md:flex" : "flex"}`}>
<!-- Panel heading -->
<div class="h-9 flex items-center px-4 border-b border-white/6 shrink-0 gap-2">
<span data-testid="panel-heading" class="text-[11px] font-bold text-slate-300 uppercase tracking-widest flex-1">
<div class="h-9 flex items-center px-4 border-b border-black/6 dark:border-white/6 shrink-0 gap-2">
<span data-testid="panel-heading" class="text-[11px] font-bold text-slate-700 dark:text-slate-300 uppercase tracking-widest flex-1">
{DOMAIN_LABELS[domain] ?? domain}
</span>
{#if domainOverridesCount > 0}
<button
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"
>
<RotateCcw class="w-3 h-3" />
Reset {domainOverridesCount}
Expand Down
5 changes: 5 additions & 0 deletions configurator/src/app.css
Original file line number Diff line number Diff line change
@@ -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 *));
Comment on lines +3 to +6

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. Unscoped dark variant selector 🐞 Bug ≡ Correctness

@custom-variant dark (&:where(.dark, .dark *)) enables dark: styles whenever any ancestor has
class .dark, so an embedded configurator can be forced into dark mode even when themeState is
light and the mount root isn’t marked dark. This makes the chrome theme correctness depend on
unrelated host-page CSS/class naming.
Agent Prompt
### Issue description
The Tailwind `dark:` variant is currently activated by any `.dark` ancestor (`.dark *`), which can be unintentionally true in embedded contexts (host page uses `.dark` for unrelated styling). This can force the configurator into dark mode even when its own theme state is light.

### Issue Context
The app intentionally toggles a theme marker on the mount root, but the CSS variant is not scoped to that root specifically.

### Fix Focus Areas
- configurator/src/app.css[3-6]
- configurator/src/lib/theme.svelte.ts[34-55]
- configurator/src/main.ts[19-29]

### Suggested fix
- Replace the global `.dark` selector with a scoped attribute/class unique to the configurator root, while keeping the variant name `dark` for existing `dark:` utilities.
  - Example: change the variant to key off `[data-studio-theme="dark"]` instead of `.dark`.
  - Update `applyToRoot()` to set/remove `data-studio-theme="dark"` on the bound root element (and stop toggling the generic `dark` class), so no external `.dark` ancestors can affect the chrome theme.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

32 changes: 16 additions & 16 deletions configurator/src/components/CommandPalette.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@
onmousedown={(e) => { if (e.target === e.currentTarget) onClose(); }}
>
<!-- Panel -->
<div class="w-[560px] max-w-[95vw] bg-[#111118] border border-white/12 rounded-2xl shadow-2xl overflow-hidden">
<div class="w-[560px] max-w-[95vw] bg-white dark:bg-[#111118] border border-black/12 dark:border-white/12 rounded-2xl shadow-2xl overflow-hidden">
<!-- Search input -->
<div class="flex items-center gap-3 px-4 py-3 border-b border-white/8">
<div class="flex items-center gap-3 px-4 py-3 border-b border-black/8 dark:border-white/8">
<svg class="w-4 h-4 text-slate-500 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
Expand All @@ -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"
/>
<kbd class="text-[9px] font-mono text-slate-600 border border-white/10 rounded px-1.5 py-0.5 shrink-0">Esc</kbd>
<kbd class="text-[9px] font-mono text-slate-400 dark:text-slate-600 border border-black/10 dark:border-white/10 rounded px-1.5 py-0.5 shrink-0">Esc</kbd>
</div>

<!-- Results -->
<div class="max-h-[360px] overflow-y-auto">
{#if query.trim() && results.length === 0}
<div class="px-4 py-8 text-center text-[11px] text-slate-600">No tokens matching "{query}"</div>
<div class="px-4 py-8 text-center text-[11px] text-slate-400 dark:text-slate-600">No tokens matching "{query}"</div>
{:else if !query.trim()}
<div class="px-4 py-8 text-center text-[11px] text-slate-600">Type to search tokens by name or description</div>
<div class="px-4 py-8 text-center text-[11px] text-slate-400 dark:text-slate-600">Type to search tokens by name or description</div>
{:else}
{#each results as r, i (r.token.name)}
<button
onmouseenter={() => { selectedIndex = i; }}
onclick={() => handleSelect(r.domain)}
class={`w-full flex items-center gap-3 px-4 py-2.5 text-left transition-colors cursor-pointer ${
selectedIndex === i ? "bg-indigo-500/15" : "hover:bg-white/4"
selectedIndex === i ? "bg-indigo-500/15" : "hover:bg-black/4 dark:hover:bg-white/4"
}`}
>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2">
<span class="text-[11px] font-mono text-slate-200 truncate">{r.token.name}</span>
<span class="text-[11px] font-mono text-slate-800 dark:text-slate-200 truncate">{r.token.name}</span>
{#if r.overridden}
<span class="shrink-0 text-[8px] font-bold text-indigo-400 bg-indigo-500/15 border border-indigo-500/25 rounded px-1 py-0.5">overridden</span>
<span class="shrink-0 text-[8px] font-bold text-indigo-600 dark:text-indigo-400 bg-indigo-500/15 border border-indigo-500/25 rounded px-1 py-0.5">overridden</span>
{/if}
</div>
{#if r.token.description}
<div class="text-[10px] text-slate-600 truncate mt-0.5">{r.token.description}</div>
<div class="text-[10px] text-slate-400 dark:text-slate-600 truncate mt-0.5">{r.token.description}</div>
{/if}
</div>
<div class="flex items-center gap-2 shrink-0">
<span class="text-[9px] text-slate-600 font-mono truncate max-w-[100px]">
<span class="text-[9px] text-slate-400 dark:text-slate-600 font-mono truncate max-w-[100px]">
{r.overridden ? overrides[r.token.name] : r.token.value}
</span>
<span class="text-[9px] font-bold text-slate-500 bg-white/5 rounded px-1.5 py-0.5">
<span class="text-[9px] font-bold text-slate-500 bg-black/5 dark:bg-white/5 rounded px-1.5 py-0.5">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Badge text missing a dark-mode variant.

Unlike sibling muted-text spans in this file (e.g. Lines 92, 98, 118 use text-slate-400 dark:text-slate-600), this badge keeps a single text-slate-500 for both themes, which is dimmer than intended against the dark background.

🎨 Proposed fix
-              <span class="text-[9px] font-bold text-slate-500 bg-black/5 dark:bg-white/5 rounded px-1.5 py-0.5">
+              <span class="text-[9px] font-bold text-slate-500 dark:text-slate-400 bg-black/5 dark:bg-white/5 rounded px-1.5 py-0.5">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<span class="text-[9px] font-bold text-slate-500 bg-black/5 dark:bg-white/5 rounded px-1.5 py-0.5">
<span class="text-[9px] font-bold text-slate-500 dark:text-slate-400 bg-black/5 dark:bg-white/5 rounded px-1.5 py-0.5">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@configurator/src/components/CommandPalette.svelte` at line 125, The badge
text in CommandPalette.svelte is missing a dark-mode color variant, so it stays
too dim on dark backgrounds. Update the span’s text color class to match the
nearby muted-text patterns in this component by adding a dark-mode Tailwind
variant alongside the existing light theme class, keeping the change localized
to the badge span.

{DOMAIN_LABELS[r.domain] ?? r.domain}
</span>
</div>
Expand All @@ -133,10 +133,10 @@

<!-- Footer hint -->
{#if results.length > 0}
<div class="px-4 py-2 border-t border-white/6 flex items-center gap-3 text-[9px] text-slate-600">
<span><kbd class="font-mono border border-white/10 rounded px-1">↑↓</kbd> navigate</span>
<span><kbd class="font-mono border border-white/10 rounded px-1">↵</kbd> open panel</span>
<span><kbd class="font-mono border border-white/10 rounded px-1">Esc</kbd> close</span>
<div class="px-4 py-2 border-t border-black/6 dark:border-white/6 flex items-center gap-3 text-[9px] text-slate-400 dark:text-slate-600">
<span><kbd class="font-mono border border-black/10 dark:border-white/10 rounded px-1">↑↓</kbd> navigate</span>
<span><kbd class="font-mono border border-black/10 dark:border-white/10 rounded px-1">↵</kbd> open panel</span>
<span><kbd class="font-mono border border-black/10 dark:border-white/10 rounded px-1">Esc</kbd> close</span>
</div>
{/if}
</div>
Expand Down
12 changes: 6 additions & 6 deletions configurator/src/components/DomainPanel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -109,25 +109,25 @@
</div>

<!-- Bottom tab bar -->
<div class="shrink-0 border-t border-white/8 bg-[#0a0a10] flex">
<div class="shrink-0 border-t border-black/8 dark:border-white/8 bg-slate-50 dark:bg-[#0a0a10] flex">
<button
onclick={() => { view = "controls"; }}
class={`flex-1 flex items-center justify-center gap-1.5 py-2 text-[10px] font-bold transition-all cursor-pointer ${
view === "controls"
? "text-indigo-300 bg-indigo-600/10 border-t-2 border-t-indigo-500 -mt-px"
: "text-slate-500 hover:text-slate-300"
? "text-indigo-700 dark:text-indigo-300 bg-indigo-600/10 border-t-2 border-t-indigo-500 -mt-px"
: "text-slate-500 hover:text-slate-700 dark:hover:text-slate-300"
}`}
>
<SlidersHorizontal class="w-3 h-3" />
Controls
</button>
<div class="w-px bg-white/8"></div>
<div class="w-px bg-black/8 dark:bg-white/8"></div>
<button
onclick={() => { view = "tokens"; }}
class={`flex-1 flex items-center justify-center gap-1.5 py-2 text-[10px] font-bold transition-all cursor-pointer ${
view === "tokens"
? "text-indigo-300 bg-indigo-600/10 border-t-2 border-t-indigo-500 -mt-px"
: "text-slate-500 hover:text-slate-300"
? "text-indigo-700 dark:text-indigo-300 bg-indigo-600/10 border-t-2 border-t-indigo-500 -mt-px"
: "text-slate-500 hover:text-slate-700 dark:hover:text-slate-300"
}`}
>
<List class="w-3 h-3" />
Expand Down
26 changes: 13 additions & 13 deletions configurator/src/components/inputs/ClampField.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,17 @@
);
</script>

<div class={`rounded-xl border p-3 ${overridden ? "bg-indigo-500/8 border-indigo-500/25" : "bg-white/4 border-white/8"}`}>
<div class={`rounded-xl border p-3 ${overridden ? "bg-indigo-500/8 border-indigo-500/25" : "bg-black/4 dark:bg-white/4 border-black/8 dark:border-white/8"}`}>
<div class="flex items-center justify-between mb-2">
<span class="text-[10px] font-bold text-slate-300">{title}</span>
<span class="text-[10px] font-bold text-slate-700 dark:text-slate-300">{title}</span>
<span class="text-[9px] font-mono text-slate-500">
clamp(<span class="text-indigo-300">{minValue}{unit}</span> … <span class="text-indigo-300">{maxValue}{unit}</span>)
clamp(<span class="text-indigo-700 dark:text-indigo-300">{minValue}{unit}</span> … <span class="text-indigo-700 dark:text-indigo-300">{maxValue}{unit}</span>)
</span>
</div>

<!-- Connected dual-thumb track -->
<div class="clampTrack relative h-5 flex items-center mb-2">
<div class="absolute inset-x-0 h-1 bg-white/8 rounded-full"></div>
<div class="absolute inset-x-0 h-1 bg-black/8 dark:bg-white/8 rounded-full"></div>
<div
class="absolute h-1 bg-indigo-500 rounded-full"
style={`left:${Math.min(minPct, maxPct)}%; right:${100 - Math.max(minPct, maxPct)}%`}
Expand All @@ -112,36 +112,36 @@
<span class="text-[9px] text-slate-500 shrink-0">{minLabel}</span>
<input type="number" {min} {max} {step} value={minValue}
onchange={(e) => onMinChange(clamp(parseFloat((e.target as HTMLInputElement).value) || min))}
class="w-full bg-white/5 border border-white/10 rounded text-[11px] font-mono text-slate-200 text-right px-1.5 py-0.5 focus:outline-none focus:border-indigo-500" />
class="w-full bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded text-[11px] font-mono text-slate-800 dark:text-slate-200 text-right px-1.5 py-0.5 focus:outline-none focus:border-indigo-500" />
</label>
<label class="flex items-center gap-1.5">
<span class="text-[9px] text-slate-500 shrink-0">{maxLabel}</span>
<input type="number" {min} {max} {step} value={maxValue}
onchange={(e) => onMaxChange(clamp(parseFloat((e.target as HTMLInputElement).value) || max))}
class="w-full bg-white/5 border border-white/10 rounded text-[11px] font-mono text-slate-200 text-right px-1.5 py-0.5 focus:outline-none focus:border-indigo-500" />
class="w-full bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded text-[11px] font-mono text-slate-800 dark:text-slate-200 text-right px-1.5 py-0.5 focus:outline-none focus:border-indigo-500" />
</label>
</div>

<!-- Min → Max preview -->
{#if previewKind === "type"}
<div class="flex items-baseline justify-between gap-3 mt-2 px-1 overflow-hidden">
<span class="text-slate-500 leading-none truncate" style={`font-size:${Math.min(minValue, 2)}rem`}>Aa<span class="text-[8px] font-mono align-middle ml-1">{minLabel.toLowerCase()}</span></span>
<span class="text-white/80 leading-none truncate" style={`font-size:${Math.min(maxValue, 2.6)}rem`}>Aa<span class="text-[8px] font-mono align-middle ml-1">{maxLabel.toLowerCase()}</span></span>
<span class="text-slate-900/80 dark:text-white/80 leading-none truncate" style={`font-size:${Math.min(maxValue, 2.6)}rem`}>Aa<span class="text-[8px] font-mono align-middle ml-1">{maxLabel.toLowerCase()}</span></span>
</div>
{:else if previewKind === "space"}
<div class="flex items-center gap-2 mt-2 px-1">
<div class="h-2 bg-indigo-500/40 rounded shrink-0" style={`width:${Math.min(minValue * 28, 120)}px`}></div>
<span class="text-[8px] font-mono text-slate-600">min</span>
<span class="text-[8px] font-mono text-slate-400 dark:text-slate-600">min</span>
<div class="h-2 bg-indigo-500/70 rounded shrink-0" style={`width:${Math.min(maxValue * 28, 200)}px`}></div>
<span class="text-[8px] font-mono text-slate-600">max</span>
<span class="text-[8px] font-mono text-slate-400 dark:text-slate-600">max</span>
</div>
{/if}

<!-- Modular-scale ratio (optional) — independent mobile & desktop ratios.
Each breakpoint has its own preset dropdown plus an always-visible custom
number input, so a preset can be picked and then fine-tuned per side. -->
{#if ratioPresets}
<div class="mt-3 pt-3 border-t border-white/6">
<div class="mt-3 pt-3 border-t border-black/6 dark:border-white/6">
<div class="text-[9px] font-semibold text-slate-500 mb-2">Modular scale ratio</div>
<div class="space-y-2">
{#each [
Expand All @@ -157,7 +157,7 @@
const v = parseFloat((e.target as HTMLSelectElement).value);
if (Number.isFinite(v)) row.onChange?.(clampRatio(v));
}}
class="flex-1 min-w-0 bg-white/5 border border-white/10 rounded text-[10px] text-slate-200 px-1.5 py-1 focus:outline-none focus:border-indigo-500 cursor-pointer"
class="flex-1 min-w-0 bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded text-[10px] text-slate-800 dark:text-slate-200 px-1.5 py-1 focus:outline-none focus:border-indigo-500 cursor-pointer"
>
{#if row.active === undefined}
<option value="" style="background:#16161e;">Custom</option>
Expand All @@ -170,7 +170,7 @@
aria-label={`${row.side} modular scale custom ratio`}
type="number" min={ratioMin_bound} max={ratioMax_bound} step={0.001} value={row.value}
onchange={(e) => { const n = parseFloat((e.target as HTMLInputElement).value); if (Number.isFinite(n)) row.onChange?.(clampRatio(n)); }}
class="w-16 shrink-0 bg-white/5 border border-white/10 rounded text-[11px] font-mono text-slate-200 text-right px-1.5 py-0.5 focus:outline-none focus:border-indigo-500"
class="w-16 shrink-0 bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded text-[11px] font-mono text-slate-800 dark:text-slate-200 text-right px-1.5 py-0.5 focus:outline-none focus:border-indigo-500"
/>
</div>
{/each}
Expand All @@ -180,7 +180,7 @@

{#if overridden && onReset}
<div class="flex justify-end mt-2">
<button onclick={onReset} class="text-[9px] text-slate-500 hover:text-rose-400 cursor-pointer">reset</button>
<button onclick={onReset} class="text-[9px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer">reset</button>
</div>
{/if}
</div>
Expand Down
10 changes: 5 additions & 5 deletions configurator/src/components/inputs/ColorInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@

<div class="flex items-center gap-2">
<!-- Swatch / native picker trigger -->
<div class="relative shrink-0 w-7 h-7 rounded border border-white/10 overflow-hidden cursor-pointer">
<div class="relative shrink-0 w-7 h-7 rounded border border-black/10 dark:border-white/10 overflow-hidden cursor-pointer">
<div class="absolute inset-0" style:background={swatchColor}></div>
{#if !isVar}
<input
Expand Down Expand Up @@ -91,22 +91,22 @@
if (e.key === "Escape") { cancelBlur = true; editing = false; }
}}
placeholder={placeholder ?? "default"}
class="flex-1 bg-white/8 border border-indigo-500/50 rounded px-1.5 py-0.5 text-[10px] font-mono text-slate-200 focus:outline-none"
class="flex-1 bg-black/8 dark:bg-white/8 border border-indigo-500/50 rounded px-1.5 py-0.5 text-[10px] font-mono text-slate-800 dark:text-slate-200 focus:outline-none"
/>
{:else}
<button
onclick={() => { editing = true; }}
class="flex-1 text-left text-[9px] font-mono text-slate-500 hover:text-slate-200 truncate cursor-pointer transition-colors"
class="flex-1 text-left text-[9px] font-mono text-slate-500 hover:text-slate-800 dark:hover:text-slate-200 truncate cursor-pointer transition-colors"
>
{#if isOverridden}
<span class="text-indigo-300">{value}</span>
<span class="text-indigo-700 dark:text-indigo-300">{value}</span>
{:else}
{placeholder ?? "default"}
{/if}
</button>
{/if}

{#if isOverridden}
<button onclick={onReset} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset</button>
<button onclick={onReset} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset</button>
{/if}
</div>
Loading