Add luminance lock, gradient editor, macros panel, and layout primitives UI - #431
Conversation
|
Warning Review limit reached
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 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (15)
📝 WalkthroughWalkthroughThe 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. ChangesMacros domain wiring
Sizing and layout controls
Styling previews and luminance lock
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoConfigurator: luminance lock preview, gradient editor, macros and layout primitives UI Description
Diagram
High-Level Assessment
Files changed (15)
|
Code Review by Qodo
Context used✅ Compliance rules (platform):
6 rules 1. Gradient editor overwrites overrides
|
| 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)); | ||
| } |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 winReuse the shared domain matcher here.
overridesByDomain()has already drifted fromconfigurator/src/lib/domains.ts: overrides like--sf-gradient-*,--sf-lumlocker-*, and--sf-palette-mix-*are counted asmischere even though the canonical patterns classify them ascolors. 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 winKeep palette routing in sync with
domainOf().This local map omits patterns that exist in
configurator/src/lib/domains.ts(for examplegradient,lumlocker,palette-mix, andcontrast). Search hits for those tokens will be labeled asMisc, 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
📒 Files selected for processing (15)
configurator/src/App.svelteconfigurator/src/components/CommandPalette.svelteconfigurator/src/components/DomainPanel.svelteconfigurator/src/components/inputs/ClampField.svelteconfigurator/src/components/panels/BordersPanel.svelteconfigurator/src/components/panels/ColorsPanel.svelteconfigurator/src/components/panels/LayoutPanel.svelteconfigurator/src/components/panels/MacrosPanel.svelteconfigurator/src/components/panels/MiscPanel.svelteconfigurator/src/components/panels/SpacingPanel.svelteconfigurator/src/components/panels/TypographyPanel.svelteconfigurator/src/components/shell/PreviewPanel.svelteconfigurator/src/components/shell/SidebarNav.svelteconfigurator/src/lib/domains.tsconfigurator/src/lib/lumlockerPreview.svelte.ts
f923417 to
28d8a04
Compare
… 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
28d8a04 to
5361b6d
Compare
Summary
This PR adds three major UI features to the configurator and enhances existing panels with more granular controls:
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.
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.
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.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-lumlockerslider, color preview grid, and canvas toggle. Refactored gradient tokens into structuredGradientDeftype 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
minLabelandmaxLabelprops for customizable field labels.PreviewPanel.svelte: Integrated
lumlockerPreviewstore to apply/removedata-lumlockerattribute 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
Blocksfor macros domain.domains.ts: Added
macrosdomain with token patterns, ordered first to prevent substring collisions.App.svelte & CommandPalette.svelte: Added macros domain to navigation and command palette routing.
Implementation Details
parseOklchandstringifyOklchto preserve chroma and hue while remapping lightness.paintTheme) with height bars representing mix percentages, doubling as a live palette.https://claude.ai/code/session_01JBrDq4qYBGC9Sa611kGLtX
Summary by CodeRabbit
New Features
Improvements