Skip to content

feat(admin): richer live preview — motion, focus ring, card, dark mode toggle, CSS export - #151

Merged
jackgranatowski merged 4 commits into
mainfrom
claude/repo-issues-closure-HBJBq
May 29, 2026
Merged

feat(admin): richer live preview — motion, focus ring, card, dark mode toggle, CSS export#151
jackgranatowski merged 4 commits into
mainfrom
claude/repo-issues-closure-HBJBq

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented May 29, 2026

Copy link
Copy Markdown
Contributor

LivePreview:

  • Replace static dark section with ☀/☾ Light/Dark toggle; all swatches,
    buttons and card instantly flip to the selected mode's color variants.
  • Extend inlineStyle to compute --preview-radius-* and --preview-shadow
    from the live radius_scale / shadow_strength tokens so the card updates
    when those tabs change.
  • Add a card component preview showing colors + typography + radius +
    shadow together (header accent strip, avatar, badges, action button).
  • Add Outline and Ghost button variants to the button row.

MiscTab:

  • Add motion preview: 5 animated fill-bars (instant → slower) running at
    their computed duration (base ms × motionScale) so the effect of
    changing Motion Scale is immediately visible.
  • Add focus ring preview: a live focusable button that renders the exact
    outline-width, outline-offset and outline-style set in the fields above.

SpacingPreview:

  • Add a container card below the scale bars showing padding (space-m) and
    gap (space-s) applied to a real card layout; values update with the
    viewport slider so fluid spacing is visible in context.

ExportImportTab:

  • Add "Download CSS file" button alongside "Download token file (.json)"
    so the standalone @layer CSS can be grabbed directly from this tab
    (mirrors the Export CSS button already present in SaveBar).

https://claude.ai/code/session_019MD9F8rrpYzCeDTmSTLA1K

Summary by CodeRabbit

Release Notes

  • New Features

    • Added dark mode toggle and customization controls
    • Added CSS file export capability
    • Added Motion and Focus ring live previews
    • Added container card preview for spacing layouts
  • Improvements

    • Enhanced live preview with reactive dark mode support
    • Improved dark color auto-derivation with override controls
    • Refined element naming in design panels
    • Updated row indentation styling system

Review Change Stack

claude added 3 commits May 29, 2026 11:28
…e toggle, CSS export

LivePreview:
- Replace static dark section with ☀/☾ Light/Dark toggle; all swatches,
  buttons and card instantly flip to the selected mode's color variants.
- Extend inlineStyle to compute --preview-radius-* and --preview-shadow
  from the live radius_scale / shadow_strength tokens so the card updates
  when those tabs change.
- Add a card component preview showing colors + typography + radius +
  shadow together (header accent strip, avatar, badges, action button).
- Add Outline and Ghost button variants to the button row.

MiscTab:
- Add motion preview: 5 animated fill-bars (instant → slower) running at
  their computed duration (base ms × motionScale) so the effect of
  changing Motion Scale is immediately visible.
- Add focus ring preview: a live focusable button that renders the exact
  outline-width, outline-offset and outline-style set in the fields above.

SpacingPreview:
- Add a container card below the scale bars showing padding (space-m) and
  gap (space-s) applied to a real card layout; values update with the
  viewport slider so fluid spacing is visible in context.

ExportImportTab:
- Add "Download CSS file" button alongside "Download token file (.json)"
  so the standalone @layer CSS can be grabbed directly from this tab
  (mirrors the Export CSS button already present in SaveBar).

https://claude.ai/code/session_019MD9F8rrpYzCeDTmSTLA1K
ColorTab: add "Custom dark mode colors" toggle (stored as
colors.dark_overrides_enabled). When OFF, the -dark color fields are
hidden and no -dark declarations are emitted — the CSS framework's
built-in relative-color-syntax auto-derivation takes over:
  oklch(from light clamp(0.65, 0.95−l·0.5, 0.88) c·0.9 h)
