Skip to content

feat(configurator): preview hub redesign — domain-synced previews + foldable generators - #399

Closed
jackgranatowski wants to merge 6 commits into
mainfrom
claude/pr397-review-fixes-mg603u
Closed

feat(configurator): preview hub redesign — domain-synced previews + foldable generators#399
jackgranatowski wants to merge 6 commits into
mainfrom
claude/pr397-review-fixes-mg603u

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Phase 1 — Foldable ScaleGenerator:

  • Add collapsible prop to ScaleGenerator; when true, starts collapsed and
    renders a chevron toggle button in the header.
  • DomainPanel passes collapsible to every ScaleGenerator instance.

Phase 2 — Preview Hub with domain auto-sync:

  • Preview pane SECTIONS expanded from 7 → 13 tabs, one per framework domain:
    Overview · Colors · Gradients · Palette · Type · Spacing · Layout ·
    Borders · Shadows · Motion · Effects · Macros · Tokens
  • $effect in Preview.svelte syncs activeSection to ui.domain on every
    domain-tab switch; users can still manually override the preview tab.

Phase 3 — New preview sections with live token feedback:

  • Gradients: brand + directional fades + gradient-on-content hero card.
  • Spacing: full 2xs–3xl ruler, gap demos, section-pad demo.
  • Borders: radius ramp on real cards, border-color variants, focus-ring trio.
  • Shadows: elevation ramp, shadow on real cards, text/drop/glow/inner examples.
  • Motion: animation demos, duration sweep bars, easing sweep bars.
  • Effects: blur scale, opacity scale, scrim/overlay demo, frosted-glass panel.

DX: inline DomainPreview cards removed — previews live exclusively in the
right Preview Hub, which auto-syncs. Left panel is now purely editing controls.

Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01Epa7UcqpdP8E7mcG9nHLWH

Summary by CodeRabbit

  • New Features

    • Added new visual editors for heading typography, corner radius, and container width tokens.
    • Expanded the preview area with additional sections for gradients, spacing, borders, shadows, motion, and effects.
    • Added inline shade strips for brand colors and ruler-style bars for container tokens.
  • Improvements

    • Several sections can now be collapsed and will remember their open/closed state.
    • Preview navigation and domain editing now surface the most relevant controls more directly.

claude added 6 commits June 24, 2026 10:16
…oldable generators

Phase 1 — Foldable ScaleGenerator:
- Add `collapsible` prop to ScaleGenerator; when true, starts collapsed and
  renders a chevron toggle button in the header.
- DomainPanel passes `collapsible` to every ScaleGenerator instance.

Phase 2 — Preview Hub with domain auto-sync:
- Preview pane SECTIONS expanded from 7 → 13 tabs, one per framework domain:
  Overview · Colors · Gradients · Palette · Type · Spacing · Layout ·
  Borders · Shadows · Motion · Effects · Macros · Tokens
- $effect in Preview.svelte syncs activeSection to ui.domain on every
  domain-tab switch; users can still manually override the preview tab.

Phase 3 — New preview sections with live token feedback:
- Gradients: brand + directional fades + gradient-on-content hero card.
- Spacing: full 2xs–3xl ruler, gap demos, section-pad demo.
- Borders: radius ramp on real cards, border-color variants, focus-ring trio.
- Shadows: elevation ramp, shadow on real cards, text/drop/glow/inner examples.
- Motion: animation demos, duration sweep bars, easing sweep bars.
- Effects: blur scale, opacity scale, scrim/overlay demo, frosted-glass panel.

DX: inline DomainPreview cards removed — previews live exclusively in the
right Preview Hub, which auto-syncs. Left panel is now purely editing controls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Epa7UcqpdP8E7mcG9nHLWH
…rt section defaults

- Add HeadingEditor.svelte: ACSS-style tabbed editor (All/H1-H6/Body/Mono) with
  live specimen preview and per-level token rows; replaces flat basicGroups for
  the Typography domain in DomainPanel
- Add foldState.js: lightweight localStorage persistence for open/closed state
  keyed by section ID; survives page reloads without touching the Svelte store
- Wire foldState into SmartSettings: sections default to closed and remember
  their state per domain (key: `domainId:sectionId`)
- Wire foldState into ScaleGenerator: collapsed state persists across page loads
  (key: `generator:type/display/space`)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Epa7UcqpdP8E7mcG9nHLWH
… width bars

- BrandColorRow: add 7-step inline shade strip below each brand color row,
  resolved live against the active preview theme using the probe host
- RadiusEditor.svelte: ACSS-style tabbed radius editor (All + 2xs→full tabs)
  with a proportional shape specimen per level and a mini-map row for quick
  navigation; wired into the Borders domain in DomainPanel
- ContainerBars.svelte: proportional bar chart for all --sf-container-* tokens
  showing relative widths with live px estimates; wired into the Layout domain
  in DomainPanel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Epa7UcqpdP8E7mcG9nHLWH
- foldState: guard localStorage parse result against non-object types
- ScaleGenerator: remove redundant collapsible guard in toggle handler
- HeadingEditor: include all token groups in 'all' tab override detection
- RadiusEditor: include per-level tokens in 'all' tab override detection
- ContainerBars: use 0 fallback pct for unmeasurable widths (not 100)
- ContainerBars: remove unused imports from earlier Qodo cleanup
- Preview: remove hardcoded duplicate full-radius item
- BrandColorRow: remove unused perceived() helper and parseRgb import

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Epa7UcqpdP8E7mcG9nHLWH
…-specific overrides

Replaces broad `for (const k in overrides)` dependency with targeted
reads of only the two source tokens for this row, avoiding redundant
measureBackground reflows on every unrelated override change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfCd6Mb2ocTxPFXYjeKg4F
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds a localStorage-backed fold state utility and wires collapsible behavior into ScaleGenerator and SmartSettings. Three new domain editor components are introduced (HeadingEditor, RadiusEditor, ContainerBars), and DomainPanel is refactored to a controls-first layout routing to these editors. BrandColorRow gains an inline shade ramp via probe measurement. Preview adds six new section UIs with a domain-to-section sync effect.

Changes

Configurator UI Expansion

Layer / File(s) Summary
Fold state persistence utility
configurator/src/lib/foldState.js
New module with versioned localStorage key, in-memory cache, and exported getFold/setFold functions for per-key boolean persistence.
Collapsible behavior in ScaleGenerator and SmartSettings
configurator/src/components/ScaleGenerator.svelte, configurator/src/components/SmartSettings.svelte
ScaleGenerator gains a collapsible prop, foldKey derivation, toggle button with aria/chevron, and conditional body rendering. SmartSettings replaces hardcoded open with getFold-driven state and an ontoggle handler calling setFold.
HeadingEditor: tabbed typography specimen editor
configurator/src/components/HeadingEditor.svelte
New component computing stageStyle from overrides and preview theme; renders a tablist with modified-dot indicators, live specimen text, and TokenRow lists per heading/body/mono tab.
RadiusEditor: tabbed radius specimen editor
configurator/src/components/RadiusEditor.svelte
New component with LEVELS, GLOBAL_TOKENS, and TABS; renders an All-tab shape specimen grid with global TokenRows and per-level tabs with large preview, clickable mini previews, and a TokenRow.
ContainerBars: normalized bar visualization for container tokens
configurator/src/components/ContainerBars.svelte
New component with parsePx multi-unit conversion, rows/maxPx derivation, and a bar per token normalized to maxPx with raw value, approximate px annotation, and modified-row highlighting.
DomainPanel: controls-first layout with domain-specific editors
configurator/src/components/DomainPanel.svelte
Removes DomainPreview/showPreview logic; Zone 1 becomes controls-only with ScaleGenerator rendered as collapsible. Curated-settings section routes to HeadingEditor, RadiusEditor, or ContainerBars by domain before the basicGroups fallback.
BrandColorRow: inline shade ramp via probe measurement
configurator/src/components/BrandColorRow.svelte
Adds SHADE_SUFFIXES and shadeColors state computed via microtask-deferred measureBackground calls; inserts a 7-swatch strip in the grid with updated layout CSS.
Preview: six new section UIs and domain-to-section sync
configurator/src/components/Preview.svelte
Extends SECTIONS with gradients, spacing, borders, shadows, motion, and effects; adds DOMAIN_TO_SECTION mapping and a reactive $effect syncing activeSection; inserts template blocks and CSS for each new section.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

codex

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% 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 clearly summarizes the main configurator preview hub redesign, including domain-synced previews and foldable generators.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/pr397-review-fixes-mg603u

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 preview hub redesign with domain-synced tabs and foldable generators
✨ Enhancement 🕐 40+ Minutes

Grey Divider

Description

• Add fold-state persistence for generators and Smart Settings sections.
• Move previews into the right Preview Hub and auto-sync it to the active domain.
• Add new domain-specific editors and richer preview sections for key token domains.
Diagram

graph TD
  U["User"] --> DP["DomainPanel"] --> ST[("store.svelte.js")]
  ST --> PV["Preview Hub"]
  DP --> SG["ScaleGenerator"] --> FS["foldState (localStorage)"]
  DP --> SS["SmartSettings"] --> FS
  DP --> BR["BrandColorRow"] --> PH["probeHost"]
  subgraph Legend
    direction LR
    _ui["UI Component"] ~~~ _store[("State Store")] ~~~ _persist[("Persistence")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Add a “Link preview to domain” toggle (pin/unpin sync)
  • ➕ Gives users explicit control to keep Preview Hub on a custom section while editing other domains
  • ➕ Avoids surprising tab switches when users prefer manual navigation
  • ➖ Adds another UI state to explain and persist
  • ➖ Slightly more complexity in Preview.svelte and store UI state
2. Persist fold state within existing UI-state storage (uiState.js)
  • ➕ Centralizes all UI persistence/versioning in one place
  • ➕ Easier to reset/clear UI preferences consistently
  • ➖ Requires threading fold state into the Svelte store and persistence effects
  • ➖ Less reusable from non-Svelte modules than a standalone helper
3. Move domain→section mapping into domain metadata (lib/domains.js)
  • ➕ Single source of truth for domain taxonomy and preview routing
  • ➕ Avoids duplicating mapping logic in Preview.svelte
  • ➖ Requires touching domain definitions and potentially more files
  • ➖ May be premature if mapping is still evolving quickly

Recommendation: Current approach is solid for a Phase 1 UX refactor: Preview Hub sync via a simple DOMAIN_TO_SECTION map and a reactive effect is straightforward, and the standalone foldState module keeps persistence lightweight. If users frequently “browse previews” independently of editing, consider adding a small pin/unpin toggle later to decouple preview navigation from domain switching.

Files changed (9) +1066 / -41

Enhancement (8) +1038 / -5
BrandColorRow.svelteAdd inline shade-ramp strip for each brand color row +53/-2

Add inline shade-ramp strip for each brand color row

• Adds a 7-swatch shade strip (superlight → superdark) beneath each row. Uses probeHost to resolve computed CSS vars under current overrides and preview theme, showing empty placeholders when a shade token is missing/unresolved.

configurator/src/components/BrandColorRow.svelte

ContainerBars.svelteNew layout preview: proportional bars for container width tokens +117/-0

New layout preview: proportional bars for container width tokens

• Introduces a ruler-like visualization for --sf-container-* tokens. Computes approximate pixel widths for common units and renders proportional bars with live values and modified highlighting.

configurator/src/components/ContainerBars.svelte

HeadingEditor.svelteNew tabbed typography editor with live specimens per heading level +203/-0

New tabbed typography editor with live specimens per heading level

• Adds a tabbed UI for All/H1–H6/Body/Mono with live rendered specimens and token rows per tab. Highlights modified tabs and applies preview declarations so specimens reflect current overrides/theme.

configurator/src/components/HeadingEditor.svelte

Preview.svelteExpand Preview Hub to domain-synced sections with new demos +399/-1

Expand Preview Hub to domain-synced sections with new demos

• Expands preview navigation to 13 sections and syncs the active preview section to ui.domain. Adds new rich preview blocks for Gradients, Spacing, Borders, Shadows, Motion, and Effects along with supporting styles.

configurator/src/components/Preview.svelte

RadiusEditor.svelteNew tabbed radius editor with per-level shape specimens +193/-0

New tabbed radius editor with per-level shape specimens

• Adds a tabbed editor for radius levels (including an All view) showing live shape previews sized per level. Provides token rows and modified-state indicators per tab/level.

configurator/src/components/RadiusEditor.svelte

ScaleGenerator.svelteMake scale generators collapsible with persisted open/closed state +22/-1

Make scale generators collapsible with persisted open/closed state

• Adds a collapsible prop that renders a chevron toggle and conditionally hides the generator body. Persists fold state per generator kind using foldState so user choices survive reloads.

configurator/src/components/ScaleGenerator.svelte

SmartSettings.sveltePersist Smart Settings section disclosure state +6/-1

Persist Smart Settings section disclosure state

• Replaces always-open <details> sections with foldState-backed open/toggle handling. Each domain/section gets a stable key so disclosure choices persist across sessions.

configurator/src/components/SmartSettings.svelte

foldState.jsAdd localStorage-backed fold-state helper for collapsible UI +45/-0

Add localStorage-backed fold-state helper for collapsible UI

• Introduces a small persistence utility (getFold/setFold) keyed by caller-provided IDs. Loads once from localStorage, updates an in-memory cache, and writes back on changes with safe guards for non-browser environments.

configurator/src/lib/foldState.js

Refactor (1) +28 / -36
DomainPanel.svelteRemove inline previews and add domain-specific editors +28/-36

Remove inline previews and add domain-specific editors

• Eliminates DomainPreview-led “Zone 1” preview cards so the left panel is now purely controls. Makes generator domains render collapsible ScaleGenerators, and adds specialized editors (HeadingEditor, RadiusEditor, ContainerBars) for typography/borders/layout domains.

configurator/src/components/DomainPanel.svelte

@coderabbitai coderabbitai Bot added the codex label Jun 24, 2026

@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 (2)
configurator/src/components/RadiusEditor.svelte (1)

99-107: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Mini previews are keyboard-inaccessible and will trigger Svelte a11y warnings.

.rad__mini is a non-interactive <div> with an onclick but no role, tabindex, or key handler. Keyboard users can't switch levels via the mini strip, and Svelte 5 emits a11y_click_events_have_key_events / a11y_no_static_element_interactions. Use a <button> so it's focusable and operable for free.

♿ Proposed fix: render mini previews as buttons
           {`#each` LEVELS.filter((l) => exists(l.token)) as l (l.id)}
             {`@const` [sw, sh] = DEMO_SIZES[l.id]}
-            <div
+            <button
+              type="button"
               class="rad__mini"
               class:rad__mini--active={l.id === activeTab}
               onclick={() => (activeTab = l.id)}
               title={l.token}
+              aria-label="{l.label} radius"
               style:width="{Math.min(sw, 44)}px"
               style:height="{Math.min(sh, 44)}px"
               style:border-radius="var({l.token})"
-            ></div>
+            ></button>
           {/each}

Add border: none; padding: 0; to the .rad__mini rule to keep the existing appearance.

🤖 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 99 - 107, The
mini preview element in RadiusEditor.svelte is a non-interactive div using
onclick, which causes Svelte a11y warnings and blocks keyboard access. Update
the mini preview markup in the RadiusEditor component to use a button for each
tab/level so it is focusable and operable by keyboard, keeping the existing
active-state behavior tied to activeTab and l.id. Preserve the current visual
styling by adjusting the .rad__mini styles as needed (for example to remove
default button chrome) so the appearance stays the same while eliminating the
accessibility issue.
configurator/src/components/DomainPanel.svelte (1)

5-23: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stale module doc comment. This block still describes Zone 1 as a "LIVE PREVIEW (always visible, leads the panel)" with a "DomainPreview card at top", but the preview now lives in the right-side Preview Hub and Zone 1 is controls-only (per line 230). Updating avoids misleading future readers.

🤖 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/DomainPanel.svelte` around lines 5 - 23, Update
the stale module doc comment in DomainPanel.svelte so it matches the current
three-zone layout: Zone 1 is controls-only, and the live preview now lives in
the right-side Preview Hub rather than a top-aligned DomainPreview card. Adjust
the descriptive text around the layout principle and the LIVE PREVIEW/QUICK
CONTROLS sections to reflect the actual structure used by DomainPanel and its
generator domain flow.
🤖 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/DomainPanel.svelte`:
- Around line 5-23: Update the stale module doc comment in DomainPanel.svelte so
it matches the current three-zone layout: Zone 1 is controls-only, and the live
preview now lives in the right-side Preview Hub rather than a top-aligned
DomainPreview card. Adjust the descriptive text around the layout principle and
the LIVE PREVIEW/QUICK CONTROLS sections to reflect the actual structure used by
DomainPanel and its generator domain flow.

In `@configurator/src/components/RadiusEditor.svelte`:
- Around line 99-107: The mini preview element in RadiusEditor.svelte is a
non-interactive div using onclick, which causes Svelte a11y warnings and blocks
keyboard access. Update the mini preview markup in the RadiusEditor component to
use a button for each tab/level so it is focusable and operable by keyboard,
keeping the existing active-state behavior tied to activeTab and l.id. Preserve
the current visual styling by adjusting the .rad__mini styles as needed (for
example to remove default button chrome) so the appearance stays the same while
eliminating the accessibility issue.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 85dec244-d847-4c13-ab3a-9c6f76fe4e54

📥 Commits

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

📒 Files selected for processing (9)
  • 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

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

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

Context used
✅ Compliance rules (platform): 5 rules

Grey Divider


Action required

1. Preview sync incomplete 🐞 Bug ≡ Correctness
Description
Preview.svelte always starts activeSection as 'overview' and only later syncs it in an
$effect, so reloads with a persisted non-home ui.domain initially render the wrong preview tab;
additionally, tool domains (wcag/themes/setup/cheatsheet) are valid ui.domain values but have no
mapping, so switching to them leaves the preview stuck on the previously active section.
Code

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

+  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

⭐⭐⭐ High

Team previously accepted eliminating first-paint preview flashes by initializing from persisted
state (PR #317/#312).

PR-#317
PR-#312
PR-#328

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The preview sets activeSection to 'overview' first and only later updates it from ui.domain
via an effect. Since ui.domain is restored from persisted UI state, reloads can start on a
non-home domain while the preview briefly renders Overview. Also, the domain taxonomy explicitly
defines tool domains (wcag/themes/setup/cheatsheet) as possible ui.domain values, but Preview’s
new map doesn’t cover them, so the sync effect won’t update the section when switching to those
tabs.

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

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 new domain→section sync logic in `Preview.svelte` has two functional gaps:
1) `activeSection` is initialized to `'overview'` regardless of the persisted `ui.domain`, then corrected later in an `$effect`, so initial render can show the wrong preview section.
2) `DOMAIN_TO_SECTION` has no entries for tool domains that are valid `ui.domain` values (`wcag`, `themes`, `setup`, `cheatsheet`), so the `$effect` becomes a no-op for those domain switches and the preview remains on the previous section.

### Issue Context
`ui.domain` is persisted/restored (store initializes it from saved UI state), and the domain taxonomy includes tool-only domains.

### Fix Focus Areas
- configurator/src/components/Preview.svelte[46-82]

### Suggested fix direction
- Initialize `activeSection` from `ui.domain` synchronously, e.g. `let activeSection = $state(DOMAIN_TO_SECTION[ui.domain] ?? 'overview');`.
- Add explicit mapping behavior for tool domains. If the intended behavior is “tools show Overview”, map them to `'overview'`; otherwise handle them explicitly (e.g. keep the last section intentionally) but then remove/adjust the unconditional sync effect so behavior is deterministic and matches the feature description.
- Keep manual override behavior by continuing to set `activeSection` directly on preview tab button clicks.

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



Remediation recommended

2. Shade strip not reactive 🐞 Bug ≡ Correctness
Description
BrandColorRow’s inline shade strip only reacts to overrides for the source-light/source-dark knobs,
but it measures derived shade tokens whose resolved value can change when other palette tokens are
overridden, leaving the strip stale until a watched key/theme changes.
Code

configurator/src/components/BrandColorRow.svelte[R77-92]

+  // ── Inline shade strip ───────────────────────────────────────────────────
+  const SHADE_SUFFIXES = ['-superlight', '-xlight', '-lighter', '', '-darker', '-xdark', '-superdark'];
+  let shadeColors = $state([]);
+
+  $effect(() => {
+    void overrides[lightName];
+    void overrides[darkName];
+    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

⭐⭐⭐ High

PR #369 accepted fixing $effect override-key tracking; same “void overrides/insufficient deps”
reactivity pitfall.

PR-#369
PR-#388

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The effect only tracks overrides[lightName]/overrides[darkName], but the measured values are
--sf-color-<key><suffix> tokens which (per the generated catalogue) can alias through other
palette tokens (e.g. --sf-color-primary-superlight aliases --sf-color-primary-50). Overriding
the aliased palette token changes the resolved shade color without touching the watched source
tokens, so the strip won’t update. The repo already demonstrates the correct override-key tracking
pattern (and this exact reactivity pitfall was a recently accepted bug).

configurator/src/components/BrandColorRow.svelte[77-92]
configurator/src/data/api-index.generated.json[5141-5176]
configurator/src/components/ShadeRamp.svelte[30-53]
configurator/src/App.svelte[104-113]
PR-#369

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.svelte` renders an inline 7-swatch shade ramp by measuring `var(--sf-color-${colorKey}${suffix})`, but its `$effect` only establishes reactive dependencies on `overrides[lightName]`, `overrides[darkName]`, and `ui.previewTheme`. If the user overrides any other token that affects those derived shade tokens (e.g. a palette step token aliased by a shade token), the UI won’t recompute `shadeColors`, so the strip can display outdated swatches.

### Issue Context
The codebase already uses a proven pattern for Svelte 5 proxy reactivity with `overrides`: iterate and `void overrides[key]` to ensure per-key changes retrigger effects (see App.svelte / ShadeRamp.svelte). This same pitfall was previously accepted as a bug pattern.

### Fix Focus Areas
- configurator/src/components/BrandColorRow.svelte[77-92]

### Suggested fix direction
- Establish reactive dependencies for *all* tokens that can affect the measured shades. Practical options:
 - Explicitly depend on the measured shade tokens’ override entries (e.g. `for (const s of SHADE_SUFFIXES) void overrides[`--sf-color-${colorKey}${s}`];`) plus `ui.previewTheme`.
 - Or (broader, but consistent with existing fixes) iterate all override keys like `App.svelte` / `ShadeRamp.svelte` do.
- Then recompute `shadeColors` as you already do.
- Optional perf improvement: avoid calling `setProbeContext` redundantly per row if the global App.svelte effect already keeps it in sync, or gate it so it’s only invoked when needed for isolated mounting.

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


Grey Divider

Qodo Logo

Comment on lines +62 to +82
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;
});

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

2. Preview sync incomplete 🐞 Bug ≡ Correctness

Preview.svelte always starts activeSection as 'overview' and only later syncs it in an
$effect, so reloads with a persisted non-home ui.domain initially render the wrong preview tab;
additionally, tool domains (wcag/themes/setup/cheatsheet) are valid ui.domain values but have no
mapping, so switching to them leaves the preview stuck on the previously active section.
Agent Prompt
### Issue description
The new domain→section sync logic in `Preview.svelte` has two functional gaps:
1) `activeSection` is initialized to `'overview'` regardless of the persisted `ui.domain`, then corrected later in an `$effect`, so initial render can show the wrong preview section.
2) `DOMAIN_TO_SECTION` has no entries for tool domains that are valid `ui.domain` values (`wcag`, `themes`, `setup`, `cheatsheet`), so the `$effect` becomes a no-op for those domain switches and the preview remains on the previous section.

### Issue Context
`ui.domain` is persisted/restored (store initializes it from saved UI state), and the domain taxonomy includes tool-only domains.

### Fix Focus Areas
- configurator/src/components/Preview.svelte[46-82]

### Suggested fix direction
- Initialize `activeSection` from `ui.domain` synchronously, e.g. `let activeSection = $state(DOMAIN_TO_SECTION[ui.domain] ?? 'overview');`.
- Add explicit mapping behavior for tool domains. If the intended behavior is “tools show Overview”, map them to `'overview'`; otherwise handle them explicitly (e.g. keep the last section intentionally) but then remove/adjust the unconditional sync effect so behavior is deterministic and matches the feature description.
- Keep manual override behavior by continuing to set `activeSection` directly on preview tab button clicks.

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

@qodo-code-review

Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: Configurator tests

Failed stage: Run npm run test:e2e [❌]

Failed test name: tests-e2e/a11y.spec.js:59:1 › segmented controls expose aria-pressed; info buttons aria-expanded

Failure summary:

The action failed because the Playwright E2E test run (playwright test) exited with code 1 after 11
failing tests (Chromium).
Key failures shown in the log:
- tests-e2e/a11y.spec.js:59:1 timed out
(30s) because row.locator('.row__info-btn') was “not visible” and await info.click() at
tests-e2e/a11y.spec.js:69:14 never succeeded.
- Multiple domain-preview tests failed because the
expected Preview UI was missing/not found:
- tests-e2e/domain-preview.spec.js:20:1 (Typography has
a Preview card) failed toHaveCount(1) at tests-e2e/domain-preview.spec.js:26:55 (received 0).
-
tests-e2e/domain-preview.spec.js:40:1 and tests-e2e/domain-preview.spec.js:57:1 failed toBeVisible()
for .dp__stage because the element(s) were not found (e.g., tests-e2e/domain-preview.spec.js:44:23,
tests-e2e/domain-preview.spec.js:62:23).
- Several generator / undo-redo tests timed out waiting for
form controls that were not available/enabled:
- tests-e2e/generator.spec.js:14:1 timed out
selecting Ratio (mobile) (select:not([disabled])) at tests-e2e/generator.spec.js:18:94.
-
tests-e2e/generator.spec.js:35:1, :56:1, :67:1 timed out waiting to fill() inputs such as Viewport
max, Base max, Base min (e.g., tests-e2e/generator.spec.js:39:77, tests-e2e/generator.spec.js:60:71,
tests-e2e/generator.spec.js:71:71).
- tests-e2e/undo-redo.spec.js:9:1 timed out selecting Ratio
(mobile) at tests-e2e/undo-redo.spec.js:26:94.
- tests-e2e/preview.spec.js:84:1 timed out waiting
for .pv__btn--primary before evaluating computed style at tests-e2e/preview.spec.js:88:28.
Notes:
-
Unit tests passed (# pass 522, # fail 0).
- svelte-check reported warnings only (not fatal): a11y
warning in configurator/src/components/RadiusEditor.svelte:99:15.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

1096:  type: 'test'
1097:  ...
1098:  1..3
1099:  ok 28 - bestContrastVsBW
1100:  ---
1101:  duration_ms: 0.879551
1102:  type: 'suite'
1103:  ...
1104:  # Subtest: wcagLevel
1105:  # Subtest: classifies known thresholds
1106:  ok 1 - classifies known thresholds
1107:  ---
1108:  duration_ms: 0.185796
1109:  type: 'test'
1110:  ...
1111:  # Subtest: non-finite ratios fail
1112:  ok 2 - non-finite ratios fail
1113:  ---
...

3940:  ok 7 - storage key is versioned
3941:  ---
3942:  duration_ms: 0.224578
3943:  type: 'test'
3944:  ...
3945:  1..7
3946:  ok 87 - sanitiseUiState
3947:  ---
3948:  duration_ms: 3.203163
3949:  type: 'suite'
3950:  ...
3951:  1..87
3952:  # tests 522
3953:  # suites 87
3954:  # pass 522
3955:  # fail 0
3956:  # cancelled 0
...

4002:  /home/runner/work/SLASHED/SLASHED/configurator/�[32msrc/components/RadiusEditor.svelte�[39m:99:15
4003:  �[33mWarn�[39m: `<div>` with a click handler must have an ARIA role
4004:  https://svelte.dev/e/a11y_no_static_element_interactions (svelte)
4005:  �[36m              {@const [sw, sh] = DEMO_SIZES[l.id]}
4006:  �[35m<div
4007:  class="rad__mini"
4008:  class:rad__mini--active={l.id === activeTab}
4009:  onclick={() => (activeTab = l.id)}
4010:  title={l.token}
4011:  style:width="{Math.min(sw, 44)}px"
4012:  style:height="{Math.min(sh, 44)}px"
4013:  style:border-radius="var({l.token})"
4014:  ></div>�[36m
4015:  {/each}�[39m
4016:  ====================================
4017:  �[33msvelte-check found 0 errors and 2 warnings in 1 file
4018:  �[39m
...

5301:  �[33m[plugin builtin:vite-reporter] 
5302:  (!) Some chunks are larger than 500 kB after minification. Consider:
5303:  - Using dynamic import() to code-split the application
5304:  - Use build.rolldownOptions.output.codeSplitting to improve chunking: https://rolldown.rs/reference/OutputOptions.codeSplitting
5305:  - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.�[39m
5306:  > slashed-configurator@0.6.13 test:e2e
5307:  > playwright test
5308:  Running 75 tests using 1 worker
5309:  �[1A�[2K[1/75] [chromium] › tests-e2e/a11y.spec.js:16:1 › no horizontal overflow on any panel at any width
5310:  �[1A�[2K[2/75] [chromium] › tests-e2e/a11y.spec.js:30:1 › no horizontal overflow with the full catalogue expanded
5311:  �[1A�[2K[3/75] [chromium] › tests-e2e/a11y.spec.js:46:1 › every button exposes an accessible name
5312:  �[1A�[2K[4/75] [chromium] › tests-e2e/a11y.spec.js:59:1 › segmented controls expose aria-pressed; info buttons aria-expanded
5313:  �[1A�[2K[5/75] (retries) [chromium] › tests-e2e/a11y.spec.js:59:1 › segmented controls expose aria-pressed; info buttons aria-expanded (retry #1)
5314:  �[1A�[2K  1) [chromium] › tests-e2e/a11y.spec.js:59:1 › segmented controls expose aria-pressed; info buttons aria-expanded 
5315:  �[31mTest timeout of 30000ms exceeded.�[39m
5316:  Error: locator.click: Test timeout of 30000ms exceeded.
5317:  Call log:
...

5329:  �[2m      - element is not visible�[22m
5330:  �[2m    - retrying click action�[22m
5331:  �[2m      - waiting 100ms�[22m
5332:  �[2m    57 × waiting for element to be visible, enabled and stable�[22m
5333:  �[2m       - element is not visible�[22m
5334:  �[2m     - retrying click action�[22m
5335:  �[2m       - waiting 500ms�[22m
5336:  67 |   const info = row.locator('.row__info-btn');
5337:  68 |   await expect(info).toHaveAttribute('aria-expanded', 'false');
5338:  > 69 |   await info.click();
5339:  |              ^
5340:  70 |   await expect(info).toHaveAttribute('aria-expanded', 'true');
5341:  71 |   await expect(row.locator('.row__raw')).toContainText('--sf-container-prose');
5342:  72 | });
5343:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/a11y.spec.js:69:14
5344:  Error Context: test-results/a11y-segmented-controls-ex-9ba24--info-buttons-aria-expanded-chromium/error-context.md
5345:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
5346:  �[31mTest timeout of 30000ms exceeded.�[39m
5347:  Error: locator.click: Test timeout of 30000ms exceeded.
5348:  Call log:
...

5360:  �[2m      - element is not visible�[22m
5361:  �[2m    - retrying click action�[22m
5362:  �[2m      - waiting 100ms�[22m
5363:  �[2m    57 × waiting for element to be visible, enabled and stable�[22m
5364:  �[2m       - element is not visible�[22m
5365:  �[2m     - retrying click action�[22m
5366:  �[2m       - waiting 500ms�[22m
5367:  67 |   const info = row.locator('.row__info-btn');
5368:  68 |   await expect(info).toHaveAttribute('aria-expanded', 'false');
5369:  > 69 |   await info.click();
5370:  |              ^
5371:  70 |   await expect(info).toHaveAttribute('aria-expanded', 'true');
5372:  71 |   await expect(row.locator('.row__raw')).toContainText('--sf-container-prose');
5373:  72 | });
5374:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/a11y.spec.js:69:14
5375:  Error Context: test-results/a11y-segmented-controls-ex-9ba24--info-buttons-aria-expanded-chromium-retry1/error-context.md
5376:  �[1A�[2K[6/75] [chromium] › tests-e2e/a11y.spec.js:74:1 › search filters the catalogue and shows an empty state for no match
5377:  �[1A�[2K[7/75] [chromium] › tests-e2e/axe.spec.js:32:3 › no structural a11y violations on the main routes (dark)
5378:  �[1A�[2K[8/75] [chromium] › tests-e2e/axe.spec.js:32:3 › no structural a11y violations on the main routes (light)
5379:  �[1A�[2K[9/75] [chromium] › tests-e2e/dogfood.spec.js:26:1 › chrome aliases are sourced from framework tokens
5380:  �[1A�[2K[10/75] [chromium] › tests-e2e/dogfood.spec.js:39:1 › overrides restyle the preview stage but never the chrome
5381:  �[1A�[2K[11/75] [chromium] › tests-e2e/dogfood.spec.js:57:1 › chrome theme toggle flips the framework [data-theme] and repaints
5382:  �[1A�[2K[12/75] [chromium] › tests-e2e/domain-preview.spec.js:20:1 › every token domain leads with an open Preview card
5383:  �[1A�[2K[13/75] (retries) [chromium] › tests-e2e/domain-preview.spec.js:20:1 › every token domain leads with an open Preview card (retry #1)
5384:  �[1A�[2K  2) [chromium] › tests-e2e/domain-preview.spec.js:20:1 › every token domain leads with an open Preview card 
5385:  Error: Typography has a Preview card
5386:  �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoHaveCount�[2m(�[22m�[32mexpected�[39m�[2m)�[22m failed
5387:  Locator:  locator('details.panel__card').filter({ has: locator('.panel__card-title').filter({ hasText: /^Preview$/ }) })
...

5389:  Received: �[31m0�[39m
5390:  Timeout:  5000ms
5391:  Call log:
5392:  �[2m  - Typography has a Preview card with timeout 5000ms�[22m
5393:  �[2m  - waiting for locator('details.panel__card').filter({ has: locator('.panel__card-title').filter({ hasText: /^Preview$/ }) })�[22m
5394:  �[2m    14 × locator resolved to 0 elements�[22m
5395:  �[2m       - unexpected value "0"�[22m
5396:  24 |     await sideItem(page, label).click();
5397:  25 |     const card = previewCard(page);
5398:  > 26 |     await expect(card, `${label} has a Preview card`).toHaveCount(1);
5399:  |                                                       ^
5400:  27 |     // Open by default — the scoped stage is visible immediately.
5401:  28 |     expect(await card.evaluate((el) => el.open), `${label} Preview is open by default`).toBe(true);
5402:  29 |     await expect(card.locator('.dp__stage'), `${label} stage is visible`).toBeVisible();
5403:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/domain-preview.spec.js:26:55
5404:  Error Context: test-results/domain-preview-every-token-4848d-s-with-an-open-Preview-card-chromium/error-context.md
5405:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
5406:  Error: Typography has a Preview card
5407:  �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoHaveCount�[2m(�[22m�[32mexpected�[39m�[2m)�[22m failed
5408:  Locator:  locator('details.panel__card').filter({ has: locator('.panel__card-title').filter({ hasText: /^Preview$/ }) })
...

5410:  Received: �[31m0�[39m
5411:  Timeout:  5000ms
5412:  Call log:
5413:  �[2m  - Typography has a Preview card with timeout 5000ms�[22m
5414:  �[2m  - waiting for locator('details.panel__card').filter({ has: locator('.panel__card-title').filter({ hasText: /^Preview$/ }) })�[22m
5415:  �[2m    14 × locator resolved to 0 elements�[22m
5416:  �[2m       - unexpected value "0"�[22m
5417:  24 |     await sideItem(page, label).click();
5418:  25 |     const card = previewCard(page);
5419:  > 26 |     await expect(card, `${label} has a Preview card`).toHaveCount(1);
5420:  |                                                       ^
5421:  27 |     // Open by default — the scoped stage is visible immediately.
5422:  28 |     expect(await card.evaluate((el) => el.open), `${label} Preview is open by default`).toBe(true);
5423:  29 |     await expect(card.locator('.dp__stage'), `${label} stage is visible`).toBeVisible();
5424:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/domain-preview.spec.js:26:55
5425:  Error Context: test-results/domain-preview-every-token-4848d-s-with-an-open-Preview-card-chromium-retry1/error-context.md
5426:  �[1A�[2K[14/75] [chromium] › tests-e2e/domain-preview.spec.js:40:1 › the Layout preview renders distinct, proportional container widths
5427:  �[1A�[2K[15/75] (retries) [chromium] › tests-e2e/domain-preview.spec.js:40:1 › the Layout preview renders distinct, proportional container widths (retry #1)
5428:  �[1A�[2K  3) [chromium] › tests-e2e/domain-preview.spec.js:40:1 › the Layout preview renders distinct, proportional container widths 
5429:  Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed
5430:  Locator: locator('details.panel__card').filter({ has: locator('.panel__card-title').filter({ hasText: /^Preview$/ }) }).locator('.dp__stage')
5431:  Expected: visible
5432:  Timeout: 5000ms
5433:  Error: element(s) not found
5434:  Call log:
5435:  �[2m  - Expect "toBeVisible" with timeout 5000ms�[22m
5436:  �[2m  - waiting for locator('details.panel__card').filter({ has: locator('.panel__card-title').filter({ hasText: /^Preview$/ }) }).locator('.dp__stage')�[22m
5437:  42 |   await sideItem(page, 'Layout').click();
5438:  43 |   const stage = previewCard(page).locator('.dp__stage');
5439:  > 44 |   await expect(stage).toBeVisible();
5440:  |                       ^
5441:  45 |   // The fix: container bars are measured to true relative scale, so their
5442:  46 |   // widths differ (the old clamp-to-100% rendered all four identical).
5443:  47 |   const widths = await stage.locator('.dp__track-fill').evaluateAll((els) =>
5444:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/domain-preview.spec.js:44:23
5445:  Error Context: test-results/domain-preview-the-Layout--f3d5e-oportional-container-widths-chromium/error-context.md
5446:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
5447:  Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed
5448:  Locator: locator('details.panel__card').filter({ has: locator('.panel__card-title').filter({ hasText: /^Preview$/ }) }).locator('.dp__stage')
5449:  Expected: visible
5450:  Timeout: 5000ms
5451:  Error: element(s) not found
5452:  Call log:
5453:  �[2m  - Expect "toBeVisible" with timeout 5000ms�[22m
5454:  �[2m  - waiting for locator('details.panel__card').filter({ has: locator('.panel__card-title').filter({ hasText: /^Preview$/ }) }).locator('.dp__stage')�[22m
5455:  42 |   await sideItem(page, 'Layout').click();
5456:  43 |   const stage = previewCard(page).locator('.dp__stage');
5457:  > 44 |   await expect(stage).toBeVisible();
5458:  |                       ^
5459:  45 |   // The fix: container bars are measured to true relative scale, so their
5460:  46 |   // widths differ (the old clamp-to-100% rendered all four identical).
5461:  47 |   const widths = await stage.locator('.dp__track-fill').evaluateAll((els) =>
5462:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/domain-preview.spec.js:44:23
5463:  Error Context: test-results/domain-preview-the-Layout--f3d5e-oportional-container-widths-chromium-retry1/error-context.md
5464:  �[1A�[2K[16/75] [chromium] › tests-e2e/domain-preview.spec.js:57:1 › the preview reflects a live override
5465:  �[1A�[2K[17/75] (retries) [chromium] › tests-e2e/domain-preview.spec.js:57:1 › the preview reflects a live override (retry #1)
5466:  �[1A�[2K  4) [chromium] › tests-e2e/domain-preview.spec.js:57:1 › the preview reflects a live override ─────
5467:  Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed
5468:  Locator: locator('details.panel__card').filter({ has: locator('.panel__card-title').filter({ hasText: /^Preview$/ }) }).locator('.dp__stage')
5469:  Expected: visible
5470:  Timeout: 5000ms
5471:  Error: element(s) not found
5472:  Call log:
5473:  �[2m  - Expect "toBeVisible" with timeout 5000ms�[22m
5474:  �[2m  - waiting for locator('details.panel__card').filter({ has: locator('.panel__card-title').filter({ hasText: /^Preview$/ }) }).locator('.dp__stage')�[22m
5475:  60 |   const card = previewCard(page);
5476:  61 |   const stage = card.locator('.dp__stage');
5477:  > 62 |   await expect(stage).toBeVisible();
5478:  |                       ^
5479:  63 |
5480:  64 |   // Baseline: the scoped stage carries the full framework cascade, so the
5481:  65 |   // radius knob is present at its default.
5482:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/domain-preview.spec.js:62:23
5483:  Error Context: test-results/domain-preview-the-preview-reflects-a-live-override-chromium/error-context.md
5484:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
5485:  Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed
5486:  Locator: locator('details.panel__card').filter({ has: locator('.panel__card-title').filter({ hasText: /^Preview$/ }) }).locator('.dp__stage')
5487:  Expected: visible
5488:  Timeout: 5000ms
5489:  Error: element(s) not found
5490:  Call log:
5491:  �[2m  - Expect "toBeVisible" with timeout 5000ms�[22m
5492:  �[2m  - waiting for locator('details.panel__card').filter({ has: locator('.panel__card-title').filter({ hasText: /^Preview$/ }) }).locator('.dp__stage')�[22m
5493:  60 |   const card = previewCard(page);
5494:  61 |   const stage = card.locator('.dp__stage');
5495:  > 62 |   await expect(stage).toBeVisible();
5496:  |                       ^
5497:  63 |
5498:  64 |   // Baseline: the scoped stage carries the full framework cascade, so the
5499:  65 |   // radius knob is present at its default.
5500:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/domain-preview.spec.js:62:23
5501:  Error Context: test-results/domain-preview-the-preview-reflects-a-live-override-chromium-retry1/error-context.md
5502:  �[1A�[2K[18/75] [chromium] › tests-e2e/domain-preview.spec.js:80:1 › the Colors panel keeps its bespoke Semantic roles card (no generic Preview)
5503:  �[1A�[2K[19/75] [chromium] › tests-e2e/generator.spec.js:14:1 › type generator writes engine scalars only
5504:  �[1A�[2K[20/75] (retries) [chromium] › tests-e2e/generator.spec.js:14:1 › type generator writes engine scalars only (retry #1)
5505:  �[1A�[2K  5) [chromium] › tests-e2e/generator.spec.js:14:1 › type generator writes engine scalars only ─────
5506:  �[31mTest timeout of 30000ms exceeded.�[39m
5507:  Error: locator.selectOption: Test timeout of 30000ms exceeded.
5508:  Call log:
5509:  �[2m  - waiting for locator('.gen').first().locator('.ctl').filter({ hasText: 'Ratio (mobile)' }).locator('select:not([disabled])')�[22m
5510:  16 |   await sideItem(page, 'Typography').click();
5511:  17 |   const gen = page.locator('.gen').first();
5512:  > 18 |   await gen.locator('.ctl', { hasText: 'Ratio (mobile)' }).locator('select:not([disabled])').selectOption('1.125');
5513:  |                                                                                              ^
5514:  19 |   await APPLY(gen).click();
5515:  20 |   const map = await readOverrides(page);
5516:  21 |   expect(map['--sf-text-ratio-min']).toBe('1.125');
5517:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/generator.spec.js:18:94
5518:  Error Context: test-results/generator-type-generator-writes-engine-scalars-only-chromium/error-context.md
5519:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
5520:  �[31mTest timeout of 30000ms exceeded.�[39m
5521:  Error: locator.selectOption: Test timeout of 30000ms exceeded.
5522:  Call log:
5523:  �[2m  - waiting for locator('.gen').first().locator('.ctl').filter({ hasText: 'Ratio (mobile)' }).locator('select:not([disabled])')�[22m
5524:  16 |   await sideItem(page, 'Typography').click();
5525:  17 |   const gen = page.locator('.gen').first();
5526:  > 18 |   await gen.locator('.ctl', { hasText: 'Ratio (mobile)' }).locator('select:not([disabled])').selectOption('1.125');
5527:  |                                                                                              ^
5528:  19 |   await APPLY(gen).click();
5529:  20 |   const map = await readOverrides(page);
5530:  21 |   expect(map['--sf-text-ratio-min']).toBe('1.125');
5531:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/generator.spec.js:18:94
5532:  Error Context: test-results/generator-type-generator-writes-engine-scalars-only-chromium-retry1/error-context.md
5533:  �[1A�[2K[21/75] [chromium] › tests-e2e/generator.spec.js:27:1 › display tab: ratio selects are read-only (shared with type)
5534:  �[1A�[2K[22/75] (retries) [chromium] › tests-e2e/generator.spec.js:27:1 › display tab: ratio selects are read-only (shared with type) (retry #1)
5535:  �[1A�[2K  6) [chromium] › tests-e2e/generator.spec.js:27:1 › display tab: ratio selects are read-only (shared with type) 
5536:  Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoHaveCount�[2m(�[22m�[32mexpected�[39m�[2m)�[22m failed
5537:  Locator:  locator('.gen').nth(1).locator('select[disabled]')
...

5539:  Received: �[31m0�[39m
5540:  Timeout:  5000ms
5541:  Call log:
5542:  �[2m  - Expect "toHaveCount" with timeout 5000ms�[22m
5543:  �[2m  - waiting for locator('.gen').nth(1).locator('select[disabled]')�[22m
5544:  �[2m    14 × locator resolved to 0 elements�[22m
5545:  �[2m       - unexpected value "0"�[22m
5546:  29 |   await sideItem(page, 'Typography').click();
5547:  30 |   const display = page.locator('.gen').nth(1);
5548:  > 31 |   await expect(display.locator('select[disabled]')).toHaveCount(2);
5549:  |                                                     ^
5550:  32 |   await expect(display.locator('.gen__hint').first()).toContainText('reuses the type ratios');
5551:  33 | });
5552:  34 |
5553:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/generator.spec.js:31:53
5554:  Error Context: test-results/generator-display-tab-rati-18399-read-only-shared-with-type--chromium/error-context.md
5555:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
5556:  Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoHaveCount�[2m(�[22m�[32mexpected�[39m�[2m)�[22m failed
5557:  Locator:  locator('.gen').nth(1).locator('select[disabled]')
...

5559:  Received: �[31m0�[39m
5560:  Timeout:  5000ms
5561:  Call log:
5562:  �[2m  - Expect "toHaveCount" with timeout 5000ms�[22m
5563:  �[2m  - waiting for locator('.gen').nth(1).locator('select[disabled]')�[22m
5564:  �[2m    14 × locator resolved to 0 elements�[22m
5565:  �[2m       - unexpected value "0"�[22m
5566:  29 |   await sideItem(page, 'Typography').click();
5567:  30 |   const display = page.locator('.gen').nth(1);
5568:  > 31 |   await expect(display.locator('select[disabled]')).toHaveCount(2);
5569:  |                                                     ^
5570:  32 |   await expect(display.locator('.gen__hint').first()).toContainText('reuses the type ratios');
5571:  33 | });
5572:  34 |
5573:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/generator.spec.js:31:53
5574:  Error Context: test-results/generator-display-tab-rati-18399-read-only-shared-with-type--chromium-retry1/error-context.md
5575:  �[1A�[2K[23/75] [chromium] › tests-e2e/generator.spec.js:35:1 › viewport range is shared: space sets it, type seeds it, reset clears it
5576:  �[1A�[2K[24/75] (retries) [chromium] › tests-e2e/generator.spec.js:35:1 › viewport range is shared: space sets it, type seeds it, reset clears it (retry #1)
5577:  �[1A�[2K  7) [chromium] › tests-e2e/generator.spec.js:35:1 › viewport range is shared: space sets it, type seeds it, reset clears it 
5578:  �[31mTest timeout of 30000ms exceeded.�[39m
5579:  Error: locator.fill: Test timeout of 30000ms exceeded.
5580:  Call log:
5581:  �[2m  - waiting for locator('.gen').first().locator('.ctl').filter({ hasText: 'Viewport max' }).locator('input')�[22m
5582:  37 |   await sideItem(page, 'Spacing').click();
5583:  38 |   const space = page.locator('.gen').first();
5584:  > 39 |   await space.locator('.ctl', { hasText: 'Viewport max' }).locator('input').fill('100');
5585:  |                                                                             ^
5586:  40 |   await APPLY(space).click();
5587:  41 |   expect((await readOverrides(page))['--sf-fluid-max-vw']).toBe('100');
5588:  42 |
5589:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/generator.spec.js:39:77
5590:  Error Context: test-results/generator-viewport-range-i-746bf-pe-seeds-it-reset-clears-it-chromium/error-context.md
5591:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
5592:  �[31mTest timeout of 30000ms exceeded.�[39m
5593:  Error: locator.fill: Test timeout of 30000ms exceeded.
5594:  Call log:
5595:  �[2m  - waiting for locator('.gen').first().locator('.ctl').filter({ hasText: 'Viewport max' }).locator('input')�[22m
5596:  37 |   await sideItem(page, 'Spacing').click();
5597:  38 |   const space = page.locator('.gen').first();
5598:  > 39 |   await space.locator('.ctl', { hasText: 'Viewport max' }).locator('input').fill('100');
5599:  |                                                                             ^
5600:  40 |   await APPLY(space).click();
5601:  41 |   expect((await readOverrides(page))['--sf-fluid-max-vw']).toBe('100');
5602:  42 |
5603:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/generator.spec.js:39:77
5604:  Error Context: test-results/generator-viewport-range-i-746bf-pe-seeds-it-reset-clears-it-chromium-retry1/error-context.md
5605:  �[1A�[2K[25/75] [chromium] › tests-e2e/generator.spec.js:56:1 › "Reset" returns the ramp to framework defaults
5606:  �[1A�[2K[26/75] (retries) [chromium] › tests-e2e/generator.spec.js:56:1 › "Reset" returns the ramp to framework defaults (retry #1)
5607:  �[1A�[2K  8) [chromium] › tests-e2e/generator.spec.js:56:1 › "Reset" returns the ramp to framework defaults 
5608:  �[31mTest timeout of 30000ms exceeded.�[39m
5609:  Error: locator.fill: Test timeout of 30000ms exceeded.
5610:  Call log:
5611:  �[2m  - waiting for locator('.gen').first().locator('.ctl').filter({ hasText: 'Base max' }).locator('input')�[22m
5612:  58 |   await sideItem(page, 'Spacing').click();
5613:  59 |   const gen = page.locator('.gen').first();
5614:  > 60 |   await gen.locator('.ctl', { hasText: 'Base max' }).locator('input').fill('2.5');
5615:  |                                                                       ^
5616:  61 |   await APPLY(gen).click();
5617:  62 |   expect((await readOverrides(page))['--sf-space-base-max']).toBe('2.5');
5618:  63 |   await gen.locator('button', { hasText: 'Reset' }).click();
5619:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/generator.spec.js:60:71
5620:  Error Context: test-results/generator--Reset-returns-the-ramp-to-framework-defaults-chromium/error-context.md
5621:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
5622:  �[31mTest timeout of 30000ms exceeded.�[39m
5623:  Error: locator.fill: Test timeout of 30000ms exceeded.
5624:  Call log:
5625:  �[2m  - waiting for locator('.gen').first().locator('.ctl').filter({ hasText: 'Base max' }).locator('input')�[22m
5626:  58 |   await sideItem(page, 'Spacing').click();
5627:  59 |   const gen = page.locator('.gen').first();
5628:  > 60 |   await gen.locator('.ctl', { hasText: 'Base max' }).locator('input').fill('2.5');
5629:  |                                                                       ^
5630:  61 |   await APPLY(gen).click();
5631:  62 |   expect((await readOverrides(page))['--sf-space-base-max']).toBe('2.5');
5632:  63 |   await gen.locator('button', { hasText: 'Reset' }).click();
5633:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/generator.spec.js:60:71
5634:  Error Context: test-results/generator--Reset-returns-the-ramp-to-framework-defaults-chromium-retry1/error-context.md
5635:  �[1A�[2K[27/75] [chromium] › tests-e2e/generator.spec.js:67:1 › edge inputs never persist garbage values
5636:  �[1A�[2K[28/75] (retries) [chromium] › tests-e2e/generator.spec.js:67:1 › edge inputs never persist garbage values (retry #1)
5637:  �[1A�[2K  9) [chromium] › tests-e2e/generator.spec.js:67:1 › edge inputs never persist garbage values ──────
5638:  �[31mTest timeout of 30000ms exceeded.�[39m
5639:  Error: locator.fill: Test timeout of 30000ms exceeded.
5640:  Call log:
5641:  �[2m  - waiting for locator('.gen').first().locator('.ctl').filter({ hasText: 'Base min' }).locator('input')�[22m
5642:  69 |   await sideItem(page, 'Spacing').click();
5643:  70 |   const gen = page.locator('.gen').first();
5644:  > 71 |   await gen.locator('.ctl', { hasText: 'Base min' }).locator('input').fill('-1');
5645:  |                                                                       ^
5646:  72 |   await gen.locator('.ctl', { hasText: 'Base max' }).locator('input').fill('0');
5647:  73 |   await APPLY(gen).click();
5648:  74 |   for (const [key, value] of Object.entries(await readOverrides(page))) {
5649:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/generator.spec.js:71:71
5650:  Error Context: test-results/generator-edge-inputs-never-persist-garbage-values-chromium/error-context.md
5651:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
5652:  �[31mTest timeout of 30000ms exceeded.�[39m
5653:  Error: locator.fill: Test timeout of 30000ms exceeded.
5654:  Call log:
5655:  �[2m  - waiting for locator('.gen').first().locator('.ctl').filter({ hasText: 'Base min' }).locator('input')�[22m
5656:  69 |   await sideItem(page, 'Spacing').click();
5657:  70 |   const gen = page.locator('.gen').first();
5658:  > 71 |   await gen.locator('.ctl', { hasText: 'Base min' }).locator('input').fill('-1');
5659:  |                                                                       ^
5660:  72 |   await gen.locator('.ctl', { hasText: 'Base max' }).locator('input').fill('0');
5661:  73 |   await APPLY(gen).click();
5662:  74 |   for (const [key, value] of Object.entries(await readOverrides(page))) {
5663:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/generator.spec.js:71:71
5664:  Error Context: test-results/generator-edge-inputs-never-persist-garbage-values-chromium-retry1/error-context.md
5665:  �[1A�[2K[29/75] [chromium] › tests-e2e/import-export.spec.js:13:1 › generated css round-trips through import (layer mode)
...

5671:  �[1A�[2K[35/75] [chromium] › tests-e2e/presets.spec.js:21:1 › shadow presets: switching leaves no leftovers from the previous preset
5672:  �[1A�[2K[36/75] [chromium] › tests-e2e/presets.spec.js:33:1 › hand-edited values show NO active preset (never a wrong one)
5673:  �[1A�[2K[37/75] [chromium] › tests-e2e/presets.spec.js:45:1 › scaling knobs live in Settings for scale domains, absent otherwise
5674:  �[1A�[2K[38/75] [chromium] › tests-e2e/presets.spec.js:59:1 › shadow-strength knob round-trips its dark-mode calc() encoding
5675:  �[1A�[2K[39/75] [chromium] › tests-e2e/preview.spec.js:12:1 › desktop: pane visible by default, header toggle hides/shows it
5676:  �[1A�[2K[40/75] [chromium] › tests-e2e/preview.spec.js:24:3 › 1000px: overlay starts closed, opens fixed, scrim dismisses
5677:  �[1A�[2K[41/75] [chromium] › tests-e2e/preview.spec.js:24:3 › 480px: overlay starts closed, opens fixed, scrim dismisses
5678:  �[1A�[2K[42/75] [chromium] › tests-e2e/preview.spec.js:39:1 › overlay bar carries working theme / motion / close controls
5679:  �[1A�[2K[43/75] [chromium] › tests-e2e/preview.spec.js:56:1 › Escape dismisses the overlay and focus returns to the toggle
5680:  �[1A�[2K[44/75] [chromium] › tests-e2e/preview.spec.js:67:1 › Escape does NOT close the persistent desktop pane
5681:  �[1A�[2K[45/75] [chromium] › tests-e2e/preview.spec.js:74:1 › resize round-trip: wide -> narrow -> wide restores the pane
5682:  �[1A�[2K[46/75] [chromium] › tests-e2e/preview.spec.js:84:1 › preview is live: editing a brand color repaints the sample button
5683:  �[1A�[2K[47/75] (retries) [chromium] › tests-e2e/preview.spec.js:84:1 › preview is live: editing a brand color repaints the sample button (retry #1)
5684:  �[1A�[2K  10) [chromium] › tests-e2e/preview.spec.js:84:1 › preview is live: editing a brand color repaints the sample button 
5685:  �[31mTest timeout of 30000ms exceeded.�[39m
5686:  Error: locator.evaluate: Test timeout of 30000ms exceeded.
5687:  Call log:
5688:  �[2m  - waiting for locator('.pv__btn--primary').first()�[22m
5689:  86 |   await page.locator('.side__item', { hasText: 'Colors' }).first().click();
5690:  87 |   const btn = page.locator('.pv__btn--primary').first();
5691:  > 88 |   const before = await btn.evaluate((el) => getComputedStyle(el).backgroundColor);
5692:  |                            ^
5693:  89 |   const input = page.locator('input.bcr__input[aria-label="--sf-color-primary-source-light value"]');
5694:  90 |   await input.fill('oklch(0.5 0.2 140)');
5695:  91 |   await input.press('Enter');
5696:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/preview.spec.js:88:28
5697:  Error Context: test-results/preview-preview-is-live-ed-cecd3--repaints-the-sample-button-chromium/error-context.md
5698:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
5699:  �[31mTest timeout of 30000ms exceeded.�[39m
5700:  Error: locator.evaluate: Test timeout of 30000ms exceeded.
5701:  Call log:
5702:  �[2m  - waiting for locator('.pv__btn--primary').first()�[22m
5703:  86 |   await page.locator('.side__item', { hasText: 'Colors' }).first().click();
5704:  87 |   const btn = page.locator('.pv__btn--primary').first();
5705:  > 88 |   const before = await btn.evaluate((el) => getComputedStyle(el).backgroundColor);
5706:  |                            ^
5707:  89 |   const input = page.locator('input.bcr__input[aria-label="--sf-color-primary-source-light value"]');
5708:  90 |   await input.fill('oklch(0.5 0.2 140)');
5709:  91 |   await input.press('Enter');
5710:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/preview.spec.js:88:28
5711:  Error Context: test-results/preview-preview-is-live-ed-cecd3--repaints-the-sample-button-chromium-retry1/error-context.md
5712:  �[1A�[2K[48/75] [chromium] › tests-e2e/share.spec.js:16:1 › editing a token writes the config into the URL fragment
5713:  �[1A�[2K[49/75] [chromium] › tests-e2e/share.spec.js:25:1 › opening a shared URL on a clean slate restores the overrides
5714:  �[1A�[2K[50/75] [chromium] › tests-e2e/share.spec.js:41:1 › the Share button copies a link with the config fragment
5715:  �[1A�[2K[51/75] [chromium] › tests-e2e/shell.spec.js:16:1 › lands on the Overview with the setup checklist
5716:  �[1A�[2K[52/75] [chromium] › tests-e2e/shell.spec.js:29:1 › every destination renders console-clean at 3 widths
5717:  �[1A�[2K[53/75] [chromium] › tests-e2e/shell.spec.js:39:1 › every category panel leads with Settings, with All variables collapsed below
5718:  �[1A�[2K[54/75] [chromium] › tests-e2e/shell.spec.js:53:1 › a domain with no curated Settings shows the catalogue inline
5719:  �[1A�[2K[55/75] [chromium] › tests-e2e/shell.spec.js:61:1 › [ and ] cycle Overview + every domain with wrap-around
5720:  �[1A�[2K[56/75] [chromium] › tests-e2e/shell.spec.js:74:1 › domain and output format persist across reload (validated restore)
5721:  �[1A�[2K[57/75] [chromium] › tests-e2e/shell.spec.js:87:1 › a legacy persisted mode field is ignored on restore
5722:  �[1A�[2K[58/75] [chromium] › tests-e2e/undo-redo.spec.js:9:1 › 5-step mixed chain unwinds and replays exactly
5723:  �[1A�[2K[59/75] (retries) [chromium] › tests-e2e/undo-redo.spec.js:9:1 › 5-step mixed chain unwinds and replays exactly (retry #1)
5724:  �[1A�[2K  11) [chromium] › tests-e2e/undo-redo.spec.js:9:1 › 5-step mixed chain unwinds and replays exactly 
5725:  �[31mTest timeout of 30000ms exceeded.�[39m
5726:  Error: locator.selectOption: Test timeout of 30000ms exceeded.
5727:  Call log:
5728:  �[2m  - waiting for locator('.gen').first().locator('.ctl').filter({ hasText: 'Ratio (mobile)' }).locator('select:not([disabled])')�[22m
5729:  24 |   await sideItem(page, 'Typography').click();
5730:  25 |   const gen = page.locator('.gen').first();
5731:  > 26 |   await gen.locator('.ctl', { hasText: 'Ratio (mobile)' }).locator('select:not([disabled])').selectOption('1.414');
5732:  |                                                                                              ^
5733:  27 |   await gen.locator('button', { hasText: /Apply scale/ }).first().click();
5734:  28 |   snapshots.push(stableSnapshot(await readOverrides(page)));
5735:  29 |
5736:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/undo-redo.spec.js:26:94
5737:  Error Context: test-results/undo-redo-5-step-mixed-chain-unwinds-and-replays-exactly-chromium/error-context.md
5738:  Retry #1 ───────────────────────────────────────────────────────────────────────────────────────
5739:  �[31mTest timeout of 30000ms exceeded.�[39m
5740:  Error: locator.selectOption: Test timeout of 30000ms exceeded.
5741:  Call log:
5742:  �[2m  - waiting for locator('.gen').first().locator('.ctl').filter({ hasText: 'Ratio (mobile)' }).locator('select:not([disabled])')�[22m
5743:  24 |   await sideItem(page, 'Typography').click();
5744:  25 |   const gen = page.locator('.gen').first();
5745:  > 26 |   await gen.locator('.ctl', { hasText: 'Ratio (mobile)' }).locator('select:not([disabled])').selectOption('1.414');
5746:  |                                                                                              ^
5747:  27 |   await gen.locator('button', { hasText: /Apply scale/ }).first().click();
5748:  28 |   snapshots.push(stableSnapshot(await readOverrides(page)));
5749:  29 |
5750:  at /home/runner/work/SLASHED/SLASHED/configurator/tests-e2e/undo-redo.spec.js:26:94
5751:  Error Context: test-results/undo-redo-5-step-mixed-chain-unwinds-and-replays-exactly-chromium-retry1/error-context.md
5752:  �[1A�[2K[60/75] [chromium] › tests-e2e/undo-redo.spec.js:54:1 › overrides persist across reload
...

5764:  �[1A�[2K[72/75] [firefox] › tests-e2e/shell.spec.js:74:1 › domain and output format persist across reload (validated restore)
5765:  �[1A�[2K[73/75] [firefox] › tests-e2e/shell.spec.js:87:1 › a legacy persisted mode field is ignored on restore
5766:  �[1A�[2K[74/75] [webkit] › tests-e2e/dogfood.spec.js:26:1 › chrome aliases are sourced from framework tokens
5767:  �[1A�[2K[75/75] [webkit] › tests-e2e/dogfood.spec.js:39:1 › overrides restyle the preview stage but never the chrome
5768:  �[1A�[2K[76/75] [webkit] › tests-e2e/dogfood.spec.js:57:1 › chrome theme toggle flips the framework [data-theme] and repaints
5769:  �[1A�[2K[77/75] [webkit] › tests-e2e/share.spec.js:16:1 › editing a token writes the config into the URL fragment
5770:  �[1A�[2K[78/75] [webkit] › tests-e2e/share.spec.js:25:1 › opening a shared URL on a clean slate restores the overrides
5771:  �[1A�[2K[79/75] [webkit] › tests-e2e/share.spec.js:41:1 › the Share button copies a link with the config fragment
5772:  �[1A�[2K[80/75] [webkit] › tests-e2e/shell.spec.js:16:1 › lands on the Overview with the setup checklist
5773:  �[1A�[2K[81/75] [webkit] › tests-e2e/shell.spec.js:29:1 › every destination renders console-clean at 3 widths
5774:  �[1A�[2K[82/75] [webkit] › tests-e2e/shell.spec.js:39:1 › every category panel leads with Settings, with All variables collapsed below
5775:  �[1A�[2K[83/75] [webkit] › tests-e2e/shell.spec.js:53:1 › a domain with no curated Settings shows the catalogue inline
5776:  �[1A�[2K[84/75] [webkit] › tests-e2e/shell.spec.js:61:1 › [ and ] cycle Overview + every domain with wrap-around
5777:  �[1A�[2K[85/75] [webkit] › tests-e2e/shell.spec.js:74:1 › domain and output format persist across reload (validated restore)
5778:  �[1A�[2K[86/75] [webkit] › tests-e2e/shell.spec.js:87:1 › a legacy persisted mode field is ignored on restore
5779:  �[1A�[2K  11 failed
5780:  [chromium] › tests-e2e/a11y.spec.js:59:1 › segmented controls expose aria-pressed; info buttons aria-expanded 
5781:  [chromium] › tests-e2e/domain-preview.spec.js:20:1 › every token domain leads with an open Preview card 
5782:  [chromium] › tests-e2e/domain-preview.spec.js:40:1 › the Layout preview renders distinct, proportional container widths 
5783:  [chromium] › tests-e2e/domain-preview.spec.js:57:1 › the preview reflects a live override ──────
5784:  [chromium] › tests-e2e/generator.spec.js:14:1 › type generator writes engine scalars only ──────
5785:  [chromium] › tests-e2e/generator.spec.js:27:1 › display tab: ratio selects are read-only (shared with type) 
5786:  [chromium] › tests-e2e/generator.spec.js:35:1 › viewport range is shared: space sets it, type seeds it, reset clears it 
5787:  [chromium] › tests-e2e/generator.spec.js:56:1 › "Reset" returns the ramp to framework defaults ─
5788:  [chromium] › tests-e2e/generator.spec.js:67:1 › edge inputs never persist garbage values ───────
5789:  [chromium] › tests-e2e/preview.spec.js:84:1 › preview is live: editing a brand color repaints the sample button 
5790:  [chromium] › tests-e2e/undo-redo.spec.js:9:1 › 5-step mixed chain unwinds and replays exactly ──
5791:  2 skipped
5792:  62 passed (9.8m)
5793:  ##[error]Process completed with exit code 1.
5794:  Post job cleanup.

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