Skip to content

UI: Add Heading/Radius editors, ContainerBars, expanded Preview sections, shade strip and fold-state persistence - #398

Merged
jackgranatowski merged 4 commits into
mainfrom
codex/fix-pr-397
Jun 24, 2026
Merged

UI: Add Heading/Radius editors, ContainerBars, expanded Preview sections, shade strip and fold-state persistence#398
jackgranatowski merged 4 commits into
mainfrom
codex/fix-pr-397

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Motivation

  • Improve the configurator UX by exposing richer, focused editors for headings and radii, inline visual helpers for brand shades and container widths, and make large generator blocks collapsible with persisted state.
  • Surface more preview sections (gradients, spacing, borders, shadows, motion, effects) so token changes can be inspected in more realistic contexts.

Description

  • Added three new components: HeadingEditor.svelte, RadiusEditor.svelte, and ContainerBars.svelte to provide dedicated UIs for typography headings, radius specimens, and container-width comparison bars respectively.
  • Enhanced BrandColorRow.svelte to render a 7-step inline shade strip; it now imports measureBackground/setProbeContext and tracks ui.previewTheme to measure effective shade colors.
  • Made ScaleGenerator.svelte optionally collapsible and persistent by introducing foldState.js and wiring getFold/setFold to store open/closed state; added a toggle UI and conditional rendering when collapsed.
  • Plumbed foldState into SmartSettings.svelte so section <details> state is persisted across reloads.
  • Reworked DomainPanel.svelte flow to replace the old inline preview with the new editors and curated cards (and to place generators and quick knobs differently), and added imports for the new components.
  • Expanded Preview.svelte with new sections and a DOMAIN_TO_SECTION mapping so the preview tab follows the active domain; added many UI blocks for gradients, spacing, borders, shadows, motion, and effects.
  • Added persistent fold-state helper src/lib/foldState.js to store UI open/closed state in localStorage.
  • Updated distribution badge JSON (dist/badge-essential.json, dist/badge-optimal.json) with new gzip size values.

Testing

  • Ran the project build (npm run build) which completed successfully.
  • Executed the test suite (npm test) and the tests passed.

Codex Task

Summary by CodeRabbit

  • New Features

    • Added new visual editors for heading, radius, and container sizing tokens.
    • Expanded the preview area with more sections for gradients, spacing, borders, shadows, motion, and effects.
    • Added inline shade strips and proportional container bars for easier token inspection.
  • Bug Fixes

    • Moved live preview content into the main preview hub for clearer navigation.
    • Added saved expand/collapse state for settings panels and generators.

@coderabbitai

coderabbitai Bot commented Jun 24, 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 24 minutes and 18 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: a289879c-4b40-434d-9fe7-7b03dd5eadbb

📥 Commits

Reviewing files that changed from the base of the PR and between 4529781 and 9edd540.

📒 Files selected for processing (5)
  • configurator/src/components/BrandColorRow.svelte
  • configurator/src/components/HeadingEditor.svelte
  • configurator/src/components/Preview.svelte
  • configurator/src/components/RadiusEditor.svelte
  • configurator/tests-e2e/domain-preview.spec.js
📝 Walkthrough

Walkthrough

Adds foldState.js for localStorage-backed collapse persistence, wires it into ScaleGenerator (new collapsible prop) and SmartSettings. Introduces three new domain editor components (HeadingEditor, RadiusEditor, ContainerBars) and an inline shade strip in BrandColorRow. Restructures DomainPanel to route to these editors and relocates live preview to an expanded Preview.svelte hub with six new sections (gradients, spacing, borders, shadows, motion, effects).

Changes

Configurator domain editors and Preview Hub expansion

Layer / File(s) Summary
Fold-state persistence module and consumers
configurator/src/lib/foldState.js, configurator/src/components/ScaleGenerator.svelte, configurator/src/components/SmartSettings.svelte
foldState.js adds getFold/setFold backed by localStorage. ScaleGenerator gains a collapsible prop with persisted open state and a toggle button. SmartSettings replaces static open attributes with fold-driven controlled values.
HeadingEditor tabbed typography inspector
configurator/src/components/HeadingEditor.svelte
New component with tabs for all, h1h6, body, and mono views; derives stageStyle, filters token rows to existing tokens, and shows per-tab override-dot indicators.
RadiusEditor tabbed radius-token inspector
configurator/src/components/RadiusEditor.svelte
New component with a tab strip for radius levels; renders an "all" overview with specimen tiles, and per-level views with a large specimen, mini level-switcher buttons, and a TokenRow.
ContainerBars proportional token visualizer
configurator/src/components/ContainerBars.svelte
New component that parses container CSS token values into approximate pixel widths and renders proportional bars with labels, override highlighting, and pixel readouts.
BrandColorRow inline shade strip
configurator/src/components/BrandColorRow.svelte
Adds a shadeColors effect that probes --sf-color-{colorKey}{suffix} via measureBackground and renders a 7-swatch strip with tooltips and empty-state styling beneath the dark editor.
DomainPanel: new editors replace inline preview
configurator/src/components/DomainPanel.svelte
Removes DomainPreview/DOMAIN_PREVIEWS and the inline preview disclosure. Zone 1 becomes controls-first. Adds domain branches routing typographyHeadingEditor, bordersRadiusEditor, layoutContainerBars.
Preview Hub: six new sections and domain routing
configurator/src/components/Preview.svelte
Extends SECTIONS with gradients and reorders items; expands DOMAIN_TO_SECTION for additional domains; adds rendered content blocks and CSS for gradients, spacing, borders, shadows, motion, and effects.
E2E tests: Preview Hub routing and ContainerBars validation
configurator/tests-e2e/domain-preview.spec.js
Rewrites tests to assert Preview Hub section routing per domain, validates .cbars__bar proportional widths for layout, and updates live override test to target .preview__stage.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • codeslash-dev/SLASHED#371: Introduced the inline DomainPreview accordion logic wired to DOMAIN_PREVIEWS in DomainPanel.svelte that this PR removes and relocates to the Preview Hub.
  • codeslash-dev/SLASHED#328: Both PRs restructure Preview.svelte's tabbed section navigation (SECTIONS, activeSection, domain routing) and add new rendered section blocks.
  • codeslash-dev/SLASHED#382: Introduced SmartSettings.svelte panels that this PR now wires to fold-state persistence via getFold/setFold.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main UI additions and persistence changes in the pull request.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-pr-397

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

UI: Add Heading/Radius editors, ContainerBars, shade strip, and fold-state persistence
✨ Enhancement 🕐 40+ Minutes

Grey Divider

Description

• Adds three new Svelte components: HeadingEditor (tabbed per-level typography editor with live
 specimens), RadiusEditor (tabbed radius editor with shape previews), and ContainerBars
 (proportional width comparison bars for container tokens).
• Enhances BrandColorRow with a 7-step inline shade strip that measures live effective colors via
 measureBackground/setProbeContext.
• Introduces foldState.js for localStorage-backed open/closed persistence, wired into
 ScaleGenerator (new collapsible prop) and SmartSettings section `` elements.
• Reworks DomainPanel to replace the old inline DomainPreview with the new focused editors and
 routes generator/knob layout accordingly.
• Expands Preview.svelte with six new sections (Gradients, Spacing, Borders, Shadows, Motion,
 Effects) and a DOMAIN_TO_SECTION map that auto-scrolls the preview tab to match the active domain.
Diagram

graph TD
  DP["DomainPanel.svelte"] --> HE["HeadingEditor.svelte"]
  DP --> RE["RadiusEditor.svelte"]
  DP --> CB["ContainerBars.svelte"]
  DP --> SG["ScaleGenerator.svelte"]
  DP --> SS["SmartSettings.svelte"]
  SG --> FS["foldState.js"]
  SS --> FS
  BCR["BrandColorRow.svelte"] --> PH(["probeHost.js"])
  PV["Preview.svelte"] --> STORE[("store.svelte.js")]
  DP --> STORE
  HE --> STORE
  CB --> STORE
  FS --> LST[("localStorage")]

  subgraph Legend
    direction LR
    _f["Svelte Component"] ~~~ _s(["JS Module"]) ~~~ _db[("Storage")]
  end
Loading
High-Level Assessment

