Skip to content

feat(bricks): add in-builder Color System panel with light/dark preview - #193

Merged
jackgranatowski merged 3 commits into
mainfrom
claude/bricks-color-palette-dx-0lxMJ
Jun 2, 2026
Merged

feat(bricks): add in-builder Color System panel with light/dark preview#193
jackgranatowski merged 3 commits into
mainfrom
claude/bricks-color-palette-dx-0lxMJ

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

A floating Color System panel for the Bricks builder, launched from a
corner pill. Browses every --sf-color-* token grouped by brand/status
family (shades & tints, transparent steps, semantic aliases) plus a
combined Semantic group.

Leans into SLASHED's adaptive light-dark() model: each swatch previews
BOTH variants at once (diagonal split in "Both" mode), and the
Light/Dark toggle drives the live canvas [data-theme] so real elements
adapt while browsing. Picking a swatch copies its var(--sf-color-*)
reference and applies it to the selected element's chosen target
(background / text / border) — always the variable, never a baked hex,
so elements stay theme- and dark-mode-aware. Degrades to copy-only with
no selection.

  • color-resolver: add resolve_dark() deriving dark sources + semantic
    tokens via the framework's own dark formulas; share the family-scale
    builder between modes.
  • inventory: add get_color_hex_map_dark() (cached, override-aware).
  • rebemer-enqueue: localise the ordered token list + light/dark hex maps
    behind a new slashed_bricks/show_color_panel filter.
  • editor-app: pure, unit-tested color-model.js (grouping/filter/value
    helpers) + ColorApp/ColorPanel/ColorSwatch/ColorLauncher Svelte UI;
    bricks-api gains active-element detection and color application.
  • tests/color-model.test.js wired into the pretest suite.

https://claude.ai/code/session_01HPTgyrXeZBfqrwFpa78d3F

Summary by CodeRabbit

  • New Features

    • Introduced a Color System Panel in the Bricks builder for managing design tokens with Light/Dark adaptive previews, clipboard copy functionality, and direct element styling capabilities.
    • Added searchable color token organization grouped by type with target selection (background, text, border).
  • Documentation

    • Updated Bricks integration README documenting the new Color System Panel feature and configuration options.
  • Tests

    • Added comprehensive unit tests for color-model utilities.

claude added 2 commits June 2, 2026 04:09
A floating Color System panel for the Bricks builder, launched from a
corner pill. Browses every --sf-color-* token grouped by brand/status
family (shades & tints, transparent steps, semantic aliases) plus a
combined Semantic group.

Leans into SLASHED's adaptive light-dark() model: each swatch previews
BOTH variants at once (diagonal split in "Both" mode), and the
Light/Dark toggle drives the live canvas [data-theme] so real elements
adapt while browsing. Picking a swatch copies its var(--sf-color-*)
reference and applies it to the selected element's chosen target
(background / text / border) — always the variable, never a baked hex,
so elements stay theme- and dark-mode-aware. Degrades to copy-only with
no selection.

- color-resolver: add resolve_dark() deriving dark sources + semantic
  tokens via the framework's own dark formulas; share the family-scale
  builder between modes.
- inventory: add get_color_hex_map_dark() (cached, override-aware).
- rebemer-enqueue: localise the ordered token list + light/dark hex maps
  behind a new slashed_bricks/show_color_panel filter.
- editor-app: pure, unit-tested color-model.js (grouping/filter/value
  helpers) + ColorApp/ColorPanel/ColorSwatch/ColorLauncher Svelte UI;
  bricks-api gains active-element detection and color application.
- tests/color-model.test.js wired into the pretest suite.

https://claude.ai/code/session_01HPTgyrXeZBfqrwFpa78d3F
…panel

Addresses two gaps in the Color System panel:

1. Custom dark variants were ignored. resolve_dark() always auto-derived
   the dark value from the light source, so a user's explicit dark
   override (admin Dark-mode overrides, theme, or hand-written CSS) was
   not reflected. derive_dark_sources() now honours an explicit
   --sf-color-{family}-dark exactly like the framework's CSS fallback
   chain — var(--sf-color-X-dark, <derived>) — and the whole dark scale
   (shades/tints/alpha/aliases/semantics) follows the override. The
   inventory's admin-override reader now also surfaces brand_dark_* /
   status_dark_* (gated by the same dark_overrides_enabled flag the CSS
   generator uses), so the preview matches the emitted CSS.

2. Organized, guided grouping. Each family group now carries a role
   tagline ("Interactive & links") and a when-to-use hint, shown in the
   group header, so users know which colour is which and when to reach
   for it. The catch-all Semantic group is split into purpose-based
   labelled subsections (Text on color, Text, Interactive states,
   Surfaces, Borders, Links, Selection & marks, Code) instead of one
   flat list — against the real inventory every token lands in a named
   section with nothing left over.

color-model.js gains FAMILY_INFO + semantic subgrouping (pure, covered
by new unit tests).

https://claude.ai/code/session_01HPTgyrXeZBfqrwFpa78d3F
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

More reviews will be available in 47 minutes and 40 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: d031aa0d-00c7-4681-9cf9-f1ca51369bef

📥 Commits

Reviewing files that changed from the base of the PR and between a3f3763 and 89be8cd.

📒 Files selected for processing (4)
  • plugins/SLASHED-for-WP/integrations/bricks/assets/editor-app/app.js
  • plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/components/ColorPanel.svelte
  • plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/bricks-api.js
  • plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/color-model.js
📝 Walkthrough

Walkthrough

A new in-builder Color System panel displays --sf-color-* design tokens with Light/Dark adaptive previews. It enables clipboard copy of token references and applies selected colors to builder elements. The feature includes server-resolved light/dark hex maps, dark-mode color resolution, Svelte UI components, and configuration hooks to enable/disable the panel.

Changes

Color System Panel Feature

Layer / File(s) Summary
Color Model & Tests
plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/color-model.js, tests/color-model.test.js, package.json
Introduces a DOM-free color token data model that classifies, labels, groups, and filters --sf-color-* variables. Tests validate token parsing (base/scale/alpha/alias), grouping order, filtering, dark fallback, and mode-based hex selection. npm pretest script updated to run color model tests.
Backend Color Resolution with Dark Mode
plugins/SLASHED-for-WP/integrations/bricks/includes/class-color-resolver.php
Adds resolve_dark() method for dark-mode color generation. Refactors resolve() to use shared build_family_scales() helper that composites alpha over provided backdrop (white for light, dark base for dark). Introduces derive_dark_sources() and resolve_semantic_tokens_dark() for dark-mode semantic tokens with family-specific formulas.
Inventory Dark-Mode Caching & Admin Overrides
plugins/SLASHED-for-WP/integrations/bricks/includes/class-inventory.php
Adds per-request cached dark-mode hex map via get_color_hex_map_dark(). Extends admin color override generation to emit both -light and -dark mappings for brand/status tokens when dark overrides are enabled, aligning light and dark resolver inputs.
Editor Configuration & Color Panel Mounting
plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/main.js, plugins/SLASHED-for-WP/integrations/bricks/includes/class-rebemer-enqueue.php
Stores editor config in module scope for reuse. Adds mountColorApp() / unmountColorApp() lifecycle for the ColorApp component. Enqueue logic adds filter-controlled slashed_bricks/show_color_panel feature flag and localizes color token variables and light/dark hex maps to the editor bundle.
Bricks API Helpers for Element & Color State
plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/bricks-api.js
Adds getActiveElementId() to resolve currently selected element from builder state with DOM fallback. Exports COLOR_TARGET_KEYS for color control locations. Implements setElementColor() to apply raw CSS values to element style controls via Bricks mutations. Adds getElementLabel() for element labels.
Color Panel UI Components
plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/components/ColorApp.svelte, ColorLauncher.svelte, ColorPanel.svelte, ColorSwatch.svelte
ColorApp orchestrates open/closed state and mounts panel on demand. ColorLauncher renders a bottom-right toggle button. ColorPanel builds color models from source, filters by search query, handles mode switching ("Light"/"Dark"/"Both"), implements canvas theme preview, and manages clipboard copy + element color application with toast feedback. ColorSwatch renders individual swatches with mode-based hex selection and click-to-apply.
Styling & Documentation
plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/styles/panel.css, plugins/SLASHED-for-WP/integrations/bricks/assets/editor-app/app.css, plugins/SLASHED-for-WP/integrations/bricks/README.md
Adds comprehensive CSS for the color panel launcher pill (bottom-right fixed positioning), right-docked panel shell, toolbar with segmented mode controls, search input, and target/apply-to chips. Defines grouped section layout, grid-style family tiles, list-style swatch rows, and alpha/translucent split-mode variants. Info toast styling added. README documents the Color System Panel feature and filter configuration.

Sequence Diagrams

sequenceDiagram
  participant Editor as Bricks Editor
  participant Server as PHP Backend
  participant ColorApp as ColorApp
  participant Panel as ColorPanel
  participant Swatch as ColorSwatch
  Editor->>Server: Request editor config (color variables, light/dark hex maps)
  Server->>Server: resolve_dark() light/dark colors
  Server->>Server: get_color_hex_map_dark() cached result
  Server-->>Editor: colorPanel { variables, light, dark }
  Editor->>ColorApp: Mount with source data
  ColorApp->>Panel: Render with model data
  Panel->>Swatch: Render grouped swatches
  Swatch-->>Panel: Click swatch → onPick()
  Panel->>Panel: Copy var(--sf-color-*) to clipboard
  Panel->>Panel: Apply color to active element (if selected)
  Panel-->>Editor: Toast feedback (success/error/info)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

  • codeslash-dev/SLASHED#183: Directly addresses the objective of showing resolved color swatches in the Bricks builder via the same resolver/inventory/enqueue codepaths introduced in this PR.

Possibly related PRs

  • codeslash-dev/SLASHED#83: Extends the same Slashed_Bricks_Inventory implementation by adding dark-mode hex-map caching and API in class-inventory.php, building on inventory-based parsing shared with the main PR.
  • codeslash-dev/SLASHED#189: Both PRs modify class-color-resolver.php's color/semantic token resolution flow; the main PR adds dark-mode resolver refactors while PR #189 extends the resolved hex map with missing *-strong status entries.
  • codeslash-dev/SLASHED#97: Main PR extends the Bricks color-resolution pipeline in class-color-resolver.php and dark-mode hex-map caching in class-inventory.php, building directly on swatch-hex resolver/inventory fixes introduced in that PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature: an in-builder Color System panel with light/dark preview capability, which aligns with the core changes across color-resolver, inventory, editor-app components, and tests.
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/bricks-color-palette-dx-0lxMJ

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: 1

🧹 Nitpick comments (3)
plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/bricks-api.js (1)

296-309: 💤 Low value

Clarify comment about "leaving sibling fields untouched".

Line 270 states "leaving any sibling hex/hsl/rgb fields untouched", but line 307 replaces the entire color control object with { raw: rawValue }, which would clear existing sibling fields rather than preserve them. The code is functionally correct for the use case—var(--sf-color-*) references can't be resolved to hex/hsl/rgb at edit time, so having only raw is appropriate. The comment could be clearer that we're deliberately creating a minimal object rather than preserving existing structure.

📝 Proposed comment revision
  * These are the standard Bricks "_" style controls; a colour control stores
  * an object whose `raw` field carries the literal CSS value (so a
- * `var(--sf-color-*)` reference round-trips intact). We only ever write
- * `raw`, leaving any sibling hex/hsl/rgb fields untouched.
+ * `var(--sf-color-*)` reference round-trips intact). We set only the `raw`
+ * field (Bricks can't resolve CSS variables to hex/hsl/rgb at edit time).
🤖 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 `@plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/bricks-api.js`
around lines 296 - 309, The comment about "leaving any sibling hex/hsl/rgb
fields untouched" is misleading because setElementColor (function
setElementColor) intentionally replaces the color control object with a minimal
{ raw: rawValue } (see where el.settings[group][key] is assigned), which clears
any sibling fields; update the comment near that code to explicitly state we
deliberately create a minimal color object containing only raw for var(...)
values and do not preserve hex/hsl/rgb siblings, so future readers understand
this is intentional rather than a bug.
plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/color-model.js (2)

226-238: 💤 Low value

Add alphabetical tiebreaker for unrecognized aliases.

When both aliases are absent from ALIAS_ORDER, they receive rank 999 and the comparator returns 0, leaving their relative order non-deterministic. The compareSemantic function (line 249) includes an alphabetical tiebreaker; consider the same here for consistent, reproducible sorting.

📋 Proposed tiebreaker
   if (a.info.kind === 'alias') {
     const ai = ALIAS_ORDER.indexOf(a.info.step);
     const bi = ALIAS_ORDER.indexOf(b.info.step);
-    return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
+    const ar = ai === -1 ? 999 : ai;
+    const br = bi === -1 ? 999 : bi;
+    if (ar !== br) return ar - br;
+    return String(a.info.step).localeCompare(String(b.info.step));
   }
🤖 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 `@plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/color-model.js`
around lines 226 - 238, The compareInFamily comparator can return 0 when both
alias steps are missing from ALIAS_ORDER, causing non-deterministic order;
update the alias branch in compareInFamily to, after computing ai/bi and falling
back to 999, if ai === -1 && bi === -1 (or both mapped to the 999 fallback) use
a lexical tiebreaker comparing a.info.step and b.info.step (same approach as
compareSemantic) to return a deterministic alphabetical order (case-insensitive
if compareSemantic does that).

28-34: ⚖️ Poor tradeoff

Family list duplication across language boundaries.

The brand and status family lists are independently defined here (lines 31, 34), in class-color-resolver.php (line 37–49 as $default_sources keys), and in class-inventory.php (lines 245–246). If a new family is added or one is renamed, all three locations must be updated in sync. Consider documenting this constraint in each location or centralizing the list in a shared data file that both JS and PHP can consume (e.g., a JSON schema or generated constants).

