Skip to content

Add light mode support to configurator UI - #500

Closed
jackgranatowski wants to merge 1 commit into
mainfrom
claude/bricks-color-picker-missing-5ajxlq
Closed

Add light mode support to configurator UI#500
jackgranatowski wants to merge 1 commit into
mainfrom
claude/bricks-color-picker-missing-5ajxlq

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

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:

  1. Persisted to localStorage
  2. Defaults to the system prefers-color-scheme preference
  3. Follows system changes live until the user explicitly toggles
  4. Toggled via a new Sun/Moon icon button in the header

Changes

  • New theme system (src/lib/theme.svelte.ts): Reactive theme state with localStorage persistence and system preference detection
  • Theme binding (src/main.ts): Mount root element bound to theme state for Tailwind's dark: variant
  • Header theme toggle (src/components/shell/StudioHeader.svelte): Added Sun/Moon icon button to toggle between light and dark modes
  • Comprehensive color updates: All panels, inputs, and UI components updated with light mode colors:
    • Background colors: bg-white/5bg-black/5 dark:bg-white/5
    • Text colors: text-slate-200text-slate-800 dark:text-slate-200
    • Borders: border-white/10border-black/10 dark:border-white/10
    • Accent colors: text-indigo-300text-indigo-700 dark:text-indigo-300
    • Applied consistently across all 15+ panel components
  • CSS layer (src/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

    • Added light/dark theme support across the configurator interface.
    • Introduced a theme toggle in the header, with the app now following saved or system theme preferences.
  • Bug Fixes

    • Updated panels, controls, previews, and navigation so colors, borders, and text remain readable in both themes.
    • Improved startup behavior so the correct theme is applied before the UI is shown.

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.
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cf800d8c-9d29-43b8-9578-d94308059075

📥 Commits

Reviewing files that changed from the base of the PR and between 5f34a8a and 26e5efb.

📒 Files selected for processing (34)
  • configurator/src/App.svelte
  • configurator/src/app.css
  • configurator/src/components/CommandPalette.svelte
  • configurator/src/components/DomainPanel.svelte
  • configurator/src/components/inputs/ClampField.svelte
  • configurator/src/components/inputs/ColorInput.svelte
  • configurator/src/components/inputs/OklchColorDesk.svelte
  • configurator/src/components/inputs/PowerKnobRow.svelte
  • configurator/src/components/inputs/RangeWithNumber.svelte
  • configurator/src/components/inputs/SliderRow.svelte
  • configurator/src/components/inputs/TokenRow.svelte
  • configurator/src/components/panels/AllTokensTab.svelte
  • configurator/src/components/panels/BordersPanel.svelte
  • configurator/src/components/panels/CheatsheetPanel.svelte
  • configurator/src/components/panels/ColorsPanel.svelte
  • configurator/src/components/panels/EffectsPanel.svelte
  • configurator/src/components/panels/ExportPanel.svelte
  • configurator/src/components/panels/GenericTokenPanel.svelte
  • configurator/src/components/panels/HomePanel.svelte
  • configurator/src/components/panels/LayoutPanel.svelte
  • configurator/src/components/panels/MacrosPanel.svelte
  • configurator/src/components/panels/MiscPanel.svelte
  • configurator/src/components/panels/MotionPanel.svelte
  • configurator/src/components/panels/ShadowsPanel.svelte
  • configurator/src/components/panels/SpacingPanel.svelte
  • configurator/src/components/panels/ThemesPanel.svelte
  • configurator/src/components/panels/TypographyPanel.svelte
  • configurator/src/components/panels/WcagPanel.svelte
  • configurator/src/components/shell/PreviewPanel.svelte
  • configurator/src/components/shell/SidebarNav.svelte
  • configurator/src/components/shell/StatusBar.svelte
  • configurator/src/components/shell/StudioHeader.svelte
  • configurator/src/lib/theme.svelte.ts
  • configurator/src/main.ts
 _________________________________________________
< Copilot is my sidekick. I'm the main character. >
 -------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/bricks-color-picker-missing-5ajxlq

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add light/dark theme toggle for Studio configurator chrome

✨ Enhancement ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add Studio chrome theme state with localStorage persistence and OS preference following.
• Bind .dark on the mount root before first paint to drive Tailwind dark: styling.
• Convert shell, panels, and inputs to light defaults with dark: overrides.
Diagram

graph TD
  header["StudioHeader"] -->|"toggleTheme()"| theme["theme.svelte.ts"] --> storage[("localStorage")]
  theme --> system{{"prefers-color-scheme"}}
  main["main.ts"] -->|"bindThemeRoot()"| theme -->|"toggle .dark"| root["Mount root"] --> tw["Tailwind dark: variant"] --> ui["Studio chrome UI"]

  subgraph Legend
    direction LR
    _ui["UI component"] ~~~ _db[("Storage")] ~~~ _ext{{"System/External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use Tailwind `darkMode: media` (no manual toggle)
  • ➕ No localStorage or custom state management
  • ➕ Automatically tracks OS preference
  • ➖ Doesn’t meet the requirement for an explicit in-app toggle
  • ➖ Can’t implement “follow system until user overrides”
2. Reuse the framework `[data-theme]` / preview theme plumbing
  • ➕ Fewer parallel theming concepts
  • ➕ Potentially less bespoke code
  • ➖ Conflates Studio chrome theming with preview rendering semantics
  • ➖ Higher risk of unintended coupling between chrome and iframe preview
3. Use `data-theme` on the mount root and key Tailwind off it
  • ➕ More extensible if additional modes are added later
  • ➕ Represents state as an attribute rather than a class
  • ➖ More configuration/refactor churn now with little immediate benefit
  • ➖ Still needs isolation from the preview’s theming model

Recommendation: Keep the PR’s approach: a dedicated Studio-chrome theme state with a root .dark class and a Tailwind custom variant. It cleanly separates chrome theming from the preview/theme-token system and avoids a first-paint flash by applying the class before mount.

Files changed (34) +799 / -708

Enhancement (33) +794 / -708
App.svelteApply light/dark chrome base styles to app shell +7/-7

Apply light/dark chrome base styles to app shell

• Switches the top-level shell background/text to light defaults with 'dark:' overrides. Updates mobile fold toggle and domain panel chrome colors for light mode readability.

configurator/src/App.svelte

CommandPalette.svelteAdd light-mode styling to command palette +16/-16

Add light-mode styling to command palette

• Updates palette panel background, borders, placeholder text, and hover states to support light mode while preserving dark mode via 'dark:'.

configurator/src/components/CommandPalette.svelte

DomainPanel.svelteLight-mode styling for domain panel bottom tabs +6/-6

Light-mode styling for domain panel bottom tabs

• Adjusts tab bar background/borders and active/inactive text colors for light mode with 'dark:' variants.

configurator/src/components/DomainPanel.svelte

ClampField.svelteLight-mode styling for clamp field +13/-13

Light-mode styling for clamp field

• Converts container chrome, track styling, numeric inputs, helper text, and reset affordances to light defaults with 'dark:' overrides.

configurator/src/components/inputs/ClampField.svelte

ColorInput.svelteLight-mode styling for ColorInput +5/-5

Light-mode styling for ColorInput

• Updates swatch border, edit input background/text colors, overridden value accent, and reset hover colors for light mode via 'dark:'.

configurator/src/components/inputs/ColorInput.svelte

OklchColorDesk.svelteLight-mode styling for OKLCH editor desk +13/-13

Light-mode styling for OKLCH editor desk

• Updates card chrome, swatch border, labels, input styling, and reset button colors to support light mode while keeping dark mode intact.

configurator/src/components/inputs/OklchColorDesk.svelte

PowerKnobRow.svelteLight-mode styling for power knob rows +4/-4

Light-mode styling for power knob rows

• Adjusts label/help text colors and reset hover state to read correctly in light mode with 'dark:' overrides.

configurator/src/components/inputs/PowerKnobRow.svelte

RangeWithNumber.svelteLight-mode styling for range+number control +3/-3

Light-mode styling for range+number control

• Updates track color, thumb border, and number input colors for light mode via 'dark:' variants.

configurator/src/components/inputs/RangeWithNumber.svelte

SliderRow.svelteLight-mode styling for SliderRow and raw entry +7/-7

Light-mode styling for SliderRow and raw entry

• Updates label text, raw-mode toggle styling, raw input colors, helper copy, and reset hover state for light mode with 'dark:' variants.

configurator/src/components/inputs/SliderRow.svelte

TokenRow.svelteLight-mode styling for token list rows +8/-8

Light-mode styling for token list rows

• Updates hover backgrounds, swatch borders, token/description text, inline edit input colors, and reset affordance for light mode via 'dark:'.

configurator/src/components/inputs/TokenRow.svelte

AllTokensTab.svelteLight-mode styling for All Tokens tab filters/search +11/-11

Light-mode styling for All Tokens tab filters/search

• Updates search input, toggle controls, separators, counts, and empty states to support light mode while retaining dark mode styling.

configurator/src/components/panels/AllTokensTab.svelte

BordersPanel.svelteLight-mode styling for borders panel previews +25/-25

Light-mode styling for borders panel previews

• Converts dividers, preview cards, and option buttons to light defaults with 'dark:' overrides across border sections.

configurator/src/components/panels/BordersPanel.svelte

CheatsheetPanel.svelteLight-mode styling for cheatsheet panel +29/-29

Light-mode styling for cheatsheet panel

• Updates kind color mappings to dual-mode classes and restyles search, tabs, list items, and copy affordances for light mode via 'dark:'.

configurator/src/components/panels/CheatsheetPanel.svelte

ColorsPanel.svelteLight-mode styling for colors panel UI +83/-83

Light-mode styling for colors panel UI

• Updates semantic previews, swatch borders, helper text, and section dividers to support light mode with 'dark:' variants.

configurator/src/components/panels/ColorsPanel.svelte

EffectsPanel.svelteLight-mode styling for effects panel previews +26/-26

Light-mode styling for effects panel previews

• Restyles blur/opacity/scrim preview chrome and explanatory copy to light defaults with 'dark:' overrides.

configurator/src/components/panels/EffectsPanel.svelte

ExportPanel.svelteLight-mode styling for export/share panel +14/-14

Light-mode styling for export/share panel

• Updates helper text, buttons, code block backgrounds, and success state colors for light mode via 'dark:' variants.

configurator/src/components/panels/ExportPanel.svelte

GenericTokenPanel.svelteLight-mode styling for generic token panel +4/-4

Light-mode styling for generic token panel

• Adjusts modified counter accent, search input styling, and empty/more states for light mode with 'dark:' overrides.

configurator/src/components/panels/GenericTokenPanel.svelte

HomePanel.svelteLight-mode styling for home panel lists and accents +22/-22

Light-mode styling for home panel lists and accents

• Updates welcome copy, override accents, list hover states, and navigation chevrons for light mode via 'dark:' variants.

configurator/src/components/panels/HomePanel.svelte

LayoutPanel.svelteLight-mode styling for layout panel labels/previews +55/-55

Light-mode styling for layout panel labels/previews

• Updates labels, preview cards, separators, and sample UI elements to light defaults with 'dark:' overrides across sections.

configurator/src/components/panels/LayoutPanel.svelte

MacrosPanel.svelteLight-mode styling for macros panel docs and demos +41/-41

Light-mode styling for macros panel docs and demos

• Updates descriptive text (including code spans), preview container chrome, and separators for light mode with 'dark:' variants.

configurator/src/components/panels/MacrosPanel.svelte

MiscPanel.svelteLight-mode styling for misc panel controls and previews +73/-73

Light-mode styling for misc panel controls and previews

• Restyles preview containers, separators, and various control states to read correctly in light mode via 'dark:'.

configurator/src/components/panels/MiscPanel.svelte

MotionPanel.svelteLight-mode styling for motion panel UI +36/-36

Light-mode styling for motion panel UI

• Updates quick toggle chrome, warnings, preview cards, selects, and separators for light mode with 'dark:' variants.

configurator/src/components/panels/MotionPanel.svelte

ShadowsPanel.svelteLight-mode styling for shadows panel previews +18/-18

Light-mode styling for shadows panel previews

• Updates elevation preview surfaces, borders, labels, toggles, and separators to support light mode via 'dark:'.

configurator/src/components/panels/ShadowsPanel.svelte

SpacingPanel.svelteLight-mode styling for spacing panel previews +8/-8

Light-mode styling for spacing panel previews

• Updates scale preview containers, label text, separators, and advanced section toggle colors for light mode via 'dark:' variants.

configurator/src/components/panels/SpacingPanel.svelte

ThemesPanel.svelteLight-mode styling for themes management panel +18/-18

Light-mode styling for themes management panel

• Restyles theme save form, empty state, theme cards, chips, and destructive actions for light mode with 'dark:' overrides.

configurator/src/components/panels/ThemesPanel.svelte

TypographyPanel.svelteLight-mode styling for typography panel controls +86/-86

Light-mode styling for typography panel controls

• Updates labels, selects, segmented controls, preview cards, and separators to support light mode while preserving dark mode via 'dark:'.

configurator/src/components/panels/TypographyPanel.svelte

WcagPanel.svelteLight-mode styling for WCAG panel + status badges +30/-30

Light-mode styling for WCAG panel + status badges

• Updates control chrome, borders, and pass/fail badge colors so contrast results remain legible in light mode with 'dark:' variants.

configurator/src/components/panels/WcagPanel.svelte

PreviewPanel.svelteLight-mode styling for preview panel chrome (not iframe theme) +20/-20

Light-mode styling for preview panel chrome (not iframe theme)

• Updates preview panel container and toolbar UI (segmented controls, separators, headers) to support light mode while keeping preview theme controls separate.

configurator/src/components/shell/PreviewPanel.svelte

SidebarNav.svelteLight-mode styling for sidebar navigation rail +4/-4

Light-mode styling for sidebar navigation rail

• Updates sidebar background/borders and inactive hover states for light mode with 'dark:' variants.

configurator/src/components/shell/SidebarNav.svelte

StatusBar.svelteLight-mode styling for bottom status bar +4/-4

Light-mode styling for bottom status bar

• Updates status bar background/borders and text accents (including override count) for light mode via 'dark:' variants.

configurator/src/components/shell/StatusBar.svelte

StudioHeader.svelteAdd Sun/Moon theme toggle + light-mode header styling +34/-19

Add Sun/Moon theme toggle + light-mode header styling

• Adds a theme toggle button wired to centralized theme state. Updates header chrome and reset confirmation modal styling to support light mode with 'dark:' overrides.

configurator/src/components/shell/StudioHeader.svelte

theme.svelte.tsAdd Studio chrome theme state (persisted + OS-following) +67/-0

Add Studio chrome theme state (persisted + OS-following)

• Introduces a reactive theme state that defaults to 'prefers-color-scheme', follows OS changes until the user explicitly toggles, then persists the choice in localStorage. Exposes APIs to bind a root element and apply '.dark'.

configurator/src/lib/theme.svelte.ts

main.tsBind theme root before mount to prevent wrong-theme flash +4/-0

Bind theme root before mount to prevent wrong-theme flash

• Registers the mount root with the theme system before app mount so '.dark' is applied for the first paint.

configurator/src/main.ts

Other (1) +5 / -0
app.cssDefine Tailwind 'dark' custom variant for Studio chrome +5/-0

Define Tailwind 'dark' custom variant for Studio chrome

• Documents separation between Studio chrome theme and framework '[data-theme]'. Adds a class-based 'dark' variant keyed off '.dark' on the mount root.

configurator/src/app.css

@coderabbitai coderabbitai Bot added the codex label Jul 3, 2026
@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 11 rules

Grey Divider


Remediation recommended

1. Leaking matchMedia listener 🐞 Bug ☼ Reliability
Description
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.
Code

configurator/src/lib/theme.svelte.ts[R61-66]

+if (typeof matchMedia !== "undefined") {
+  matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => {
+    if (!followSystem) return;
+    themeState.value = e.matches ? "dark" : "light";
+    applyToRoot();
+  });
Relevance

⭐⭐⭐ High

They accept listener cleanup patterns (matchMedia change listener w/ remove in #312/#316; teardown
cleanup in #443).

PR-#312
PR-#316
PR-#443

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code adds an event listener unconditionally when matchMedia exists, and there is no
corresponding removeEventListener or any one-time guard, so repeated evaluations can attach
multiple callbacks.

configurator/src/lib/theme.svelte.ts[61-66]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### 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


2. Unscoped dark variant selector 🐞 Bug ≡ Correctness
Description
@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.
Code

configurator/src/app.css[R3-6]

+/* 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 *));
Relevance

⭐⭐ Medium

Embed-host isolation fixes accepted (PR #463), but no history on Tailwind dark variant selector
scoping.

PR-#463

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The dark variant is defined to match any .dark ancestor, and the runtime theme system toggles a
generic dark class on the mount root selected in main.ts; this combination means unrelated
.dark classes outside the app tree can trigger dark: styles even when the app tries to stay in
light mode.

configurator/src/app.css[3-6]
configurator/src/lib/theme.svelte.ts[34-44]
configurator/src/main.ts[19-28]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### 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


Grey Divider

Qodo Logo

Comment thread configurator/src/app.css
Comment on lines +3 to +6
/* 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 *));

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

Comment on lines +61 to +66
if (typeof matchMedia !== "undefined") {
matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => {
if (!followSystem) return;
themeState.value = e.matches ? "dark" : "light";
applyToRoot();
});

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

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

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (2)
configurator/src/lib/theme.svelte.ts (1)

61-67: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Media-query listener is never removed; consider testability/HMR implications.

This module registers a change listener on matchMedia(...) 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 tradeoff

Repeated 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5f34a8a and 26e5efb.

📒 Files selected for processing (34)
  • configurator/src/App.svelte
  • configurator/src/app.css
  • configurator/src/components/CommandPalette.svelte
  • configurator/src/components/DomainPanel.svelte
  • configurator/src/components/inputs/ClampField.svelte
  • configurator/src/components/inputs/ColorInput.svelte
  • configurator/src/components/inputs/OklchColorDesk.svelte
  • configurator/src/components/inputs/PowerKnobRow.svelte
  • configurator/src/components/inputs/RangeWithNumber.svelte
  • configurator/src/components/inputs/SliderRow.svelte
  • configurator/src/components/inputs/TokenRow.svelte
  • configurator/src/components/panels/AllTokensTab.svelte
  • configurator/src/components/panels/BordersPanel.svelte
  • configurator/src/components/panels/CheatsheetPanel.svelte
  • configurator/src/components/panels/ColorsPanel.svelte
  • configurator/src/components/panels/EffectsPanel.svelte
  • configurator/src/components/panels/ExportPanel.svelte
  • configurator/src/components/panels/GenericTokenPanel.svelte
  • configurator/src/components/panels/HomePanel.svelte
  • configurator/src/components/panels/LayoutPanel.svelte
  • configurator/src/components/panels/MacrosPanel.svelte
  • configurator/src/components/panels/MiscPanel.svelte
  • configurator/src/components/panels/MotionPanel.svelte
  • configurator/src/components/panels/ShadowsPanel.svelte
  • configurator/src/components/panels/SpacingPanel.svelte
  • configurator/src/components/panels/ThemesPanel.svelte
  • configurator/src/components/panels/TypographyPanel.svelte
  • configurator/src/components/panels/WcagPanel.svelte
  • configurator/src/components/shell/PreviewPanel.svelte
  • configurator/src/components/shell/SidebarNav.svelte
  • configurator/src/components/shell/StatusBar.svelte
  • configurator/src/components/shell/StudioHeader.svelte
  • configurator/src/lib/theme.svelte.ts
  • configurator/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">

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.

Comment on lines +55 to +62
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"}`}>

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

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.

Comment on lines 71 to +80
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',

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

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.

Comment on lines +45 to +49
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>

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

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.

Comment on lines +444 to +458
<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>

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

<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>

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

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.

Suggested change
<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.

Comment on lines +94 to +99
? "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",

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 | 🟠 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.

Suggested change
? "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.

Copy link
Copy Markdown
Contributor Author

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):

  1. Add light mode toggle to Studio shell chrome (1/3) #506 — theme system + toggle + shell chrome (10 files)
  2. Light mode for input controls and panels, batch A (2/3) #507 — input controls + panels batch A (15 files)
  3. Light mode for remaining panels, batch B (3/3) #508 — remaining panels, batch B (9 files)

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants