Add light mode support to configurator UI - #500
Conversation
The Studio UI was hardcoded dark-only. Adds a light/dark theme toggle (header) that defaults to and live-follows prefers-color-scheme until the user picks explicitly, persisted to localStorage. Every dark-only Tailwind utility across the shell and panels now pairs with a light-mode base value plus a `dark:` variant, using Tailwind v4's class-based custom variant keyed off a `.dark` class applied to the mount root before first paint (no flash of the wrong theme). Fixed backgrounds used purely as content previews (scrim/caption demos, tooltip chips) were deliberately left unmirrored since they represent color values, not app chrome.
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (34)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoAdd light/dark theme toggle for Studio configurator chrome
AI Description
Diagram
High-Level Assessment
Files changed (34)
|
Code Review by Qodo
Context used✅ Compliance rules (platform):
11 rules 1. Leaking matchMedia listener
|
| /* 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 *)); |
There was a problem hiding this comment.
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
| if (typeof matchMedia !== "undefined") { | ||
| matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => { | ||
| if (!followSystem) return; | ||
| themeState.value = e.matches ? "dark" : "light"; | ||
| applyToRoot(); | ||
| }); |
There was a problem hiding this comment.
2. Leaking matchmedia listener 🐞 Bug ☼ Reliability
theme.svelte.ts registers a prefers-color-scheme change listener at module load with no guard or removal mechanism. If the module is re-evaluated (e.g., HMR/dev reload or multiple bundles/instances), listeners can accumulate and cause redundant theme updates and memory leaks.
Agent Prompt
### Issue description
A `matchMedia("(prefers-color-scheme: dark)")` `change` listener is added at module scope and never removed. In scenarios where the module is evaluated more than once, this can register multiple listeners.
### Issue Context
The listener mutates shared module state (`themeState`, `followSystem`) and calls `applyToRoot()`, so duplicate listeners can produce redundant work and harder-to-debug behavior.
### Fix Focus Areas
- configurator/src/lib/theme.svelte.ts[23-67]
### Suggested fix
- Store the `MediaQueryList` and handler function in module scope and ensure the listener is only registered once (e.g., a `let listenerAttached = false`).
- Optionally export a `disposeThemeSystem()` (or return a disposer from `bindThemeRoot`) that calls `mql.removeEventListener("change", handler)` for teardown/HMR safety.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
configurator/src/lib/theme.svelte.ts (1)
61-67: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMedia-query listener is never removed; consider testability/HMR implications.
This module registers a
changelistener onmatchMedia(...)as a top-level side effect at import time, and it's never cleaned up. For a browser app this is generally fine for the app's lifetime, but it does mean:
- Under Vite HMR, if this module is re-evaluated, duplicate listeners can accumulate in dev (not a production issue).
- Unit tests that import this module repeatedly (e.g., with vitest) will have a hard time resetting/mocking state cleanly, since
stored,followSystem, and the listener are all fixed at first import.Not blocking, but worth a defensive teardown/reset hook (or restructuring as a factory) if this module needs to be tested or hot-reloaded frequently.
🤖 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/lib/theme.svelte.ts` around lines 61 - 67, The top-level matchMedia change listener in theme.svelte.ts is never cleaned up, which can cause duplicate listeners on HMR and make tests hard to reset. Refactor the listener setup around the existing theme state logic (e.g. applyToRoot, followSystem, themeState) so it can be torn down or recreated, such as by exposing a setup/cleanup hook or factory, and ensure repeated imports do not register multiple listeners.configurator/src/components/panels/LayoutPanel.svelte (1)
153-153: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffRepeated divider markup across all panels — candidate for a shared component.
The
<div class="h-px bg-black/6 dark:bg-white/6"></div>divider pattern recurs dozens of times in this file (and identically in MacrosPanel, MiscPanel, MotionPanel, ShadowsPanel, SpacingPanel). Extracting a tiny<Divider />component would centralize future palette tweaks. Not blocking, purely a DRY nicety spanning many files.Also applies to: 189-189, 244-244, 294-294, 337-337, 369-369, 396-396, 616-616, 661-661
🤖 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/panels/LayoutPanel.svelte` at line 153, The divider markup is duplicated across LayoutPanel and the other panel components, so extract the repeated h-px bg-black/6 dark:bg-white/6 block into a shared Divider component and replace each inline occurrence with that component. Update LayoutPanel and the sibling panel files (MacrosPanel, MiscPanel, MotionPanel, ShadowsPanel, SpacingPanel) to use the new Divider component consistently so future style changes only need to happen in one place.
🤖 Prompt for all review comments with 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.
Inline comments:
In `@configurator/src/components/CommandPalette.svelte`:
- 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.
In `@configurator/src/components/panels/AllTokensTab.svelte`:
- Around line 55-62: The search input in AllTokensTab.svelte still uses a
light-theme placeholder color, so update the placeholder styling to include a
dark-mode variant alongside the existing input classes. Adjust the search field
markup in the component that renders the filter input so the placeholder
contrast matches the new dark chrome, following the same theming pattern used
elsewhere in the search input styles.
In `@configurator/src/components/panels/CheatsheetPanel.svelte`:
- Around line 71-80: The KIND_COLOR map in CheatsheetPanel.svelte is missing
dark-mode variants for the motion and form entries, so those badges won’t match
the rest of the theme-aware palette. Update the KIND_COLOR object to add
dark:class values for motion and form, following the same pattern used by
layout, macro, state, accessibility, print, component, and theme so the badge
colors stay readable in dark mode.
In `@configurator/src/components/panels/GenericTokenPanel.svelte`:
- Around line 45-49: The search input in GenericTokenPanel.svelte keeps a
light-only placeholder style, so the hint text is too dim in dark mode. Update
the input’s placeholder classes to include a matching dark variant alongside the
existing placeholder:text-slate-600, using the same theming pattern as the
updated search input styling so the placeholder remains readable in dark panels.
In `@configurator/src/components/panels/LayoutPanel.svelte`:
- Around line 444-458: The hardcoded <option> background in the select controls
is preventing theme-aware readability in light mode. Update both occurrences in
LayoutPanel.svelte (the cluster UI select and the background-layer Fit/Position
select) so the <option> styling adapts to the current theme instead of always
using the dark background; use the existing theme-aware approach used by the
surrounding <select> styles, and keep the change tied to the relevant select
rendering blocks.
In `@configurator/src/components/shell/StatusBar.svelte`:
- Line 21: The domain label in StatusBar.svelte is too low-contrast in light
mode, making it hard to read on the current background. Update the domain span’s
text color class to match the file’s muted dual-theme pattern used by the
sibling label, keeping it legible in both light and dark modes. Locate the span
rendering {domain} in StatusBar.svelte and adjust its Tailwind text color
utility accordingly.
In `@configurator/src/components/shell/StudioHeader.svelte`:
- Around line 94-99: The save-state chip styles in StudioHeader are using
dark-only red/emerald backgrounds with light/dark text pairs, so add matching
light-mode background variants for the error and saved states. Update the
conditional class strings in the saveState branch of the header chip styling so
the red and emerald states use readable light-mode backgrounds while preserving
the existing dark-mode appearance and text color pairings.
---
Nitpick comments:
In `@configurator/src/components/panels/LayoutPanel.svelte`:
- Line 153: The divider markup is duplicated across LayoutPanel and the other
panel components, so extract the repeated h-px bg-black/6 dark:bg-white/6 block
into a shared Divider component and replace each inline occurrence with that
component. Update LayoutPanel and the sibling panel files (MacrosPanel,
MiscPanel, MotionPanel, ShadowsPanel, SpacingPanel) to use the new Divider
component consistently so future style changes only need to happen in one place.
In `@configurator/src/lib/theme.svelte.ts`:
- Around line 61-67: The top-level matchMedia change listener in theme.svelte.ts
is never cleaned up, which can cause duplicate listeners on HMR and make tests
hard to reset. Refactor the listener setup around the existing theme state logic
(e.g. applyToRoot, followSystem, themeState) so it can be torn down or
recreated, such as by exposing a setup/cleanup hook or factory, and ensure
repeated imports do not register multiple listeners.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: cf800d8c-9d29-43b8-9578-d94308059075
📒 Files selected for processing (34)
configurator/src/App.svelteconfigurator/src/app.cssconfigurator/src/components/CommandPalette.svelteconfigurator/src/components/DomainPanel.svelteconfigurator/src/components/inputs/ClampField.svelteconfigurator/src/components/inputs/ColorInput.svelteconfigurator/src/components/inputs/OklchColorDesk.svelteconfigurator/src/components/inputs/PowerKnobRow.svelteconfigurator/src/components/inputs/RangeWithNumber.svelteconfigurator/src/components/inputs/SliderRow.svelteconfigurator/src/components/inputs/TokenRow.svelteconfigurator/src/components/panels/AllTokensTab.svelteconfigurator/src/components/panels/BordersPanel.svelteconfigurator/src/components/panels/CheatsheetPanel.svelteconfigurator/src/components/panels/ColorsPanel.svelteconfigurator/src/components/panels/EffectsPanel.svelteconfigurator/src/components/panels/ExportPanel.svelteconfigurator/src/components/panels/GenericTokenPanel.svelteconfigurator/src/components/panels/HomePanel.svelteconfigurator/src/components/panels/LayoutPanel.svelteconfigurator/src/components/panels/MacrosPanel.svelteconfigurator/src/components/panels/MiscPanel.svelteconfigurator/src/components/panels/MotionPanel.svelteconfigurator/src/components/panels/ShadowsPanel.svelteconfigurator/src/components/panels/SpacingPanel.svelteconfigurator/src/components/panels/ThemesPanel.svelteconfigurator/src/components/panels/TypographyPanel.svelteconfigurator/src/components/panels/WcagPanel.svelteconfigurator/src/components/shell/PreviewPanel.svelteconfigurator/src/components/shell/SidebarNav.svelteconfigurator/src/components/shell/StatusBar.svelteconfigurator/src/components/shell/StudioHeader.svelteconfigurator/src/lib/theme.svelte.tsconfigurator/src/main.ts
| {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"> |
There was a problem hiding this comment.
🎯 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.
| <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.
| class="w-full bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded-lg px-3 py-1.5 text-[11px] text-slate-800 dark:text-slate-200 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500" | ||
| /> | ||
| <div class="flex items-center gap-3"> | ||
| <button | ||
| onclick={() => { onlyModified = !onlyModified; }} | ||
| class={`flex items-center gap-1 text-[9px] font-bold transition-colors cursor-pointer ${onlyModified ? "text-indigo-400" : "text-slate-600 hover:text-slate-400"}`} | ||
| class={`flex items-center gap-1 text-[9px] font-bold transition-colors cursor-pointer ${onlyModified ? "text-indigo-600 dark:text-indigo-400" : "text-slate-400 dark:text-slate-600 hover:text-slate-600 dark:hover:text-slate-400"}`} | ||
| > | ||
| <div class={`w-2.5 h-2.5 rounded border flex items-center justify-center transition-colors ${onlyModified ? "bg-indigo-600 border-indigo-500" : "border-white/20"}`}> | ||
| <div class={`w-2.5 h-2.5 rounded border flex items-center justify-center transition-colors ${onlyModified ? "bg-indigo-600 border-indigo-500" : "border-black/20 dark:border-white/20"}`}> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Match the search placeholder to dark mode.
placeholder:text-slate-600 is still light-theme tuned, so the hint reads at the wrong contrast level in the new dark chrome. Add a matching dark:placeholder:* variant here. Based on the updated search-input theming hunk.
🤖 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/panels/AllTokensTab.svelte` around lines 55 - 62,
The search input in AllTokensTab.svelte still uses a light-theme placeholder
color, so update the placeholder styling to include a dark-mode variant
alongside the existing input classes. Adjust the search field markup in the
component that renders the filter input so the placeholder contrast matches the
new dark chrome, following the same theming pattern used elsewhere in the search
input styles.
| const KIND_COLOR: Record<string, string> = { | ||
| layout: 'text-violet-400', | ||
| macro: 'text-sky-400', | ||
| state: 'text-amber-400', | ||
| accessibility: 'text-emerald-400', | ||
| layout: 'text-violet-600 dark:text-violet-400', | ||
| macro: 'text-sky-600 dark:text-sky-400', | ||
| state: 'text-amber-600 dark:text-amber-400', | ||
| accessibility: 'text-emerald-600 dark:text-emerald-400', | ||
| motion: 'text-pink-400', | ||
| print: 'text-slate-400', | ||
| print: 'text-slate-600 dark:text-slate-400', | ||
| form: 'text-orange-400', | ||
| component: 'text-indigo-400', | ||
| theme: 'text-teal-400', | ||
| component: 'text-indigo-600 dark:text-indigo-400', | ||
| theme: 'text-teal-600 dark:text-teal-400', |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Finish the dark-mode palette for KIND_COLOR.
motion and form still use light-only colors, so those badges won't match the rest of the theme-aware map and will read poorly in dark mode. Add the missing dark variants for those entries. Based on the updated kind-badge theming hunk.
🤖 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/panels/CheatsheetPanel.svelte` around lines 71 -
80, The KIND_COLOR map in CheatsheetPanel.svelte is missing dark-mode variants
for the motion and form entries, so those badges won’t match the rest of the
theme-aware palette. Update the KIND_COLOR object to add dark:class values for
motion and form, following the same pattern used by layout, macro, state,
accessibility, print, component, and theme so the badge colors stay readable in
dark mode.
| class="w-full bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded-lg px-3 py-1.5 text-[11px] text-slate-800 dark:text-slate-200 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500" | ||
| /> | ||
|
|
||
| {#if filtered().length === 0} | ||
| <p class="text-[11px] text-slate-600 text-center py-8">No tokens found</p> | ||
| <p class="text-[11px] text-slate-400 dark:text-slate-600 text-center py-8">No tokens found</p> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Give the search placeholder a dark variant too.
The updated input keeps a light-only placeholder color, so the hint text will remain too dim against the dark panel. Add a matching dark:placeholder:* class. Based on the updated search-input theming hunk.
🤖 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/panels/GenericTokenPanel.svelte` around lines 45
- 49, The search input in GenericTokenPanel.svelte keeps a light-only
placeholder style, so the hint text is too dim in dark mode. Update the input’s
placeholder classes to include a matching dark variant alongside the existing
placeholder:text-slate-600, using the same theming pattern as the updated search
input styling so the placeholder remains readable in dark panels.
| <span class="text-[10px] font-semibold text-slate-600 dark:text-slate-400 w-16 shrink-0">{row.label}</span> | ||
| <select | ||
| value={overrides[row.token] ?? row.def} | ||
| onchange={(e) => { | ||
| const v = (e.target as HTMLSelectElement).value; | ||
| v === row.def ? onReset(row.token) : onSet(row.token, v); | ||
| }} | ||
| class="flex-1 bg-white/5 border border-white/10 rounded px-1.5 py-1 text-[9px] font-mono text-slate-300 focus:outline-none focus:border-indigo-500 cursor-pointer" | ||
| class="flex-1 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 focus:outline-none focus:border-indigo-500 cursor-pointer" | ||
| > | ||
| {#each row.opts as o (o)} | ||
| <option value={o} style="background:#16161e;">{o}</option> | ||
| {/each} | ||
| </select> | ||
| {#if row.token in overrides} | ||
| <button onclick={() => onReset(row.token)} class="text-[8px] text-slate-500 hover:text-rose-400 cursor-pointer shrink-0">reset</button> | ||
| <button onclick={() => onReset(row.token)} class="text-[8px] text-slate-500 hover:text-rose-600 dark:hover:text-rose-400 cursor-pointer shrink-0">reset</button> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
<option> background stays hardcoded dark, breaking light-mode readability.
Both <select> blocks (cluster UI at Lines 445-455 and background-layer Fit/Position at Lines 682-693) style their <option> elements with a fixed style="background:#16161e;". The <select> itself and its text now switch color with the theme (text-slate-700 dark:text-slate-300), but the option background never adapts — in light mode this renders dark-gray text on a near-black dropdown background, hurting readability.
💡 Proposed fix
- <option value={o} style="background:`#16161e`;">{o}</option>
+ <option value={o} class="bg-white dark:bg-[`#16161e`] text-slate-700 dark:text-slate-300">{o}</option>Apply the same change to the second occurrence around Line 691.
Also applies to: 675-695
🤖 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/panels/LayoutPanel.svelte` around lines 444 -
458, The hardcoded <option> background in the select controls is preventing
theme-aware readability in light mode. Update both occurrences in
LayoutPanel.svelte (the cluster UI select and the background-layer Fit/Position
select) so the <option> styling adapts to the current theme instead of always
using the dark background; use the existing theme-aware approach used by the
surrounding <select> styles, and keep the change tied to the relevant select
rendering blocks.
| </span> | ||
| <div class="flex-1"></div> | ||
| <span class="text-[9px] font-mono text-slate-700">{domain}</span> | ||
| <span class="text-[9px] font-mono text-slate-300 dark:text-slate-700">{domain}</span> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Domain label likely invisible in light mode.
text-slate-300 on bg-slate-100 has very low contrast (near-invisible), unlike the sibling muted-text pattern in this file (text-slate-400 dark:text-slate-600 at Line 13) which stays legible in both themes.
🎨 Proposed fix
- <span class="text-[9px] font-mono text-slate-300 dark:text-slate-700">{domain}</span>
+ <span class="text-[9px] font-mono text-slate-400 dark:text-slate-700">{domain}</span>📝 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.
| <span class="text-[9px] font-mono text-slate-300 dark:text-slate-700">{domain}</span> | |
| <span class="text-[9px] font-mono text-slate-400 dark:text-slate-700">{domain}</span> |
🤖 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/shell/StatusBar.svelte` at line 21, The domain
label in StatusBar.svelte is too low-contrast in light mode, making it hard to
read on the current background. Update the domain span’s text color class to
match the file’s muted dual-theme pattern used by the sibling label, keeping it
legible in both light and dark modes. Locate the span rendering {domain} in
StatusBar.svelte and adjust its Tailwind text color utility accordingly.
| ? "bg-red-900/40 border border-red-500/30 text-red-700 dark:text-red-300" | ||
| : saveState === 'saved' | ||
| ? "bg-emerald-900/40 border border-emerald-500/30 text-emerald-300" | ||
| ? "bg-emerald-900/40 border border-emerald-500/30 text-emerald-700 dark:text-emerald-300" | ||
| : hasPendingChanges | ||
| ? "bg-emerald-600 hover:bg-emerald-500 text-white shadow-sm shadow-emerald-600/30" | ||
| : "bg-white/5 text-slate-500 disabled:opacity-40 disabled:pointer-events-none", | ||
| : "bg-black/5 dark:bg-white/5 text-slate-500 disabled:opacity-40 disabled:pointer-events-none", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Error/saved save-state chip backgrounds not paired with a light-mode variant.
Lines 94 and 96 pair light/dark text colors (text-red-700 dark:text-red-300, text-emerald-700 dark:text-emerald-300) with backgrounds that stay dark-only (bg-red-900/40, bg-emerald-900/40, no dark: prefix). In light mode this produces a near-black chip with dark-red/dark-emerald text on top — likely unreadable, unlike the rest of the header where every other background got a light counterpart.
🎨 Proposed fix
class={saveState === 'error'
- ? "bg-red-900/40 border border-red-500/30 text-red-700 dark:text-red-300"
+ ? "bg-red-100 dark:bg-red-900/40 border border-red-500/30 text-red-700 dark:text-red-300"
: saveState === 'saved'
- ? "bg-emerald-900/40 border border-emerald-500/30 text-emerald-700 dark:text-emerald-300"
+ ? "bg-emerald-100 dark:bg-emerald-900/40 border border-emerald-500/30 text-emerald-700 dark:text-emerald-300"
: hasPendingChanges📝 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.
| ? "bg-red-900/40 border border-red-500/30 text-red-700 dark:text-red-300" | |
| : saveState === 'saved' | |
| ? "bg-emerald-900/40 border border-emerald-500/30 text-emerald-300" | |
| ? "bg-emerald-900/40 border border-emerald-500/30 text-emerald-700 dark:text-emerald-300" | |
| : hasPendingChanges | |
| ? "bg-emerald-600 hover:bg-emerald-500 text-white shadow-sm shadow-emerald-600/30" | |
| : "bg-white/5 text-slate-500 disabled:opacity-40 disabled:pointer-events-none", | |
| : "bg-black/5 dark:bg-white/5 text-slate-500 disabled:opacity-40 disabled:pointer-events-none", | |
| ? "bg-red-100 dark:bg-red-900/40 border border-red-500/30 text-red-700 dark:text-red-300" | |
| : saveState === 'saved' | |
| ? "bg-emerald-100 dark:bg-emerald-900/40 border border-emerald-500/30 text-emerald-700 dark:text-emerald-300" | |
| : hasPendingChanges | |
| ? "bg-emerald-600 hover:bg-emerald-500 text-white shadow-sm shadow-emerald-600/30" | |
| : "bg-black/5 dark:bg-white/5 text-slate-500 disabled:opacity-40 disabled:pointer-events-none", |
🤖 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/shell/StudioHeader.svelte` around lines 94 - 99,
The save-state chip styles in StudioHeader are using dark-only red/emerald
backgrounds with light/dark text pairs, so add matching light-mode background
variants for the error and saved states. Update the conditional class strings in
the saveState branch of the header chip styling so the red and emerald states
use readable light-mode backgrounds while preserving the existing dark-mode
appearance and text color pairings.
|
Splitting this into a stacked chain of smaller PRs per feedback that this was too much to review at once (34 files in one diff):
Each PR was verified to type-check and pass the full test suite on its own, and the 3-way split reconstitutes byte-for-byte the same diff this PR had. Also folded in fixes for the real findings the review bots caught here (contrast bugs, missing dark: pairings, toggle accessibility, a matchMedia hardening) before splitting, so they don't need to be re-litigated on the new PRs. Closing this one in favor of the stack above. Generated by Claude Code |
Adds a complete light/dark theme toggle to the SLASHED Studio configurator, independent of the framework's own token preview theme.
Summary
The configurator chrome (header, sidebar, panels, inputs) now supports both light and dark modes. The theme preference is:
prefers-color-schemepreferenceChanges
src/lib/theme.svelte.ts): Reactive theme state with localStorage persistence and system preference detectionsrc/main.ts): Mount root element bound to theme state for Tailwind'sdark:variantsrc/components/shell/StudioHeader.svelte): Added Sun/Moon icon button to toggle between light and dark modesbg-white/5→bg-black/5 dark:bg-white/5text-slate-200→text-slate-800 dark:text-slate-200border-white/10→border-black/10 dark:border-white/10text-indigo-300→text-indigo-700 dark:text-indigo-300src/app.css): Added documentation for the studio chrome theme layer (separate from framework token preview)The framework's own token preview (in the preview panel) remains unaffected — only the configurator UI chrome responds to the theme toggle.
https://claude.ai/code/session_01LmmKpKVFUw5XNAAJxPFQep
Summary by CodeRabbit
New Features
Bug Fixes