Values are preserved in the DB so toggling back re-enables them.

class-css-generator.php + export.js: skip brand_dark_* / status_dark_*
declarations when dark_overrides_enabled === '0'. Default (key absent
or '1') preserves existing behaviour.

tests/color-semantic.spec.js: add 4 Playwright tests (Chromium — all
pass) confirming the auto-derivation actually runs end-to-end:
 · primary is lighter in dark theme than light theme
 · all 6 brand colors obey the formula (base inverts, others brighten)
 · base inverts near-white → near-dark (luminance > 0.7 vs < 0.08)
 · explicit -dark override takes precedence over auto-derived fallback

https://claude.ai/code/session_019MD9F8rrpYzCeDTmSTLA1K
…xt-aware naming

- Add `align-self: center` to `.rebemer-badge-host` so it no longer
  stretches to the full row height in Bricks' flex structure panel.
  The badge now sits as a small inline word rather than a tall block.

- Replace the CSS `[data-depth="1/2/3"]` margin rules (which capped
  indentation at 3 levels) with a `--rebemer-row-depth` CSS custom
  property. Row.svelte sets the property from `row.depth` directly —
  any subtree depth now renders with the correct 12px-per-level indent.

- Add context-aware second pass in BemPanel `onMount` that refines
  auto-suggested names before rows are rendered:
  - Layout containers (section/container/block/div) now get names
    inferred from their children: `actions`, `header`, `body`, `footer`,
    `media`, `content`, `form`, `nav`, etc. instead of always `item`.
  - Sole-child elements get semantic overrides: `heading` → `title`,
    `button` → `action`, `text`/`text-basic` → `description`.
  - Elements the user already labelled are never touched.

https://claude.ai/code/session_019MD9F8rrpYzCeDTmSTLA1K
@jackgranatowski

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

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 36 minutes and 29 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

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

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

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: 7639522e-cd51-43ae-b83c-d515ebff829e

📥 Commits

Reviewing files that changed from the base of the PR and between a00b7ea and 8584bb5.

📒 Files selected for processing (8)
  • integrations/bricks/admin-app/src/components/ExportImportTab.svelte
  • integrations/bricks/admin-app/src/components/LivePreview.svelte
  • integrations/bricks/admin-app/src/components/MiscTab.svelte
  • integrations/bricks/assets/admin-app/app.js
  • integrations/bricks/assets/editor-app/app.js
  • integrations/bricks/editor-app/src/lib/element-types.js
  • package.json
  • tests/element-types.test.js
📝 Walkthrough

Walkthrough

This PR introduces a toggleable dark-mode override system in the admin app, gates CSS generation in both JavaScript and PHP export pipelines by a new dark_overrides_enabled flag, enhances live previews with dark-mode support and new motion/focus/spacing previews, adds CSS file export capability, and refines BEM element naming logic in the editor app via helper functions and two-pass naming refinement.

Changes

Dark-Mode Overrides, Admin UI Enhancements, and BEM Naming