The PR's approach is well-suited to the problem. Dedicated per-domain editor components (HeadingEditor, RadiusEditor, ContainerBars) give users focused, contextual UIs without bloating a single panel. The lightweight foldState.js module (outside Svelte reactivity) is the right call for cross-file persistence without coupling to the store. Alternatives considered: (1) storing fold state in the Svelte store — rejected because it would reset on page reload without extra serialization logic; (2) using a single mega-panel with all token rows — rejected as it was the previous approach and lacked visual context. The chosen decomposition is clean and extensible.

Files changed (11) +1071 / -43

Enhancement (4) +479 / -5
BrandColorRow.svelteAdd 7-step inline shade strip with live color measurement +52/-2

Add 7-step inline shade strip with live color measurement

• Imports 'measureBackground'/'setProbeContext' and the 'ui' store to reactively measure effective shade colors. Renders a full-width 7-swatch strip below each brand color row, showing the superlight→superdark ramp with a checkerboard fallback for unmeasured slots. Grid layout gains a second row to accommodate the strip.

configurator/src/components/BrandColorRow.svelte

Preview.svelteAdd Gradients, Spacing, Borders, Shadows, Motion, and Effects preview sections +399/-1

Add Gradients, Spacing, Borders, Shadows, Motion, and Effects preview sections

• Adds six new named sections to the preview tab, each with multiple 'pv__block' subsections demonstrating the relevant tokens on real UI patterns (elevation cards, blur boxes, motion animations, scrim overlays, etc.). Introduces a 'DOMAIN_TO_SECTION' map and a '$effect' that auto-switches the active section when the user changes domain in the left panel.

configurator/src/components/Preview.svelte

ScaleGenerator.svelteAdd optional collapsible mode with persisted fold state +22/-1

Add optional collapsible mode with persisted fold state

• Adds a 'collapsible' prop; when true, a chevron toggle button is rendered in the header and the body is conditionally shown. Open/closed state is read from and written to 'foldState.js' using a 'generator:<kind>' key.

configurator/src/components/ScaleGenerator.svelte

SmartSettings.sveltePersist section <details> open/closed state via foldState +6/-1

Persist section <details> open/closed state via foldState

• Replaces the hardcoded 'open' attribute on each '<details>' element with 'getFold(domainId:sectionId, false)' for initial state and an 'ontoggle' handler that calls 'setFold' on change.

configurator/src/components/SmartSettings.svelte

Refactor (1) +28 / -36
DomainPanel.svelteReplace inline DomainPreview with focused domain editors and restructured zones +28/-36

Replace inline DomainPreview with focused domain editors and restructured zones

• Removes 'DomainPreview' and its 'showPreview' state; imports 'HeadingEditor', 'RadiusEditor', and 'ContainerBars'. Rewrites Zone 1 so typography uses 'HeadingEditor', borders use 'RadiusEditor', layout uses 'ContainerBars', and generator domains render a collapsible 'ScaleGenerator'. Simplifies the knob placement logic.

configurator/src/components/DomainPanel.svelte

Other (6) +564 / -2
ContainerBars.svelteNew component: proportional container-width comparison bars +117/-0

New component: proportional container-width comparison bars

• New component that renders a labeled bar chart for the five '--sf-container-*' tokens, converting rem/ch/em/% values to approximate pixel widths and scaling bars proportionally. Modified tokens are highlighted with an accent inset shadow.

configurator/src/components/ContainerBars.svelte

HeadingEditor.svelteNew component: tabbed heading/body/mono typography editor with live specimens +203/-0

New component: tabbed heading/body/mono typography editor with live specimens

• New tabbed editor covering H1–H6, Body, and Mono tabs. Each tab shows a live specimen rendered with the active token values and a list of 'TokenRow' inputs. The 'All' tab renders all heading levels plus a body paragraph in a single specimen. Modified tabs display a dot indicator.

configurator/src/components/HeadingEditor.svelte

RadiusEditor.svelteNew component: tabbed radius editor with live shape specimens +197/-0

New component: tabbed radius editor with live shape specimens

• New tabbed editor for the 10 '--sf-radius-*' levels (2xs through full). The 'All' tab shows all shapes side-by-side; individual tabs show a larger specimen plus a mini-button row for quick navigation. Includes a 'TokenRow' for the global '--sf-radius-scale' multiplier.

configurator/src/components/RadiusEditor.svelte

foldState.jsNew utility: localStorage-backed fold-state persistence helper +45/-0

New utility: localStorage-backed fold-state persistence helper

• Provides 'getFold(key, default)' and 'setFold(key, open)' backed by a single 'localStorage' entry ('slashed-configurator/fold/v1'). Initialised eagerly at module load; deliberately outside Svelte's reactivity system for portability.

configurator/src/lib/foldState.js

badge-essential.jsonUpdate essential bundle gzip size badge +1/-1

Update essential bundle gzip size badge

• Updates the displayed gzip size from 56.2 kB to 15.6 kB.

dist/badge-essential.json

badge-optimal.jsonUpdate optimal bundle gzip size badge +1/-1

Update optimal bundle gzip size badge

• Updates the displayed gzip size from 64.1 kB to 17.9 kB.

dist/badge-optimal.json

@qodo-code-review

qodo-code-review Bot commented Jun 24, 2026

Copy link
Copy Markdown

Code Review by Qodo

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

Context used
✅ Compliance rules (platform): 5 rules

Grey Divider


Remediation recommended

1. Unmapped preview domains ✓ Resolved 🐞 Bug ≡ Correctness
Description
Preview’s domain→section sync only updates activeSection when ui.domain exists in
DOMAIN_TO_SECTION; tool domains like themes, setup, wcag, and cheatsheet are valid
ui.domain values but are unmapped. Navigating to these domains can leave the preview stuck showing
an unrelated prior section.
Code

configurator/src/components/Preview.svelte[R62-81]

+  const DOMAIN_TO_SECTION = {
+    home:       'overview',
+    colors:     'colors',
+    gradients:  'gradients',
+    typography: 'typography',
+    spacing:    'spacing',
+    layout:     'layout',
+    borders:    'borders',
+    shadows:    'shadows',
+    motion:     'motion',
+    effects:    'effects',
+    misc:       'tokens',
+  };
+
  let activeSection = $state('overview');

+  $effect(() => {
+    const sec = DOMAIN_TO_SECTION[ui.domain];
+    if (sec) activeSection = sec;
+  });
Relevance

⭐⭐ Medium

No historical evidence on DOMAIN_TO_SECTION handling tool domains; preview-domain sync not
previously reviewed.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Preview.svelte only updates activeSection when DOMAIN_TO_SECTION[ui.domain] is defined, but
domains.js defines additional domain ids (tool domains) that can become ui.domain and are not
mapped.

configurator/src/components/Preview.svelte[46-82]
configurator/src/lib/domains.js[376-402]
configurator/src/lib/store.svelte.js[40-49]

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

### Issue description
Preview auto-section selection doesn’t handle all valid `ui.domain` values; for unmapped domains the preview retains the previous section.

### Issue Context
`ui.domain` includes tool domains (wcag/themes/setup/cheatsheet). The preview should either map these explicitly or fall back deterministically (e.g., overview).

### Fix
Add missing domain mappings and/or set a safe default when `DOMAIN_TO_SECTION[ui.domain]` is undefined.

### Fix Focus Areas
- configurator/src/components/Preview.svelte[62-82]
- configurator/src/lib/domains.js[376-402]

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



Informational

2. FoldState special-key hazard ✓ Resolved 🐞 Bug ⛨ Security
Description
foldState.js loads _state from JSON into a normal object and uses key in _state plus
_state[key] = open; special keys like __proto__ can behave unexpectedly on normal objects. This
is low-risk today (keys appear internal), but it’s fragile and easy to harden.
Code

configurator/src/lib/foldState.js[R12-35]

+const FOLD_KEY = 'slashed-configurator/fold/v1';
+
+let _state = {};
+
+if (typeof localStorage !== 'undefined') {
+  try {
+    const raw = localStorage.getItem(FOLD_KEY);
+    if (raw) { const parsed = JSON.parse(raw); if (parsed && typeof parsed === 'object') _state = parsed; }
+  } catch { /* ignore */ }
+}
+
+function _persist() {
+  try { localStorage.setItem(FOLD_KEY, JSON.stringify(_state)); } catch { /* ignore */ }
+}
+
+/**
+ * Read the persisted open state for a section.
+ * @param {string} key     Unique section identifier.
+ * @param {boolean} [def]  Returned when no stored value exists (default false).
+ * @returns {boolean}
+ */
+export function getFold(key, def = false) {
+  return key in _state ? Boolean(_state[key]) : def;
+}
Relevance