🤖 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 `@plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/color-model.js`
around lines 28 - 34, BRAND_FAMILIES and STATUS_FAMILIES are duplicated across
JS and PHP (BRAND_FAMILIES, STATUS_FAMILIES in color-model.js vs
$default_sources in class-color-resolver.php and lists in class-inventory.php);
centralize these lists by extracting them to a shared JSON (e.g.,
colors-families.json) and update color-model.js to import/require that JSON and
update class-color-resolver.php and class-inventory.php to read/parse the same
JSON, or alternately add a clear TODO comment in each location referencing the
canonical source and documenting the need to update all locations when changing
families; ensure the shared symbol names (BRAND_FAMILIES / STATUS_FAMILIES) are
preserved so existing code paths (color-model.js functions) continue to work.
🤖 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
`@plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/components/ColorPanel.svelte`:
- Around line 127-147: When pick(...) detects an active element id but
api.setElementColor(id, target, value) returns false, don't fall through to the
generic copy-only toast; instead set a specific toast explaining the color
couldn't be applied to that element (use targetLabel and the element name from
api.getElementLabel(id) or 'element'). If copying succeeded, include that the
value was copied as a secondary note (e.g., "Copied X — paste into any Bricks
colour field; this element doesn't support {targetLabel}"), otherwise show a
clear error/warning about unsupported color application; update the pick
function's applied check to set that specific toast before returning.

---

Nitpick comments:
In `@plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/bricks-api.js`:
- Around line 296-309: The comment about "leaving any sibling hex/hsl/rgb fields
untouched" is misleading because setElementColor (function setElementColor)
intentionally replaces the color control object with a minimal { raw: rawValue }
(see where el.settings[group][key] is assigned), which clears any sibling
fields; update the comment near that code to explicitly state we deliberately
create a minimal color object containing only raw for var(...) values and do not
preserve hex/hsl/rgb siblings, so future readers understand this is intentional
rather than a bug.

In
`@plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/color-model.js`:
- Around line 226-238: The compareInFamily comparator can return 0 when both
alias steps are missing from ALIAS_ORDER, causing non-deterministic order;
update the alias branch in compareInFamily to, after computing ai/bi and falling
back to 999, if ai === -1 && bi === -1 (or both mapped to the 999 fallback) use
a lexical tiebreaker comparing a.info.step and b.info.step (same approach as
compareSemantic) to return a deterministic alphabetical order (case-insensitive
if compareSemantic does that).
- Around line 28-34: BRAND_FAMILIES and STATUS_FAMILIES are duplicated across JS
and PHP (BRAND_FAMILIES, STATUS_FAMILIES in color-model.js vs $default_sources
in class-color-resolver.php and lists in class-inventory.php); centralize these
lists by extracting them to a shared JSON (e.g., colors-families.json) and
update color-model.js to import/require that JSON and update
class-color-resolver.php and class-inventory.php to read/parse the same JSON, or
alternately add a clear TODO comment in each location referencing the canonical
source and documenting the need to update all locations when changing families;
ensure the shared symbol names (BRAND_FAMILIES / STATUS_FAMILIES) are preserved
so existing code paths (color-model.js functions) continue to work.
🪄 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: 2ac24631-81a3-4711-871b-2c39481a5d2f

📥 Commits

Reviewing files that changed from the base of the PR and between c7204a1 and a3f3763.

📒 Files selected for processing (16)
  • package.json
  • plugins/SLASHED-for-WP/integrations/bricks/README.md
  • plugins/SLASHED-for-WP/integrations/bricks/assets/editor-app/app.css
  • plugins/SLASHED-for-WP/integrations/bricks/assets/editor-app/app.js
  • plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/components/ColorApp.svelte
  • plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/components/ColorLauncher.svelte
  • plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/components/ColorPanel.svelte
  • plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/components/ColorSwatch.svelte
  • plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/bricks-api.js
  • plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/lib/color-model.js
  • plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/main.js
  • plugins/SLASHED-for-WP/integrations/bricks/editor-app/src/styles/panel.css
  • plugins/SLASHED-for-WP/integrations/bricks/includes/class-color-resolver.php
  • plugins/SLASHED-for-WP/integrations/bricks/includes/class-inventory.php
  • plugins/SLASHED-for-WP/integrations/bricks/includes/class-rebemer-enqueue.php
  • tests/color-model.test.js

- color-model: drop the unused SCALE_STEPS constant (CodeQL dead-code);
  add a deterministic lexical tiebreaker for unranked aliases; note the
  JS/PHP family-list mirror so the three definitions stay in sync.
- bricks-api: clarify that setElementColor intentionally writes a minimal
  { raw } object (Bricks can't resolve a CSS var to hex/hsl/rgb at edit
  time), rather than implying it preserves sibling fields.
- ColorPanel: when an element is selected but apply fails (e.g. a stale
  id from the DOM fallback), say so instead of the generic copy-only
  toast.

https://claude.ai/code/session_01HPTgyrXeZBfqrwFpa78d3F
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