Layer / File(s) Summary
Dark-mode override toggle flag
integrations/bricks/admin-app/src/components/ColorTab.svelte
ColorTab imports field readers/writers, derives a reactive darkOn boolean from colors.dark_overrides_enabled, implements toggleDark() to flip the flag, and renders a toggle UI with conditional rendering of dark color editors gated by the flag state.
JavaScript export conditional dark generation
integrations/bricks/admin-app/src/lib/export.js
export.js removes unconditional dark brand output and wraps both brand-dark and status-dark CSS declarations in a darkEnabled conditional derived from settings.dark_overrides_enabled, skipping dark overrides when explicitly disabled.
PHP export conditional dark generation
integrations/bricks/includes/class-css-generator.php
class-css-generator.php removes unconditional dark brand output and adds a dark_overrides_enabled toggle to conditionally generate brand_dark_* and status_dark_* CSS declarations, defaulting to enabled when absent.
LivePreview dark-mode state and generated CSS
integrations/bricks/admin-app/src/components/LivePreview.svelte
LivePreview adds reactive darkMode state and derived mode suffix, computes inline CSS variables for radius/shadow, derives full generated CSS via generateExportCSS(tokens), and completely rewrites the template and styles to support dark-mode swatch/button selection and an enriched card preview.
CSS export download capability
integrations/bricks/admin-app/src/components/ExportImportTab.svelte
ExportImportTab imports the tokens store, derives canExportCSS from override presence, implements downloadCSS() to generate and download a date-stamped CSS file, and adds a secondary "Download CSS file" button with conditional disabling and tooltip.
Motion and focus ring live previews
integrations/bricks/admin-app/src/components/MiscTab.svelte
MiscTab introduces derived values for motion scale, duration arrays, and focus-ring parameters, renders a motion preview section with animated bars and milliseconds, and adds a focus ring preview button with CSS variable bindings and descriptive text.
Spacing container card preview
integrations/bricks/admin-app/src/components/SpacingPreview.svelte
SpacingPreview derives a cardSpacing value from viewport interpolation and token overrides, renders a container card section with padding/gap bindings, and adds corresponding CSS layout classes.
Admin app preview visual updates
integrations/bricks/assets/admin-app/app.css
app.css reworks minified slashed-preview styling for typography, swatches, buttons (outline/ghost variations), dark-mode selectors, and preview card sub-elements including accent bar, body, footer, badges, and caption/code blocks.
BEM naming helper functions
integrations/bricks/editor-app/src/lib/element-types.js
element-types.js exports SOLE_CHILD_LABEL_OVERRIDES mapping element types to semantic BEM labels and suggestContainerName() which derives container labels from child element types with position-based fallbacks.
BEM panel two-pass row naming
integrations/bricks/editor-app/src/components/BemPanel.svelte
BemPanel.svelte implements two-pass initialization: first seeds row names from element labels/types, then refines via parent-child context using the new naming helpers, applies sole-type semantic overrides, and skips user-labeled elements.
Row depth CSS variable styling
integrations/bricks/editor-app/src/components/Row.svelte, integrations/bricks/editor-app/src/styles/panel.css, integrations/bricks/assets/editor-app/app.css
Row.svelte and panel.css replace fixed data-depth attribute indentation with --rebemer-row-depth CSS variable; app.css (editor) updates rebemer styling to use the variable-based margin calculation; badge alignment is centered.
Dark-mode auto-derivation tests
tests/color-semantic.spec.js
color-semantic.spec.js adds a "Dark mode auto-derivation" test suite validating light-dark() fallback behavior: confirms brand tokens lighten in dark theme while base darkens, verifies bg color inversion, and confirms explicit dark overrides take precedence.

Possibly Related PRs

  • codeslash-dev/SLASHED#141: Both PRs modify the same Playwright file tests/color-semantic.spec.js, with the main PR adding a "Dark mode auto-derivation" test suite that extends the color semantic/theme coverage.
  • codeslash-dev/SLASHED#25: The main PR's dark_overrides_enabled gating controls whether *-dark token declarations are emitted, which directly affects the "dark falls back to light via light-dark()" behavior introduced in core tokens.
  • codeslash-dev/SLASHED#32: Both PRs change behavior around -dark token overrides so dark theme values are auto-derived unless explicit dark overrides are enabled/set.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly reflects the main features added across the PR: richer live preview with motion, focus ring, card, dark mode toggle, and CSS export functionality.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 claude/repo-issues-closure-HBJBq

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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
tests/color-semantic.spec.js (1)

252-327: ⚡ Quick win

Ensure Playwright tests use rebuilt dist bundles

  • tests/fixture.html loads ../dist/slashed.full.css.
  • CI runs npm run build before the Playwright test step, so the checks run against freshly generated CSS.
  • Local npm test won’t rebuild dist (no pretest hook; test is just playwright test), so consider adding a pretest step or documenting npm run build as a prerequisite.