⭐⭐ Medium

No historical evidence on hardening localStorage JSON objects against __proto__/prototype pollution;
acceptance unclear.

PR-#303

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
foldState assigns parsed JSON directly to _state, checks membership via key in _state, and
writes via _state[key] = open, all of which are sensitive to special keys on normal objects.

configurator/src/lib/foldState.js[12-45]

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

### Issue description
foldState persists into a plain object and uses `key in _state` and `_state[key] = ...`, which has special-key edge cases on normal objects.

### Issue Context
While current callers supply internal keys, hardening avoids future regressions if keys ever become influenced by user input or corrupted storage.

### Fix
Store fold state in a null-prototype map and use an own-property check (e.g., `Object.hasOwn`). Optionally ignore dangerous keys on set.

### Fix Focus Areas
- configurator/src/lib/foldState.js[14-45]

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


3. SmartSettings default closed ✓ Resolved 🐞 Bug ≡ Correctness
Description
SmartSettings and ScaleGenerator now bind <details>/collapsible UI to `open={getFold(...,
false)}, which evaluates to false` when there is no persisted localStorage entry and causes these
sections to render collapsed on first visit. Because DomainPanel enables collapsible for
generator domains (typography/spacing), this behavior change broadly hides key editing controls by
default compared to the prior always-open UI.
Code

configurator/src/components/SmartSettings.svelte[R69-73]

+      <details
+        class="cfg-card smart__section"
+        open={getFold(`${domainId}:${section.id}`, false)}
+        ontoggle={(e) => setFold(`${domainId}:${section.id}`, e.currentTarget.open)}
+      >
Relevance

⭐ Low

ScaleGenerator previously changed to start collapsed by default as intended UX; likely keep
collapsed-by-default behavior.

PR-#337

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The cited components read fold state via getFold(..., false) and use that value directly to
determine whether the <details> (in SmartSettings) or the generator body (in ScaleGenerator
when collapsible is true) is open; for new users or new fold keys, getFold falls back to
false, so the UI starts closed. Since DomainPanel mounts generator domains with collapsible
enabled, the ScaleGenerator collapsed-by-default behavior applies widely across generator-based
editing screens, making the first-load experience hide the controls unless a prior toggle was
persisted.

configurator/src/components/SmartSettings.svelte[66-73]
configurator/src/lib/foldState.js[27-35]
configurator/src/components/ScaleGenerator.svelte[31-44]
configurator/src/components/DomainPanel.svelte[223-252]

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

## Issue description
SmartSettings and ScaleGenerator currently default their collapsible sections to **closed** because they initialize `open` from `getFold(key, false)`, which returns `false` when no persisted value exists. This changes behavior from the previous always-open presentation and hides important controls for first-time users.

## Issue Context
- `SmartSettings` uses `getFold(..., false)` directly in the `<details>` `open` attribute, so new keys render collapsed by default.
- `ScaleGenerator` also uses `getFold(foldKey, false)` in collapsible mode, and its body visibility is gated by `open`.
- `DomainPanel` enables `collapsible` for generator domains (e.g., typography/spacing), so the collapsed-by-default generator impacts core editing UI broadly.

## Fix Focus Areas
- configurator/src/components/SmartSettings.svelte[69-73]
- configurator/src/components/ScaleGenerator.svelte[40-44]
- configurator/src/lib/foldState.js[33-35]

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


4. Redundant probe context updates ✓ Resolved 🐞 Bug ➹ Performance
Description
BrandColorRow calls setProbeContext inside its per-row $effect, even though App.svelte
already keeps the singleton probe host in sync with overrides/theme. This adds repeated signature
computation work per row per change (even when the host won’t update because the signature is
unchanged).
Code

configurator/src/components/BrandColorRow.svelte[R81-90]

+  $effect(() => {
+    for (const k in overrides) void overrides[k];
+    void ui.previewTheme;
+    queueMicrotask(() => {
+      setProbeContext({ overrides, theme: ui.previewTheme });
+      shadeColors = SHADE_SUFFIXES.map((s) => {
+        const rgb = measureBackground(`var(--sf-color-${colorKey}${s})`);
+        return rgb && rgb !== 'rgba(0, 0, 0, 0)' ? rgb : null;
+      });
+    });
Relevance

⭐ Low

They recently added per-component setProbeContext before measureBackground (ShadeRamp) and accepted
similar effect patterns.

PR-#388
PR-#369

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
App.svelte already calls setProbeContext({ overrides, theme }) reactively. probeHost’s
setProbeContext builds an expensive signature from sorted override keys/values; BrandColorRow
triggers that extra computation per row despite the singleton design.

configurator/src/components/BrandColorRow.svelte[77-91]
configurator/src/App.svelte[104-113]
configurator/src/lib/probeHost.js[58-65]

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

### Issue description
BrandColorRow redundantly calls `setProbeContext()` even though App.svelte already updates the probe host globally; this can add avoidable overhead on each override/theme change.

### Issue Context
`setProbeContext` computes a sorted key/value signature each call; doing that per BrandColorRow can be costly.

### Fix
Remove the row-level `setProbeContext` call (or guard it behind a shared/once-per-tick updater) and rely on the App-level probe context maintenance.

### Fix Focus Areas
- configurator/src/components/BrandColorRow.svelte[81-91]
- configurator/src/App.svelte[104-113]
- configurator/src/lib/probeHost.js[58-65]

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


Grey Divider

Qodo Logo

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

🧹 Nitpick comments (3)
configurator/tests-e2e/domain-preview.spec.js (1)

49-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid hard-coding the number of container bars.

Line 50 locks this test to exactly 5 bars, so token-list growth will fail the test even when proportional rendering still works.

♻️ Proposed refactor
   const bars = page.locator('.cbars__bar');
-  await expect(bars).toHaveCount(5);
+  const barCount = await bars.count();
+  expect(barCount, 'ContainerBars should render at least one row').toBeGreaterThan(0);

   const widths = await bars.evaluateAll((els) =>
     els.map((el) => el.getBoundingClientRect().width)
   );
-  expect(new Set(widths.map((w) => Math.round(w))).size, 'container bars use distinct proportional widths').toBeGreaterThanOrEqual(3);
+  expect(
+    new Set(widths.map((w) => Math.round(w))).size,
+    'container bars use distinct proportional widths'
+  ).toBeGreaterThanOrEqual(Math.min(3, barCount));
🤖 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/tests-e2e/domain-preview.spec.js` around lines 49 - 55, The e2e
assertion in the domain preview test is hard-coded to expect exactly five
container bars, which makes the test brittle as the token list grows. Update the
check around the bars locator in domain-preview.spec.js to avoid asserting a
fixed count; instead validate that the rendered bars exist and keep the
proportional width behavior using the existing bars locator and width comparison
logic. Keep the distinct-width assertion, but make the count expectation dynamic
or relative so the test continues to pass when the number of container bars
changes.
configurator/src/components/RadiusEditor.svelte (1)

47-57: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Same tablist ARIA/keyboard gap as HeadingEditor.

The radius tab strip mirrors the HeadingEditor tablist: no role="tabpanel" on .rad__body, no aria-controls/id linkage, and no roving tabindex/arrow-key navigation. The mini level-switcher buttons (Lines 99-109) are correctly labeled, so consider applying the same care to the primary tab strip.

🤖 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/RadiusEditor.svelte` around lines 47 - 57, The
primary radius tab strip in RadiusEditor has the same ARIA/keyboard
accessibility gap as HeadingEditor. Update the tablist so each tab button is
linked to its panel via matching id/aria-controls and the body uses
role="tabpanel", and add roving tabindex plus left/right arrow-key navigation
for the tab buttons. Keep the mini level-switcher buttons unchanged, and apply
the fix in the RadiusEditor tab strip markup and its associated interaction
handlers.
configurator/src/components/HeadingEditor.svelte (1)

75-89: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tablist is missing ARIA wiring and keyboard semantics.

The role="tablist"/role="tab" markup has no associated role="tabpanel" (the .hed__body panel), no aria-controls/id linkage, and no roving tabindex / arrow-key navigation. Screen-reader and keyboard users get a degraded experience versus the WAI-ARIA tabs pattern. Tabs are still Tab-focusable since they're <button>s, so this is not a hard blocker, but given the PR's accessibility goals it's worth wiring up.

🤖 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/HeadingEditor.svelte` around lines 75 - 89, The
HeadingEditor tab UI is missing the full WAI-ARIA tabs pattern, so update the
tab markup and state wiring around TABS, activeTab, and tabHasOverride to add
proper tab/panel relationships. Give each tab an id and aria-controls, make the
.hed__body act as the matching role="tabpanel" with a linked aria-labelledby,
and add roving tabindex plus arrow-key navigation so only the active tab is in
the tab order while still allowing keyboard switching.
🤖 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.

Nitpick comments:
In `@configurator/src/components/HeadingEditor.svelte`:
- Around line 75-89: The HeadingEditor tab UI is missing the full WAI-ARIA tabs
pattern, so update the tab markup and state wiring around TABS, activeTab, and
tabHasOverride to add proper tab/panel relationships. Give each tab an id and
aria-controls, make the .hed__body act as the matching role="tabpanel" with a
linked aria-labelledby, and add roving tabindex plus arrow-key navigation so
only the active tab is in the tab order while still allowing keyboard switching.

In `@configurator/src/components/RadiusEditor.svelte`:
- Around line 47-57: The primary radius tab strip in RadiusEditor has the same
ARIA/keyboard accessibility gap as HeadingEditor. Update the tablist so each tab
button is linked to its panel via matching id/aria-controls and the body uses
role="tabpanel", and add roving tabindex plus left/right arrow-key navigation
for the tab buttons. Keep the mini level-switcher buttons unchanged, and apply
the fix in the RadiusEditor tab strip markup and its associated interaction
handlers.

In `@configurator/tests-e2e/domain-preview.spec.js`:
- Around line 49-55: The e2e assertion in the domain preview test is hard-coded
to expect exactly five container bars, which makes the test brittle as the token
list grows. Update the check around the bars locator in domain-preview.spec.js
to avoid asserting a fixed count; instead validate that the rendered bars exist
and keep the proportional width behavior using the existing bars locator and
width comparison logic. Keep the distinct-width assertion, but make the count
expectation dynamic or relative so the test continues to pass when the number of
container bars changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 24b6bf2d-3cd6-4321-9b9e-de5213ace982

📥 Commits

Reviewing files that changed from the base of the PR and between 1a3197a and 4529781.

⛔ Files ignored due to path filters (2)
  • dist/badge-essential.json is excluded by !**/dist/**
  • dist/badge-optimal.json is excluded by !**/dist/**
📒 Files selected for processing (10)
  • configurator/src/components/BrandColorRow.svelte
  • configurator/src/components/ContainerBars.svelte
  • configurator/src/components/DomainPanel.svelte
  • configurator/src/components/HeadingEditor.svelte
  • configurator/src/components/Preview.svelte
  • configurator/src/components/RadiusEditor.svelte
  • configurator/src/components/ScaleGenerator.svelte
  • configurator/src/components/SmartSettings.svelte
  • configurator/src/lib/foldState.js
  • configurator/tests-e2e/domain-preview.spec.js

claude added 2 commits June 24, 2026 15:51
…unt, shade-strip perf

- HeadingEditor/RadiusEditor: add full WAI-ARIA tabs pattern (id, aria-controls,
  role="tabpanel", aria-labelledby, roving tabindex, arrow-key navigation)
- domain-preview.spec.js: replace hard-coded toHaveCount(5) with dynamic count
  check so the test stays valid as the container token list grows
- BrandColorRow: narrow shade-strip $effect to row-specific overrides only,
  avoiding redundant measureBackground reflows on unrelated token edits

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfCd6Mb2ocTxPFXYjeKg4F
…fix failing e2e test

The domain-sync effect navigates the preview to the 'colors' section when the
Colors sidebar item is clicked, hiding the pv__btn--primary that previously
only lived in Overview. Adding a compact buttons row to the Colors section
restores the live-repaint test and improves the UX (color changes are now
immediately visible on real components within the Colors section).

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