Skip to content

Add luminance lock, gradient editor, macros panel, and layout primitives UI - #431

Merged
jackgranatowski merged 1 commit into
mainfrom
claude/configurator-refinements-z5javt
Jun 27, 2026
Merged

Add luminance lock, gradient editor, macros panel, and layout primitives UI#431
jackgranatowski merged 1 commit into
mainfrom
claude/configurator-refinements-z5javt

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds three major UI features to the configurator and enhances existing panels with more granular controls:

  1. Luminance Lock — A new color-locking system that pins brand colors (primary, secondary, tertiary, action) to a fixed OKLCH lightness value, useful for stable sections. Includes a live preview toggle and before/after color swatches.

  2. Gradient Editor — Converts the static gradient token list into a structured editor with granular controls for interpolation space, angle/direction, and color stops. Each gradient now shows its composed formula and allows real-time editing.

  3. Macros Panel — A new domain panel exposing utility macro tokens (.sf-prose, .sf-flow, .sf-line-clamp, .sf-aspect, .sf-scroll-shadow, .sf-scrim) with dedicated UI controls for flow rhythm, line clamping, aspect ratio presets, scroll shadow sizing, scrim color/direction, and prose spacing.

  4. Layout Primitives UI — Expanded LayoutPanel with controls for switcher threshold, sidebar min-width, equal grid columns, cover height, frame ratio, reel item width, imposter margin, content grid widths, and alternate gap.

Key Changes

  • ColorsPanel.svelte: Added luminance lock section with --sf-lumlocker slider, color preview grid, and canvas toggle. Refactored gradient tokens into structured GradientDef type with lazy-loaded editors and composition functions. Enhanced curve preview to show actual resolved palette swatches with side toggle (light/dark).

  • MacrosPanel.svelte (new): Complete UI for prose, flow, line-clamp, aspect, scroll-shadow, and scrim tokens with live previews and preset buttons.

  • LayoutPanel.svelte: Added "More layout primitives" collapsible section with sliders and preset buttons for switcher, sidebar, equal, cover, frame, reel, imposter, content-grid, and alternate tokens.

  • lumlockerPreview.svelte.ts (new): Module-level rune store for toggling luminance lock preview on the live canvas without prop drilling.

  • MiscPanel.svelte: Added focus ring style preview box and live caret/underline sample input.

  • SpacingPanel.svelte: Added gutter slider and shared fluid viewport endpoint controls (vwMin/vwMax).

  • TypographyPanel.svelte: Added shared fluid viewport endpoint controls (vwMin/vwMax) and improved modular scale section labeling.

  • BordersPanel.svelte: Added border style token control.

  • ClampField.svelte: Added optional minLabel and maxLabel props for customizable field labels.

  • PreviewPanel.svelte: Integrated lumlockerPreview store to apply/remove data-lumlocker attribute on iframe root based on toggle state. Fixed border style syntax in marketing template.

  • DomainPanel.svelte: Imported and wired MacrosPanel.

  • SidebarNav.svelte: Updated icon import to use Blocks for macros domain.

  • domains.ts: Added macros domain with token patterns, ordered first to prevent substring collisions.

  • App.svelte & CommandPalette.svelte: Added macros domain to navigation and command palette routing.

Implementation Details

  • Luminance lock uses parseOklch and stringifyOklch to preserve chroma and hue while remapping lightness.
  • Gradient editor lazily initializes edits from defaults, allowing UI to show both the framework's derived formula and user-editable pieces.
  • Curve preview now renders actual resolved color swatches (via paintTheme) with height bars representing mix percentages, doubling as a live palette.
  • Macros panel groups related tokens (prose spacing, scrim direction, etc.) with preset buttons and live preview boxes.
  • Layout primitives use consistent slider/preset patterns matching existing panels.
  • All new controls follow existing override/reset patterns and integrate with the configurator's state management.

https://claude.ai/code/session_01JBrDq4qYBGC9Sa611kGLtX

Summary by CodeRabbit

  • New Features

    • Added a new “Macros” section with controls for common token-based settings.
    • Expanded layout controls with more adjustable primitives and presets.
    • Added a luminance-lock option for brand colors in the preview.
  • Improvements

    • Enhanced color editing with richer gradient controls and clearer swatches.
    • Added live previews for borders, focus rings, links, carets, and spacing settings.
    • Improved labels and navigation so related controls are easier to find.

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jackgranatowski, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 26 minutes and 53 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7448c400-4bbc-4417-b6db-6c6423d48200

📥 Commits

Reviewing files that changed from the base of the PR and between f923417 and 5361b6d.

📒 Files selected for processing (15)
  • configurator/src/App.svelte
  • configurator/src/components/CommandPalette.svelte
  • configurator/src/components/DomainPanel.svelte
  • configurator/src/components/inputs/ClampField.svelte
  • configurator/src/components/panels/BordersPanel.svelte
  • configurator/src/components/panels/ColorsPanel.svelte
  • configurator/src/components/panels/LayoutPanel.svelte
  • configurator/src/components/panels/MacrosPanel.svelte
  • configurator/src/components/panels/MiscPanel.svelte
  • configurator/src/components/panels/SpacingPanel.svelte
  • configurator/src/components/panels/TypographyPanel.svelte
  • configurator/src/components/shell/PreviewPanel.svelte
  • configurator/src/components/shell/SidebarNav.svelte
  • configurator/src/lib/domains.ts
  • configurator/src/lib/lumlockerPreview.svelte.ts
📝 Walkthrough

Walkthrough

The configurator adds a Macros domain and panel, expands fluid-scale and layout controls, and updates color, border, misc, and preview surfaces for luminance lock and new live samples.

Changes

Macros domain wiring

Layer / File(s) Summary
Domain routing and navigation
configurator/src/lib/domains.ts, configurator/src/App.svelte, configurator/src/components/CommandPalette.svelte, configurator/src/components/shell/SidebarNav.svelte, configurator/src/components/DomainPanel.svelte
Macros domain matching, labels, navigation, and panel selection now route macro-related tokens to the macros domain.
Macros panel controls
configurator/src/components/panels/MacrosPanel.svelte
MacrosPanel renders flow, clamp, aspect, scroll-shadow, scrim, and prose token controls with previews.

Sizing and layout controls

Layer / File(s) Summary
ClampField and fluid scale
configurator/src/components/inputs/ClampField.svelte, configurator/src/components/panels/SpacingPanel.svelte, configurator/src/components/panels/TypographyPanel.svelte
ClampField now accepts custom min/max labels, and the spacing and typography panels use them for shared fluid-scale viewport endpoints.
More layout primitives
configurator/src/components/panels/LayoutPanel.svelte
LayoutPanel adds a collapsible section with additional layout token controls and preset ratios.

Styling previews and luminance lock

Layer / File(s) Summary
Luminance lock sync
configurator/src/lib/lumlockerPreview.svelte.ts, configurator/src/components/panels/ColorsPanel.svelte, configurator/src/components/shell/PreviewPanel.svelte
A shared luminance-lock toggle is wired through ColorsPanel and PreviewPanel.
Gradient and swatch previews
configurator/src/components/panels/ColorsPanel.svelte
ColorsPanel replaces the gradient metadata and preview rendering with structured gradient and swatch views.
Border and misc samples
configurator/src/components/panels/BordersPanel.svelte, configurator/src/components/panels/MiscPanel.svelte, configurator/src/components/shell/PreviewPanel.svelte
BordersPanel and MiscPanel add live samples, and PreviewPanel updates demo borders to use shared border-style variables.

Sequence Diagram(s)

sequenceDiagram
  participant ColorsPanel
  participant lumlockerPreview
  participant PreviewPanel
  participant PreviewIframeDocument
  ColorsPanel->>lumlockerPreview: set value
  PreviewPanel->>lumlockerPreview: read value
  PreviewPanel->>PreviewIframeDocument: set or remove data-lumlocker
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • codeslash-dev/SLASHED#302 — Adds the domain tab system and domain routing that this PR extends with a new macros domain.
  • codeslash-dev/SLASHED#423 — Updates token-to-domain categorization in CommandPalette.svelte, the same area expanded here for macros.
  • codeslash-dev/SLASHED#429 — Extends ClampField.svelte, which this PR also updates with shared min/max label props.

Suggested labels

codex

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main user-facing additions: luminance lock, gradient editing, macros controls, and layout primitives UI.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/configurator-refinements-z5javt

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

Configurator: luminance lock preview, gradient editor, macros and layout primitives UI
✨ Enhancement 🐞 Bug fix 🕐 40+ Minutes

Grey Divider

Description

• Add luminance lock controls with before/after swatches and live-canvas preview toggle.
• Replace gradient token list with a structured editor (space, angle/direction, stops, formula).
• Introduce a Macros domain/panel and expand Layout/Misc/Type/Space panels with richer controls.
Diagram

graph TD
  A["App / SidebarNav / CommandPalette"] --> B["DomainPanel"] --> C["ColorsPanel"]
  A --> B --> D["MacrosPanel"]
  A --> B --> E["LayoutPanel"]
  A --> B --> F["Typography & Spacing Panels"] --> G["ClampField"]
  C --> H(("lumlockerPreview store")) --> I["PreviewPanel (iframe root)"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Central token schema + generated editors
  • ➕ Reduces per-panel hardcoded token lists (gradients, prose spacing, layout primitives).
  • ➕ Makes it easier to add new tokens/domains without duplicating UI patterns.
  • ➕ Enables consistent help text/units/validation from a single source of truth.
  • ➖ More up-front abstraction work and indirection for a UI that is still evolving.
  • ➖ Harder to design bespoke previews (curve palette, scrim demo) purely from schema.
2. Svelte context/store for preview toggles (instead of module-level rune store)
  • ➕ Explicit dependency injection; easier to test and scope to configurator instance.
  • ➕ Avoids hidden global state if multiple configurators ever exist on one page.
  • ➖ Requires plumbing provider/consumer through shell components anyway.
  • ➖ Module-level store is simpler and sufficient if there is only one preview surface.
3. Extract gradient editor into a dedicated component per gradient
  • ➕ Keeps ColorsPanel smaller and improves maintainability/readability.
  • ➕ Allows unit-testing composition logic separately from the panel.
  • ➖ More components/files to manage; may slow iteration while UX is in flux.
  • ➖ Still needs shared state initialization and override wiring.

Recommendation: The current approach is reasonable for rapid configurator iteration: structured gradient edits + a module-level preview toggle keeps complexity localized and avoids invasive refactors. If gradient/macro/layout token UIs continue to grow, consider introducing a small token metadata registry (units, defaults, help strings, editor type) to reduce duplication while preserving custom previews where needed.

Files changed (15) +864 / -70

Enhancement (14) +855 / -67
App.svelteRoute macro-related overrides into the new macros domain +5/-2

Route macro-related overrides into the new macros domain

• Adds a Macros domain label and updates override-to-domain classification to recognize prose/flow/line-clamp/aspect/scroll-shadow/scrim/surface-color tokens as macros.

configurator/src/App.svelte

CommandPalette.svelteAdd Macros domain routing and keywords to the command palette +5/-2

Add Macros domain routing and keywords to the command palette

• Introduces macros-related keywords to map searches to the macros domain and adds a Macros label to the palette's domain list. Also removes scrim from the effects keyword set to avoid misrouting.

configurator/src/components/CommandPalette.svelte

DomainPanel.svelteWire MacrosPanel into domain rendering +3/-0

Wire MacrosPanel into domain rendering

• Imports the new MacrosPanel and renders it when the active domain is "macros".

configurator/src/components/DomainPanel.svelte

ClampField.svelteSupport custom endpoint labels for clamp editors +10/-6

Support custom endpoint labels for clamp editors

• Adds optional minLabel/maxLabel props and uses them in numeric inputs and previews. This enables Mobile/Desktop labeling for shared fluid endpoint controls.

configurator/src/components/inputs/ClampField.svelte

BordersPanel.svelteExpose border style and add a combined border preview +12/-0

Expose border style and add a combined border preview

• Tracks --sf-border-style and renders a preview showing border width × style × color together, making the effects of border tokens immediately visible.

configurator/src/components/panels/BordersPanel.svelte

ColorsPanel.svelteAdd luminance lock UI, palette/curve swatch preview, and gradient editor +236/-27

Add luminance lock UI, palette/curve swatch preview, and gradient editor

• Introduces a luminance lock section for brand colors with an OKLCH lightness slider, before/after swatches, and a live-canvas preview toggle via a shared store. Refactors gradients into a structured definition with per-gradient editable parts (space, angle/direction, stops) while still showing the framework-derived default formula and allowing raw overrides. Enhances previews: status colors render light/dark palette strips and the curve preview becomes a resolved palette swatch chart with light/dark toggle.

configurator/src/components/panels/ColorsPanel.svelte

LayoutPanel.svelteAdd a 'More layout primitives' section for additional layout tokens +166/-0

Add a 'More layout primitives' section for additional layout tokens

• Adds a collapsible section exposing UI controls for switcher threshold, sidebar content min width, equal grid min column, cover min height, frame ratio presets, reel item width presets, imposter margin, content/breakout widths, and alternate inner gap.

configurator/src/components/panels/LayoutPanel.svelte

MacrosPanel.svelteNew Macros panel for utility macro tokens with previews and presets +261/-0

New Macros panel for utility macro tokens with previews and presets

• Adds a new domain panel that edits macro tokens for flow rhythm, line clamp, aspect ratio, scroll shadow, scrim direction/color, and prose spacing/radius/marker color. Includes live preview blocks and preset buttons to make token effects immediately observable.

configurator/src/components/panels/MacrosPanel.svelte

MiscPanel.svelteAdd focus-ring preview and live caret/underline samples +39/-0

Add focus-ring preview and live caret/underline samples

• Adds a focus ring style preview box and a live input/link example to visualize caret color and underline offset/thickness. Includes a clarifying note about what renders in the live preview vs. static canvas.

configurator/src/components/panels/MiscPanel.svelte

SpacingPanel.svelteExpose gutter and shared mobile/desktop fluid viewport endpoints +29/-2

Expose gutter and shared mobile/desktop fluid viewport endpoints

• Adds a gutter slider for --sf-gutter and introduces a viewport range ClampField (Mobile → Desktop) shared with typography's fluid scale. Updates modular scale labeling to reflect endpoint-based configuration.

configurator/src/components/panels/SpacingPanel.svelte

TypographyPanel.svelteAdd shared mobile/desktop viewport range controls for modular type scale +22/-2

Add shared mobile/desktop viewport range controls for modular type scale

• Introduces shared fluid viewport endpoint controls (--sf-fluid-min-vw/--sf-fluid-max-vw) and updates the modular scale section to be explicitly Mobile → Desktop with clearer labeling and ClampField endpoint labels.

configurator/src/components/panels/TypographyPanel.svelte

PreviewPanel.svelteApply luminance lock preview to iframes and fix border style usage in templates +50/-25

Apply luminance lock preview to iframes and fix border style usage in templates

• Reads lumlockerPreview and mirrors it onto the preview iframe root via the data-lumlocker attribute in both single and split preview modes. Fixes template border syntax to include --sf-border-style and adds additional sample markup (form input, link, divider) to better surface caret, focus, underline, and divider behaviors in previews.

configurator/src/components/shell/PreviewPanel.svelte

SidebarNav.svelteAdd Macros to the sidebar navigation +2/-1

Add Macros to the sidebar navigation

• Adds a Macros nav item under tokens and switches icon imports to include Blocks for the new domain.

configurator/src/components/shell/SidebarNav.svelte

lumlockerPreview.svelte.tsAdd shared rune store for luminance lock preview toggle +15/-0

Add shared rune store for luminance lock preview toggle

• Creates a module-level $state store used to toggle luminance lock preview without prop drilling; ColorsPanel writes it and PreviewPanel reads it to update iframe root attributes.

configurator/src/lib/lumlockerPreview.svelte.ts

Other (1) +9 / -3
domains.tsIntroduce macros domain patterns and avoid substring collisions +9/-3

Introduce macros domain patterns and avoid substring collisions

• Adds a macros domain with specific token substrings and ensures it is evaluated before broad patterns like "color". Removes overlapping substrings (scrim/line-clamp/aspect) from other domains to improve routing accuracy.

configurator/src/lib/domains.ts

@coderabbitai coderabbitai Bot added the codex label Jun 27, 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): 6 rules

Grey Divider


Action required

1. Gradient editor overwrites overrides 🐞 Bug ≡ Correctness
Description
ColorsPanel’s structured gradient controls are always initialized from GRADIENT_TOKENS defaults, so
if a gradient token already has a raw override, using angle/dir/stop controls will overwrite it with
a new gradient composed from defaults. This can silently discard previously-authored gradient
overrides and makes the structured UI inconsistent with the current token value.
Code

configurator/src/components/panels/ColorsPanel.svelte[R180-195]

+  function gradEdit(g: GradientDef) {
+    if (!gradientEdits[g.name]) {
+      gradientEdits[g.name] = { space: g.space, kind: g.kind, angle: g.angle, dir: g.dir, stop1: g.stop1, stop2: g.stop2 };
+    }
+    return gradientEdits[g.name];
+  }
+  function composeGradient(g: GradientDef): string {
+    const e = gradEdit(g);
+    const head = e.kind === "dir" ? `to ${e.dir}` : `${e.angle}deg`;
+    return `linear-gradient(in ${e.space} ${head}, ${e.stop1}, ${e.stop2})`;
+  }
+  function setGradientPart(g: GradientDef, part: "angle" | "dir" | "stop1" | "stop2", value: string | number) {
+    const e = gradEdit(g);
+    (e as Record<string, string | number>)[part] = value;
+    onSet(g.name, composeGradient(g));
+  }
Relevance

⭐⭐⭐ High

Team accepted prior fixes preventing ColorsPanel controls overwriting/desyncing existing overrides
(Auto-dark). Similar risk here.

PR-#423
PR-#428

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The editor model is seeded from hard-coded defaults (not overrides[g.name]), but structured
controls always call onSet(g.name, composeGradient(g)), which rebuilds the entire gradient from
that model and overwrites any existing raw override value.

configurator/src/components/panels/ColorsPanel.svelte[170-195]
configurator/src/components/panels/ColorsPanel.svelte[653-737]

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 structured gradient editor state (`gradientEdits`) is lazily seeded only from `GRADIENT_TOKENS` defaults. When `g.name` already exists in `overrides`, any structured edit (angle/dir/stops) calls `onSet(g.name, composeGradient(g))` and replaces the user’s existing raw override with a new gradient built from defaults.

### Issue Context
- `gradEdit()` initializes the editor model from `g.space/g.kind/g.angle/...` only.
- The UI always renders structured controls and wires them to `setGradientPart()` regardless of whether `overrides[g.name]` already exists.

### Fix Focus Areas
- configurator/src/components/panels/ColorsPanel.svelte[175-195]
- configurator/src/components/panels/ColorsPanel.svelte[653-737]

### Suggested fix
- When initializing `gradientEdits[g.name]`, prefer hydrating from `overrides[g.name]` if present:
 - If the override matches a parseable pattern (e.g. `linear-gradient(in <space> <angle|to dir>, <stop1>, <stop2>)`), parse out `space`, `angle`/`dir`, and the first two stops.
 - If it is not parseable (or has more than 2 stops), either:
   - disable structured controls with a clear message (“Raw override active; structured editor unavailable”), or
   - add an explicit button (“Adopt raw override into structured editor”) that attempts a best-effort parse.
- Ensure clicking “reset” also resets/removes the corresponding `gradientEdits[g.name]` entry so the editor re-seeds correctly next time.

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



Remediation recommended

2. Lumlock preview ignores hex 🐞 Bug ≡ Correctness
Description
The luminance-lock swatch preview only applies locking when the source is an oklch(...) literal; for
hex/rgb/var() inputs it returns the original value unchanged, so “Locked” can incorrectly match
“Current”. This diverges from the actual framework lumlocker, which computes the lock via
oklch(from <color> ...) and works with any valid CSS color input.
Code

configurator/src/components/panels/ColorsPanel.svelte[R198-205]

+  function lockedColor(colorKey: string, side: "light" | "dark"): string {
+    const srcName = `--sf-color-${colorKey}-source-${side}`;
+    const src = overrides[srcName] ?? BRAND_SOURCES.find(s => s.name === srcName)?.default ?? "";
+    const { l, c, h, valid } = parseOklch(src);
+    if (!valid) return src;
+    void l;
+    return stringifyOklch(lumlockerL, c, h);
+  }
Relevance

⭐⭐⭐ High

Team values preview/runtime parity in ColorsPanel; accepted aligning derivations and resolved
previews, so this mismatch likely fixed.

PR-#428
PR-#429
PR-#430

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
lockedColor() only locks when parseOklch(src) succeeds, but parseOklch() only matches
oklch(...) strings. The core lumlocker is implemented with oklch(from ...), which supports
arbitrary valid CSS colors, so the preview can be wrong for non-OKLCH inputs.

configurator/src/components/panels/ColorsPanel.svelte[197-205]
configurator/src/lib/colorUtils.ts[83-96]
core/themes.css[23-56]

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

### Issue description
`lockedColor()` uses `parseOklch(src)` and bails out (returns `src`) when parsing fails. Since `parseOklch()` only recognizes `oklch(...)` strings, the preview will not show the locked result for valid non-OKLCH inputs (hex/rgb/var()), even though the framework’s lumlocker uses `oklch(from ...)` and will lock those correctly.

### Issue Context
- `parseOklch()` is regex-based and only supports `oklch(...)`.
- Framework lumlocker logic in `core/themes.css` uses `oklch(from var(--sf-color-*-source-*) var(--sf-lumlocker) c h)`.

### Fix Focus Areas
- configurator/src/components/panels/ColorsPanel.svelte[197-205]
- configurator/src/lib/colorUtils.ts[83-96]
- core/themes.css[23-56]

### Suggested fix
- Avoid parsing OKLCH in JS for the preview. Instead, build the same CSS expression the framework uses and let `paint()/resolveColor()` compute it:
 - Return something like: `oklch(from ${src} ${lumlockerL} c h)` (or `oklch(from ${src} var(--sf-lumlocker) c h)` if you want it tied strictly to the token).
- This makes the preview accurate for any valid CSS color input that the browser can resolve, including hex/rgb/var() and derived expressions.

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


Grey Divider

Qodo Logo

Comment on lines +180 to +195
function gradEdit(g: GradientDef) {
if (!gradientEdits[g.name]) {
gradientEdits[g.name] = { space: g.space, kind: g.kind, angle: g.angle, dir: g.dir, stop1: g.stop1, stop2: g.stop2 };
}
return gradientEdits[g.name];
}
function composeGradient(g: GradientDef): string {
const e = gradEdit(g);
const head = e.kind === "dir" ? `to ${e.dir}` : `${e.angle}deg`;
return `linear-gradient(in ${e.space} ${head}, ${e.stop1}, ${e.stop2})`;
}
function setGradientPart(g: GradientDef, part: "angle" | "dir" | "stop1" | "stop2", value: string | number) {
const e = gradEdit(g);
(e as Record<string, string | number>)[part] = value;
onSet(g.name, composeGradient(g));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Gradient editor overwrites overrides 🐞 Bug ≡ Correctness

ColorsPanel’s structured gradient controls are always initialized from GRADIENT_TOKENS defaults, so
if a gradient token already has a raw override, using angle/dir/stop controls will overwrite it with
a new gradient composed from defaults. This can silently discard previously-authored gradient
overrides and makes the structured UI inconsistent with the current token value.
Agent Prompt
### Issue description
The structured gradient editor state (`gradientEdits`) is lazily seeded only from `GRADIENT_TOKENS` defaults. When `g.name` already exists in `overrides`, any structured edit (angle/dir/stops) calls `onSet(g.name, composeGradient(g))` and replaces the user’s existing raw override with a new gradient built from defaults.

### Issue Context
- `gradEdit()` initializes the editor model from `g.space/g.kind/g.angle/...` only.
- The UI always renders structured controls and wires them to `setGradientPart()` regardless of whether `overrides[g.name]` already exists.

### Fix Focus Areas
- configurator/src/components/panels/ColorsPanel.svelte[175-195]
- configurator/src/components/panels/ColorsPanel.svelte[653-737]

### Suggested fix
- When initializing `gradientEdits[g.name]`, prefer hydrating from `overrides[g.name]` if present:
  - If the override matches a parseable pattern (e.g. `linear-gradient(in <space> <angle|to dir>, <stop1>, <stop2>)`), parse out `space`, `angle`/`dir`, and the first two stops.
  - If it is not parseable (or has more than 2 stops), either:
    - disable structured controls with a clear message (“Raw override active; structured editor unavailable”), or
    - add an explicit button (“Adopt raw override into structured editor”) that attempts a best-effort parse.
- Ensure clicking “reset” also resets/removes the corresponding `gradientEdits[g.name]` entry so the editor re-seeds correctly next time.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 5361b6d. gradEdit() now calls parseLinearGradient(overrides[g.name]) on first access and seeds the editor from the parsed values if the override is a recognisable linear-gradient(in <space> <head>, <stop1>, <stop2>) expression; falls back to the token defaults only if parsing fails. The reset button and raw-override input both call delete gradientEdits[g.name] so the editor re-seeds correctly from the new value next time a structured control is touched.


Generated by Claude Code

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
configurator/src/App.svelte (1)

35-50: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reuse the shared domain matcher here.

overridesByDomain() has already drifted from configurator/src/lib/domains.ts: overrides like --sf-gradient-*, --sf-lumlocker-*, and --sf-palette-mix-* are counted as misc here even though the canonical patterns classify them as colors. That makes the sidebar badges disagree with actual panel routing.

🤖 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/App.svelte` around lines 35 - 50, The domain grouping logic
in overridesByDomain() has drifted from the canonical matcher in
configurator/src/lib/domains.ts, causing some color-related overrides to be
counted as misc. Update App.svelte to reuse the shared domain matcher instead of
duplicating keyword checks, so overrides like the gradient/lumlocker/palette-mix
patterns are classified the same way as the panel routing.
configurator/src/components/CommandPalette.svelte (1)

16-36: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep palette routing in sync with domainOf().

This local map omits patterns that exist in configurator/src/lib/domains.ts (for example gradient, lumlocker, palette-mix, and contrast). Search hits for those tokens will be labeled as Misc, and pressing Enter will open the wrong panel.

🤖 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` around lines 16 - 36, The
command palette routing in inferDomain() is out of sync with domainOf(), so some
searches are classified as Misc and open the wrong panel. Update the local
DOMAIN_MAP and matching logic in CommandPalette.svelte to cover the same token
patterns handled in configurator/src/lib/domains.ts, especially gradient,
lumlocker, palette-mix, and contrast. Make sure the inferred domain returned by
inferDomain() matches the shared domain mapping so Enter routes to the correct
panel.
🤖 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/panels/ColorsPanel.svelte`:
- Around line 421-427: The “Current” swatch in ColorsPanel is being derived from
the live preview state via paintTheme(), so it picks up the canvas lock when
lumlockerPreview.value enables data-lumlocker in PreviewPanel. Update the
left-hand swatch logic to use the unlocked source token value instead of reading
from the iframe preview, while keeping the right-hand swatch driven by
lockedColor()/paint() so the before/after comparison stays visible.
- Around line 678-735: The structured gradient editor in ColorsPanel.svelte is
keeping a stale cached value in gradEdit()/gradientEdits[g.name] after
onReset(g.name) or raw override changes. Update the reset/raw-override flow so
the cached gradient edit state is cleared or reseeded from the current override
before any further structured edits, ensuring the angle/direction/stop controls
always reflect the latest gradient formula.

In `@configurator/src/components/panels/LayoutPanel.svelte`:
- Around line 305-312: The toggle in LayoutPanel.svelte for “More layout
primitives” currently only changes visually; update the button to expose the
disclosure state for assistive tech. In the button markup tied to showMore, add
aria-expanded bound to showMore and aria-controls pointing to the collapsible
content container, and make sure the controlled section has a matching id so the
relationship is explicit.

In `@configurator/src/components/panels/MacrosPanel.svelte`:
- Around line 171-178: The scrim and marker text inputs in MacrosPanel are using
onchange, so updates only apply on blur/Enter instead of live while typing.
Update the input handlers in the affected text controls (and the matching one
later in the same component) to use oninput, keeping their behavior consistent
with the sliders and preset buttons via onSet and onReset.

---

Outside diff comments:
In `@configurator/src/App.svelte`:
- Around line 35-50: The domain grouping logic in overridesByDomain() has
drifted from the canonical matcher in configurator/src/lib/domains.ts, causing
some color-related overrides to be counted as misc. Update App.svelte to reuse
the shared domain matcher instead of duplicating keyword checks, so overrides
like the gradient/lumlocker/palette-mix patterns are classified the same way as
the panel routing.

In `@configurator/src/components/CommandPalette.svelte`:
- Around line 16-36: The command palette routing in inferDomain() is out of sync
with domainOf(), so some searches are classified as Misc and open the wrong
panel. Update the local DOMAIN_MAP and matching logic in CommandPalette.svelte
to cover the same token patterns handled in configurator/src/lib/domains.ts,
especially gradient, lumlocker, palette-mix, and contrast. Make sure the
inferred domain returned by inferDomain() matches the shared domain mapping so
Enter routes to the correct panel.
🪄 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: bcadf442-d423-418c-8044-cde08c2afd0f

📥 Commits

Reviewing files that changed from the base of the PR and between 477856b and f923417.

📒 Files selected for processing (15)
  • configurator/src/App.svelte
  • configurator/src/components/CommandPalette.svelte
  • configurator/src/components/DomainPanel.svelte
  • configurator/src/components/inputs/ClampField.svelte
  • configurator/src/components/panels/BordersPanel.svelte
  • configurator/src/components/panels/ColorsPanel.svelte
  • configurator/src/components/panels/LayoutPanel.svelte
  • configurator/src/components/panels/MacrosPanel.svelte
  • configurator/src/components/panels/MiscPanel.svelte
  • configurator/src/components/panels/SpacingPanel.svelte
  • configurator/src/components/panels/TypographyPanel.svelte
  • configurator/src/components/shell/PreviewPanel.svelte
  • configurator/src/components/shell/SidebarNav.svelte
  • configurator/src/lib/domains.ts
  • configurator/src/lib/lumlockerPreview.svelte.ts

Comment thread configurator/src/components/panels/ColorsPanel.svelte Outdated
Comment thread configurator/src/components/panels/ColorsPanel.svelte
Comment thread configurator/src/components/panels/LayoutPanel.svelte
Comment thread configurator/src/components/panels/MacrosPanel.svelte
@jackgranatowski
jackgranatowski force-pushed the claude/configurator-refinements-z5javt branch from f923417 to 28d8a04 Compare June 27, 2026 11:53
… layout UI

1. Colors — LumLocker section: --sf-lumlocker slider, before/after
   swatches for every lockable brand color (primary/secondary/tertiary/
   action, light+dark), and a "Preview LumLocker on canvas" toggle
   driving data-lumlocker on the preview iframe via a shared store.
   Source swatches read from -source-<side> tokens so the before/after
   table stays correct when LumLocker is active on canvas. LumLocker
   preview uses CSS oklch(from <src> L c h) — works for any valid CSS
   color input, matching the framework formula exactly.
2. Colors — Shade/tint curve preview is now a live palette: bars filled
   with the resolved primary-NNN swatch (light/dark toggle).
3. Colors — Gradients gain structured controls (angle/direction + colour
   stops) plus the visible default formula. Editor seeds from existing
   override if parseable; reset and raw-override changes clear the cache
   so structured controls always reflect the current value.
4. Colors — Status colors show their constrained light + dark palette
   strips.
5. Typography + Spacing — Utopia-style modular scale: viewport / base /
   ratio exposed for both Mobile (min) and Desktop (max) endpoints;
   ClampField gains minLabel/maxLabel.
6. Spacing — exposes the parent --sf-gutter token.
7. Layout — "More layout primitives" group (switcher, sidebar content
   min, equal grid, cover, frame ratio, reel, imposter, content-grid,
   alternate) with aria-expanded/aria-controls on the toggle button.
8. New Macros panel + sidebar/nav/command-palette wiring and macros
   domain patterns (prose, flow, line-clamp, aspect, scroll-shadow,
   scrim, surface). Scrim/marker text inputs use oninput for live
   updates. overridesByDomain() delegates to shared domainOf() from
   domains.ts. CommandPalette DOMAIN_MAP synced with domains.ts
   (gradient/lumlocker/palette-mix/contrast now route to colors).
9. Borders — preview templates now honour var(--sf-border-style) and
   include a divider element; in-panel border sample reflects width x
   style x colour.
10. Misc — focus-ring-style, caret and link-underline previews in-panel,
    plus a form/links block and divider in the Components preview
    template.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBrDq4qYBGC9Sa611kGLtX
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