🤖 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 `@tests/color-semantic.spec.js` around lines 252 - 327, The Playwright tests
load ../dist/slashed.full.css from tests/fixture.html but local npm test doesn't
rebuild dist, so CI passes because it runs npm run build beforehand; fix by
adding an automatic build step before tests (e.g., add a pretest script that
runs npm run build) or document that developers must run npm run build before
running playwright test; update package.json's "test" lifecycle (add "pretest":
"npm run build") or developer docs accordingly so tests in
tests/color-semantic.spec.js run against the rebuilt dist artifact.
integrations/bricks/editor-app/src/lib/element-types.js (1)

151-184: ⚡ Quick win

Add focused unit tests for suggestContainerName (heuristic regression coverage)

No current unit/spec coverage references suggestContainerName or lib/element-types.js; the helper is only defined in integrations/bricks/editor-app/src/lib/element-types.js (lines 151-184) and consumed by integrations/bricks/editor-app/src/components/BemPanel.svelte. Add table-driven tests covering every ordered return path (including the heading+text+button case) and the positional fallback (header/footer/body/content).

🤖 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 `@integrations/bricks/editor-app/src/lib/element-types.js` around lines 151 -
184, Add focused unit tests for the heuristic function suggestContainerName in
integrations/bricks/editor-app/src/lib/element-types.js: create table-driven
tests that exercise every distinct return path of suggestContainerName (cases:
form, nav, actions (button-only), media (image-only), header (heading-only and
heading+button and positional first sibling), body (text-only and positional
middle sibling), content (heading+text, default single-sibling fallback),
icon-group, list-wrap, and footer (positional last sibling)). For each row call
suggestContainerName with appropriate childTypes arrays and
positionAmongContainerSiblings/totalContainerSiblings values and assert the
exact returned string, including the specific combined case heading+text+button
to ensure it returns 'content'. Use the test framework already in the repo and
keep tests table-driven to cover all ordered return paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@integrations/bricks/admin-app/src/components/ExportImportTab.svelte`:
- Around line 10-22: The export button enables when there is generated CSS, not
merely any non-empty token flag: replace the current canExportCSS =
$derived(hasOverrides(tokens)) logic with a derived value based on
generateExportCSS(tokens) (e.g., compute css = generateExportCSS(tokens) and set
canExportCSS to Boolean(css.length)) so the button is disabled when
generateExportCSS would return an empty string; update downloadCSS to rely on
that same generated css (call generateExportCSS(tokens) once) and remove the
hasOverrides import if it becomes unused.

In `@integrations/bricks/admin-app/src/components/LivePreview.svelte`:
- Around line 67-69: The code computes ss with
parseFloat(shadows.shadow_strength ?? meta.defaults?.shadows?.shadow_strength ??
0.08) which lets an empty-string token become NaN; update the guard so empty
strings fall back too (e.g., use logical OR or an explicit empty-string check)
before calling parseFloat: read shadows.shadow_strength, fall back to
meta.defaults?.shadows?.shadow_strength, then to 0.08 if the value is
null/undefined/empty-string, and assign that sanitized value to ss (the variable
used in the pairs.push for --preview-shadow) so parseFloat never receives an
empty string.

In `@integrations/bricks/admin-app/src/components/MiscTab.svelte`:
- Around line 61-66: focusRingWidth and focusRingOffset call parseFloat directly
and use ?? fallbacks, so an empty-string token yields NaN; mirror the
empty-string guard used by durationMs: treat tokens.contrast?.focus_ring_width
and tokens.contrast?.focus_ring_offset as "unset" when they are '' before
parsing, then call parseFloat on the guarded value and fall back to
contrastDefaults (e.g. use the same conditional check durationMs uses to detect
'' and only parse when non-empty), keeping the derived stores named
focusRingWidth and focusRingOffset.

---

Nitpick comments:
In `@integrations/bricks/editor-app/src/lib/element-types.js`:
- Around line 151-184: Add focused unit tests for the heuristic function
suggestContainerName in integrations/bricks/editor-app/src/lib/element-types.js:
create table-driven tests that exercise every distinct return path of
suggestContainerName (cases: form, nav, actions (button-only), media
(image-only), header (heading-only and heading+button and positional first
sibling), body (text-only and positional middle sibling), content (heading+text,
default single-sibling fallback), icon-group, list-wrap, and footer (positional
last sibling)). For each row call suggestContainerName with appropriate
childTypes arrays and positionAmongContainerSiblings/totalContainerSiblings
values and assert the exact returned string, including the specific combined
case heading+text+button to ensure it returns 'content'. Use the test framework
already in the repo and keep tests table-driven to cover all ordered return
paths.

In `@tests/color-semantic.spec.js`:
- Around line 252-327: The Playwright tests load ../dist/slashed.full.css from
tests/fixture.html but local npm test doesn't rebuild dist, so CI passes because
it runs npm run build beforehand; fix by adding an automatic build step before
tests (e.g., add a pretest script that runs npm run build) or document that
developers must run npm run build before running playwright test; update
package.json's "test" lifecycle (add "pretest": "npm run build") or developer
docs accordingly so tests in tests/color-semantic.spec.js run against the
rebuilt dist artifact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e4ca6929-37c3-444a-9fea-93e919a4cd54

📥 Commits

Reviewing files that changed from the base of the PR and between 80eb06c and a00b7ea.

📒 Files selected for processing (16)
  • integrations/bricks/admin-app/src/components/ColorTab.svelte
  • integrations/bricks/admin-app/src/components/ExportImportTab.svelte
  • integrations/bricks/admin-app/src/components/LivePreview.svelte
  • integrations/bricks/admin-app/src/components/MiscTab.svelte
  • integrations/bricks/admin-app/src/components/SpacingPreview.svelte
  • integrations/bricks/admin-app/src/lib/export.js
  • integrations/bricks/assets/admin-app/app.css
  • integrations/bricks/assets/admin-app/app.js
  • integrations/bricks/assets/editor-app/app.css
  • integrations/bricks/assets/editor-app/app.js
  • integrations/bricks/editor-app/src/components/BemPanel.svelte
  • integrations/bricks/editor-app/src/components/Row.svelte
  • integrations/bricks/editor-app/src/lib/element-types.js
  • integrations/bricks/editor-app/src/styles/panel.css
  • integrations/bricks/includes/class-css-generator.php
  • tests/color-semantic.spec.js

Comment thread integrations/bricks/admin-app/src/components/ExportImportTab.svelte Outdated
Comment thread integrations/bricks/admin-app/src/components/LivePreview.svelte
Comment thread integrations/bricks/admin-app/src/components/MiscTab.svelte
ExportImportTab: derive canExportCSS from generateExportCSS(tokens)
directly so the button is disabled when the generated CSS string is
empty (e.g. only the dark-toggle flag is stored, which hasOverrides()
caught but generateExportCSS() skips). Removes the hasOverrides import.

LivePreview: guard shadow_strength against empty-string tokens — `??`
does not catch '', which made parseFloat produce NaN and broke the
--preview-shadow custom property.

MiscTab: apply the same empty-string guard to focusRingWidth and
focusRingOffset via a shared numField() helper, matching the pattern
already used by durationMs above them.

element-types: swap the `heading+button` and `heading+text` rules in
suggestContainerName so that a container holding heading+text+button
returns 'content' (rich content area) rather than 'header' (nav-like).

tests: add table-driven unit tests for suggestContainerName covering
all 21 return paths (form, nav, actions, media, header, body, content,
icon-group, list-wrap, positional header/body/footer, and the specific
heading+text+button → content case).

package.json: add pretest script that rebuilds dist and runs the new
unit tests before Playwright, so local npm test always exercises fresh
CSS bundles and catches element-types regressions automatically.

https://claude.ai/code/session_019MD9F8rrpYzCeDTmSTLA1K
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants