feat(gutenberg): full token palette, gradients, font-size/spacing presets + in-editor token panel - #260
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (6)
📝 WalkthroughWalkthroughThis PR adds shared token data classes (CSS parser, color resolver, inventory + fallback JSON), converts Bricks integrations into thin shims that reuse those shared classes, and implements a Gutenberg editor integration: presets, an enqueue/localize class, and a full overlay panel (JS+CSS) with apply/copy helpers and a color model. ChangesToken infrastructure and Gutenberg editor integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
jackgranatowski
left a comment
There was a problem hiding this comment.
Code Review — PR #260 feat(gutenberg): full token palette, gradients, font-size/spacing presets + in-editor token panel
Autofix commit pushed to feat/gutenberg-token-integration (80ee822).
Fixed (autofix applied ✅)
1. Missing aria-label on color swatch buttons — panel.js:308
The title attribute is not announced by most screen readers. Added 'aria-label': \${sw.label} (${hex})`alongside the existingtitle`.
2. Unguarded copyToClipboard in doCopy() — panel.js:461
copyToClipboard is async and can throw (e.g. Permissions API denied). Without a try/catch, the error surfaces as an unhandled promise rejection. Wrapped in try { ... } catch { toast('Copy failed'); }.
3. Missing null-guards in filterModel() — color-model.js:378
group.sections and section.swatches were iterated without defensive checks. If the model shape ever deviates, the panel throws instead of rendering an empty state. Added if (!Array.isArray(...)) continue guards.
4. Inconsistent isset/ternary in class-inventory.php — class-inventory.php:243,272
isset($inv['color_values']) ? $inv['color_values'] : array() replaced with $inv['color_values'] ?? array() for consistency with the rest of the file.
Recommendations (not autofixed — need design decisions)
5. Capability check too broad — class-editor-enqueue.php:44
current_user_can('edit_posts') grants access to the token panel for any author-level user. The panel allows applying theme-level color/gradient tokens to blocks. Consider whether edit_theme_options is more appropriate for the panel reveal, or at minimum document the intent in a comment so the decision is explicit.
6. Duplicate color classification logic — color-model.js (JS) vs class-presets.php (PHP)
classifyVar() in JS and classify_color() in PHP implement identical family/scale/alpha detection. If either is updated, the other will drift. Consider a code comment referencing the counterpart, or a future build step that generates one from the other.
7. Panel CSS dark mode — panel.css
Colors are hardcoded (#14152e, #fff). In a dark WP admin theme the contrast may be undesirable. Consider a @media (prefers-color-scheme: dark) block or using WP's CSS custom properties (--wp-admin-theme-color, etc.).
Generated by Claude Code
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 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 @.stylelintrc.json:
- Line 3: The ignore pattern in .stylelintrc.json uses "ignoreFiles" and
currently includes "plugins/SLASHED-for-WP/integrations/gutenberg/assets/**",
which unintentionally skips new source CSS like
plugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/panel.css; update
the ignore entry to only match built/distribution files (e.g. replace the broad
Gutenberg pattern with a more specific build/dist pattern such as excluding only
built dirs) or remove the Gutenberg pattern entirely so editor CSS is linted,
and while editing confirm that both bricks entries
("plugins/SLASHED-for-WP/integrations/bricks/assets/editor-app/**" and
"plugins/SLASHED-for-WP/integrations/bricks/editor-app/**") are intentionally
distinct and adjust them only if they were meant to be the same.
In `@plugins/SLASHED-for-WP/includes/class-inventory.php`:
- Line 154: The call uses late-static binding to invoke a private static method
— change the invocation in the get() cache initialization from static::resolve()
to self::resolve() so the private static function resolve() is callable from
this class; specifically update the line that currently does self::$cache[
static::class ] = self::sanitize_inventory( static::resolve() ); to call
self::resolve() (keep sanitize_inventory as-is) so subclasses like
Slashed_Bricks_Inventory / Slashed_Gutenberg_Inventory won’t trigger a
private-method visibility fatal.
In `@plugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/color-model.js`:
- Around line 139-149: The code currently skips '-light' source tokens but
leaves '-dark' to be classified as an alias, which surfaces source-only tokens
in the UI and breaks swatch behavior; update the suffix filtering in the
color-model logic (the block that currently checks `if (suffix === 'light')
return null;`) to also exclude `dark` (e.g. treat `suffix === 'light' || suffix
=== 'dark'` as non-renderable) so that `swatchValue()` never receives a `-dark`
token; keep the rest of the suffix handling for numeric scales, alpha
(`/^a[0-9]+$/`), and aliases unchanged.
In `@plugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/panel.js`:
- Around line 415-422: The toggle handler updates the row DOM class using
row.classList.toggle but never updates the checkmark span, so the check (the
el('span', { class: `${NS}-classrow__check`, text: active ? '✓' : '' })) becomes
out of sync; after toggling (where `now` is set) update the check span's text to
now ? '✓' : '' (e.g. find the child with class `${NS}-classrow__check` or update
the `active` variable to `now` before building) so the visual checkmark matches
the new active state.
- Around line 103-111: The change detection in onStoreChange currently only
compares selectedClientId() against lastSelId, which misses changes in
multi-select where the first ID can stay the same; update onStoreChange to
compute a full-selection identity (e.g., get the array/set of selected client
IDs via the selection API, normalize/sort them and stringify or produce a stable
key) instead of using selectedClientId(), compare that full key to lastSelId,
store the full key in lastSelId, and then call updateContextLine() and
renderBody() as before when the key changes (references: lastSelId,
selectedClientId()/selection retrieval, onStoreChange, updateContextLine,
renderBody, state.tab).
In
`@plugins/SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php`:
- Around line 61-65: The code currently calls
wp_enqueue_style(self::STYLE_HANDLE, $base_url . 'panel.css', array(), $css_ver)
unconditionally which causes 404s if panel.css is missing; update the block to
only call wp_enqueue_style when the file exists (use the same
file_exists($css_path) guard you used to compute $css_ver) so the stylesheet is
skipped when absent; reference the existing variables $css_path, $css_ver and
the function wp_enqueue_style/self::STYLE_HANDLE to locate and modify the code.
In `@plugins/SLASHED-for-WP/integrations/gutenberg/includes/class-presets.php`:
- Around line 145-165: The presets builder currently always injects
color.palette which can overwrite a theme's palette when empty; modify the
cleanup logic in class-presets.php after building $settings (which calls
build_palette(), build_gradients(), build_font_sizes(), build_spacing_sizes())
to mirror the other guards: if empty($settings['color']['palette'])
unset($settings['color']['palette']); and if both
empty($settings['color']['palette']) and empty($settings['color']['gradients'])
unset($settings['color']); this ensures you don't send an explicit empty palette
(or empty color group) to update_with.
🪄 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: 71b056d8-7a39-4a03-85b3-785b0a79e8b8
📒 Files selected for processing (17)
.stylelintrc.jsonplugins/SLASHED-for-WP/data/inventory.jsonplugins/SLASHED-for-WP/includes/class-color-resolver.phpplugins/SLASHED-for-WP/includes/class-css-parser.phpplugins/SLASHED-for-WP/includes/class-inventory.phpplugins/SLASHED-for-WP/integrations/bricks/includes/class-color-resolver.phpplugins/SLASHED-for-WP/integrations/bricks/includes/class-css-parser.phpplugins/SLASHED-for-WP/integrations/bricks/includes/class-inventory.phpplugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/apply.jsplugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/color-model.jsplugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/panel.cssplugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/panel.jsplugins/SLASHED-for-WP/integrations/gutenberg/includes/class-color-palette.phpplugins/SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.phpplugins/SLASHED-for-WP/integrations/gutenberg/includes/class-inventory.phpplugins/SLASHED-for-WP/integrations/gutenberg/includes/class-presets.phpplugins/SLASHED-for-WP/integrations/gutenberg/slashed-gutenberg.php
💤 Files with no reviewable changes (1)
- plugins/SLASHED-for-WP/integrations/gutenberg/includes/class-color-palette.php
…presets and token panel Replaces the stale hand-maintained 20-entry color palette with the full token set derived live from the active CSS bundle, and adds gradients, font-size and spacing presets to the native block-editor controls. Phase 1 - native presets (class-presets.php): - full --sf-color-* palette (var() values, family-ordered, slashed- slugs) - --sf-gradient-* gradient presets - --sf-text-* font sizes and --sf-space-* spacing sizes - registered via add_theme_support + wp_theme_json_data_theme Phase 2 - in-editor token panel (assets/editor + class-editor-enqueue.php): - floating, categorized, searchable browser for colors, gradients, classes and variables with light/dark hex previews - click-to-apply to the selected block (color/background/border, gradient, class toggle); copy var() fallback when nothing is selected Shared data layer: - promote the CSS parser, color resolver and inventory to includes/ as Slashed_CSS_Parser / Slashed_Color_Resolver / Slashed_Inventory - Bricks classes become thin subclasses preserving their filters, cache keys and paths; Gutenberg adds its own inventory subclass - per-class caches keep the two integrations independent Excludes the Gutenberg editor assets from stylelint (matches the existing Bricks editor-app convention). Co-authored-by: Jack Granatowski <contact@codeslash.net>
…anel - Add aria-label to color swatch buttons so screen readers announce token name and hex value (title attribute is not exposed by most screen readers) - Wrap copyToClipboard in try-catch so clipboard errors are surfaced as toast instead of uncaught promise rejections - Guard filterModel against missing group.sections / section.swatches to prevent runtime errors on malformed model data - Use null-coalescing operator (?? array()) instead of isset/ternary for consistency in class-inventory.php https://claude.ai/code/session_01GiM4ZqWCStCSrBvSwdqnfb
80ee822 to
9c2874a
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
.stylelintrc.json (1)
3-3:⚠️ Potential issue | 🟠 Major | ⚡ Quick winThe ignore pattern excludes new source CSS from linting.
The pattern
"plugins/SLASHED-for-WP/integrations/gutenberg/assets/**"will prevent the newly addedplugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/panel.cssfrom being linted. Sincepanel.cssis source CSS (not built output), it should be checked against the project's CSS rules.Either remove the Gutenberg pattern entirely or narrow it to exclude only built/dist subdirectories.
🤖 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 @.stylelintrc.json at line 3, The ignoreFiles entry in .stylelintrc.json currently contains the pattern "plugins/SLASHED-for-WP/integrations/gutenberg/assets/**" which excludes source CSS like plugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/panel.css from linting; update the ignoreFiles list by removing that Gutenberg assets pattern or replace it with a more specific pattern that only matches built output (e.g., dist/build folders under that path) so source files such as editor/panel.css remain linted.plugins/SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php (1)
61-65:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAvoid enqueueing a stylesheet when
panel.cssis missing.Line 64 enqueues
panel.cssunconditionally. If the asset is absent, the editor incurs a 404 and the panel renders unstyled. Mirror the JS existence guard before enqueueing the style.🛡️ Proposed fix
$js_ver = (string) filemtime( $js_path ); $css_path = $base_path . 'panel.css'; $css_ver = file_exists( $css_path ) ? (string) filemtime( $css_path ) : $js_ver; - wp_enqueue_style( self::STYLE_HANDLE, $base_url . 'panel.css', array(), $css_ver ); + if ( file_exists( $css_path ) ) { + wp_enqueue_style( self::STYLE_HANDLE, $base_url . 'panel.css', array(), $css_ver ); + }🤖 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/gutenberg/includes/class-editor-enqueue.php` around lines 61 - 65, The code unconditionally calls wp_enqueue_style(self::STYLE_HANDLE, $base_url . 'panel.css', array(), $css_ver) even when panel.css is missing; modify the enqueue logic in the same block to only call wp_enqueue_style when file_exists($css_path) is true (use the computed $css_path/$css_ver), mirroring the JS guard so you avoid 404s and unstyled UI — i.e., wrap or gate the wp_enqueue_style call with the file_exists check for $css_path and only enqueue when present.
🧹 Nitpick comments (1)
plugins/SLASHED-for-WP/includes/class-inventory.php (1)
806-818: 💤 Low valueFallback inventory is missing
color_valueskey.The
fallback_inventory()return array omits thecolor_valueskey that the parser contract expects. Whilesanitize_inventory()gracefully coerces this to an empty map, consider adding the key for consistency with the canonical inventory shape.♻️ Suggested fix
return array( 'variables' => array_values( $decoded['variables'] ), 'sf_classes' => array_values( $decoded['sf_classes'] ), 'is_classes' => array_values( $decoded['is_classes'] ), + 'color_values' => isset( $decoded['color_values'] ) ? (array) $decoded['color_values'] : array(), );🤖 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/includes/class-inventory.php` around lines 806 - 818, The fallback_inventory() implementation omits the expected 'color_values' key; update the function to check for and include color_values (similar to variables/sf_classes/is_classes) when decoding the JSON from fallback_json_path(), returning 'color_values' => array_values($decoded['color_values']) when present or an empty array when not, so the returned shape matches the parser contract and aligns with sanitize_inventory().
🤖 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/includes/class-inventory.php`:
- Around line 301-306: get_admin_color_overrides() calls
Slashed_Token_Store::get_settings() without checking that the token store class
is available, which can cause a fatal error; add a defensive guard at the start
of get_admin_color_overrides() that checks class_exists('Slashed_Token_Store')
(and optionally method_exists('Slashed_Token_Store','get_settings')) and return
an empty array if the class or method is missing, otherwise call
Slashed_Token_Store::get_settings() as before so the function is safe when the
token store isn't loaded yet.
---
Duplicate comments:
In @.stylelintrc.json:
- Line 3: The ignoreFiles entry in .stylelintrc.json currently contains the
pattern "plugins/SLASHED-for-WP/integrations/gutenberg/assets/**" which excludes
source CSS like
plugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/panel.css from
linting; update the ignoreFiles list by removing that Gutenberg assets pattern
or replace it with a more specific pattern that only matches built output (e.g.,
dist/build folders under that path) so source files such as editor/panel.css
remain linted.
In
`@plugins/SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php`:
- Around line 61-65: The code unconditionally calls
wp_enqueue_style(self::STYLE_HANDLE, $base_url . 'panel.css', array(), $css_ver)
even when panel.css is missing; modify the enqueue logic in the same block to
only call wp_enqueue_style when file_exists($css_path) is true (use the computed
$css_path/$css_ver), mirroring the JS guard so you avoid 404s and unstyled UI —
i.e., wrap or gate the wp_enqueue_style call with the file_exists check for
$css_path and only enqueue when present.
---
Nitpick comments:
In `@plugins/SLASHED-for-WP/includes/class-inventory.php`:
- Around line 806-818: The fallback_inventory() implementation omits the
expected 'color_values' key; update the function to check for and include
color_values (similar to variables/sf_classes/is_classes) when decoding the JSON
from fallback_json_path(), returning 'color_values' =>
array_values($decoded['color_values']) when present or an empty array when not,
so the returned shape matches the parser contract and aligns with
sanitize_inventory().
🪄 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: 2728a60c-e85d-4dab-b53c-49528961bc51
📒 Files selected for processing (17)
.stylelintrc.jsonplugins/SLASHED-for-WP/data/inventory.jsonplugins/SLASHED-for-WP/includes/class-color-resolver.phpplugins/SLASHED-for-WP/includes/class-css-parser.phpplugins/SLASHED-for-WP/includes/class-inventory.phpplugins/SLASHED-for-WP/integrations/bricks/includes/class-color-resolver.phpplugins/SLASHED-for-WP/integrations/bricks/includes/class-css-parser.phpplugins/SLASHED-for-WP/integrations/bricks/includes/class-inventory.phpplugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/apply.jsplugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/color-model.jsplugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/panel.cssplugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/panel.jsplugins/SLASHED-for-WP/integrations/gutenberg/includes/class-color-palette.phpplugins/SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.phpplugins/SLASHED-for-WP/integrations/gutenberg/includes/class-inventory.phpplugins/SLASHED-for-WP/integrations/gutenberg/includes/class-presets.phpplugins/SLASHED-for-WP/integrations/gutenberg/slashed-gutenberg.php
💤 Files with no reviewable changes (1)
- plugins/SLASHED-for-WP/integrations/gutenberg/includes/class-color-palette.php
✅ Files skipped from review due to trivial changes (2)
- plugins/SLASHED-for-WP/data/inventory.json
- plugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/panel.css
🚧 Files skipped from review as they are similar to previous changes (6)
- plugins/SLASHED-for-WP/includes/class-css-parser.php
- plugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/color-model.js
- plugins/SLASHED-for-WP/integrations/gutenberg/includes/class-presets.php
- plugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/apply.js
- plugins/SLASHED-for-WP/includes/class-color-resolver.php
- plugins/SLASHED-for-WP/integrations/gutenberg/assets/editor/panel.js
- class-inventory.php: change static::resolve() → self::resolve() to avoid
fatal when private method is called through late-static binding from subclasses
(Slashed_Bricks_Inventory / Slashed_Gutenberg_Inventory)
- class-inventory.php: add class_exists('Slashed_Token_Store') guard in
get_admin_color_overrides() to prevent fatal when token store isn't loaded
- class-inventory.php: include color_values key in fallback_inventory() return
to match the canonical inventory shape expected by sanitize_inventory()
- color-model.js: also exclude -dark source tokens from renderable swatches
(already excluded -light; -dark has the same issue of exposing internal tokens)
- panel.js: track full selection set (selectedClientIds().join(',')) in
onStoreChange instead of only the first selectedClientId(), fixing stale
context line and class-active markers in multi-select
- panel.js: update checkmark span text after class toggle so visual state
stays in sync with the actual active state
- class-editor-enqueue.php: guard wp_enqueue_style with file_exists(css_path)
to avoid 404s when panel.css is absent (mirrors the existing JS guard)
- class-presets.php: add empty-palette guard matching the existing empty-gradients
guard, plus empty-color cleanup, so an empty inventory never overwrites the
theme's existing color palette with nothing
- .stylelintrc.json: narrow Gutenberg ignore from assets/** to assets/admin-app/**
so new source CSS (editor/panel.css) is linted
https://claude.ai/code/session_01GiM4ZqWCStCSrBvSwdqnfb
The editor panel CSS is now linted (assets/admin-app/** ignore no longer covers it). Bring it into compliance with the project rules: - convert rgba() to modern rgb( ... / a) notation - rename the local --c / --g custom properties to --sf-gb-fill / --sf-gb-grad to satisfy custom-property-pattern; update the matching inline styles in panel.js npm run lint:css now passes; audit:check, check-artifacts --check and the color-model tests remain green.
This pull request was created by @kiro-agent on behalf of @jackgranatowski 👻
Comment with /kiro fix to address specific feedback or /kiro all to address everything.
Learn about Kiro Web
Summary
Brings the Gutenberg integration up to parity with the Bricks one. The block editor was shipping a stale, hand-maintained 20-entry color palette with no gradients, font sizes, or spacing presets. This replaces all of it with a live, bundle-driven token system plus a categorized, searchable in-editor panel (the CORE-Framework / ACSS-style experience).
Phase 1 — native presets (
class-presets.php)Registers the SLASHED tokens with the block editor's native controls, derived live from the active CSS bundle (essential/optimal/full):
--sf-color-*set (276 entries: 6 brand + 5 status families with 50–950 scales, alpha steps, semantic aliases, plus page-level semantic tokens), family-ordered,slashed-namespaced slugs.--sf-gradient-*tokens in the gradient picker.--sf-text-*(incl. display sizes) in the typography control.--sf-space-*in the padding/margin/gap controls.Every value is a
var(--sf-*)reference, so dark mode and admin token overrides stay live. Registered viaadd_theme_support(classic themes) +wp_theme_json_data_theme(block/FSE themes, and the only path for spacing presets).Phase 2 — in-editor token panel (
assets/editor/+class-editor-enqueue.php)A floating, categorized, searchable browser for colors, gradients, classes, and variables:
var()would not resolve).var()fallback when nothing is selected.wp.data; no bundler step.Shared data layer
includes/asSlashed_CSS_Parser/Slashed_Color_Resolver/Slashed_Inventory(single source of truth).Testing
php -lclean on all 10 changed/new PHP files.node --checkclean on all JS modules; existingtests/color-model.test.jspasses (32/32).dist/slashed.optimal.css: parser extracts 806 vars / 9 gradients; resolver builds 441-entry light + dark hex maps; presets emit 276 palette / 9 gradient / 12 font-size / 9 spacing entries with unique slugs.Notes / limitations
var(--sf-gradient-*).0.5.21(releases are handled by the version-sync workflow / release-it).slashed_gutenberg/{color_palette,gradient_presets,font_size_presets,spacing_presets,show_panel}.Summary by CodeRabbit
New Features
Refactor
Style
Chore