Skip to content

feat(colors): complete color system — shade scale, section theming, swatch coverage - #188

Merged
jackgranatowski merged 4 commits into
mainfrom
claude/color-swatches-ui-variables-lrdQ2
Jun 1, 2026
Merged

feat(colors): complete color system — shade scale, section theming, swatch coverage#188
jackgranatowski merged 4 commits into
mainfrom
claude/color-swatches-ui-variables-lrdQ2

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Summary

A comprehensive pass over the SLASHED color system touching the core framework CSS, the Bricks builder integration, and the documentation. All changes are additive; nothing public is removed or renamed.

1. Bricks variable-picker swatch coverage

All --sf-color-* tokens now have hex swatches in the variable-picker dropdown.

Missing semantic tokens added to class-color-resolver.php:

  • bg--hover/active/selected/focus/disabled — neutral/action mixed over white at the token's effective alpha
  • border--subtle/strong/focus/disabled/translucent — neutral-source oklch with the CSS L-clamp offsets
  • link/link--hover/link--active/link--visited — action-source oklch with light-mode lightness clamps; visited uses the CSS +60° hue shift
  • text--on-* — L-threshold check (< 0.6 → near-white, ≥ 0.6 → near-black) for all 11 families
  • well/raised/inverse/overlay — base-source oklch with ±0.02/+0.04/inverted-L
  • success/warning/error/info/danger -strong — source L minus the same offsets as the CSS formula (-0.15 success, -0.25 warning, -0.10 error/info/danger)
  • dim, code-bg/text, heading, mark-bg, selection-bg

Shade generation switched from sRGB to oklch interpolation:

Before: mixed source hex toward a dark-navy text color in sRGB → purple hue drift on cool palettes.
After: interpolates L and C toward white (light steps) or black (dark steps) in oklch space — perceptually uniform, no hue drift regardless of source hue.

2. Perceptually-uniform shade scale (core/tokens.css)

Adds 11 × 20 = 220 new tokens, two groups per family:

Numeric palette (50–950) — built with color-mix(in oklch, source X%, white/black). Reference the -light source so they are stable across dark mode (Tailwind convention — pick a different step in dark mode if needed):

--sf-color-primary-50:  color-mix(in oklch, var(--sf-color-primary-light)  4%, white);
--sf-color-primary-500: var(--sf-color-primary-light);
--sf-color-primary-950: color-mix(in oklch, var(--sf-color-primary-light)  8%, black);

Semantic aliases — reference the adaptive --sf-color-{family} token so they automatically shift in dark mode:

--sf-color-primary-lighter:   oklch(from var(--sf-color-primary) clamp(0, calc(l + 0.17), 0.92) calc(c * 0.88) h);
--sf-color-primary-hover:     var(--sf-color-primary-darker);
--sf-color-primary-ghost:     oklch(from var(--sf-color-primary) l c h / 0.05);

Inventory fallback (data/inventory.json) updated from 631 → 731 variables.

3. Section-level [data-theme] — now fully works (core/themes.css)

Root cause: light-dark() resolves when a property is declared (on :root), not when it is inherited. A <section data-theme="dark"> only set color-scheme and --sf-is-dark — every color token inherited from :root stayed baked at the root's light values.

Fix: Added an @supports (color: oklch(from red l c h)) block that re-declares all 35 mode-sensitive tokens directly on [data-theme="dark"] and [data-theme="light"] elements using explicit formulas (the dark/light sides of the original light-dark() calls extracted and mirrored). Covered groups:

Group Tokens
Brand primary, secondary, tertiary, action, neutral, base
Status success, warning, error, info, danger
Text text, text--secondary, text--placeholder, text--disabled, text--inverse, heading
Borders border, border--subtle, border--strong
Links link, link--hover, link--active, link--visited
Selection selection-bg
Status strongs success/warning/error/info/danger -strong

Graceful degradation: Chrome < 119, Safari < 17.2, Firefox < 128 skip the block — color-scheme and --sf-is-dark still flip correctly, only the explicit token re-declarations are absent.

Usage — now just works out of the box:

<header data-theme="dark">Always dark, even on a light page</header>
<article data-theme="light">Always light inside a dark page</article>
<section data-brand="sunset" data-theme="dark">Dark + custom palette</section>

4. Documentation

  • docs/dark-mode.md — recipes 3–8 expanded (section theming, single-value override, per-section brand, contrast boost); Gotchas section updated with browser compatibility note
  • docs/theming.md — added three-tier override guide (source token → -dark token → resolved token)
  • optional/theme-example.css — section 3b: section-level dark/light theming usage note

Test plan

  • Open Bricks variable picker → verify all sf-color-* entries show a colored swatch (no plain-text rows among color tokens)
  • Verify sf-color-{success,warning,error,info,danger}-strong swatches are deep/saturated (darker than the base family color)
  • Add data-theme="dark" to a <section> on a light page → confirm all text, background, border, and link colors invert correctly (not just color-scheme)
  • Add data-theme="light" inside html[data-theme="dark"] → confirm the nested section renders in full light mode
  • Run npm run docs && git diff --exit-code docs/ plugins/ → no stale generated files
  • CI: Lint CSS, Regression tests, Verify generated docs all green

https://claude.ai/code/session_01JLvpeyEzNZtgc7TVjVMhzu

claude added 3 commits June 1, 2026 11:59
…ex map

Extends resolve_semantic_tokens() to cover all --sf-color-* tokens
registered in inventory.json, not just the handful previously hardcoded.
Previously bg--hover/active/selected/focus/disabled, border--subtle/
strong/focus/disabled/translucent, all link-- states, all text--on-*
contrast tokens, and other structural tokens (well, raised, inverse,
overlay, dim, code-bg/text, heading, mark-bg, selection-bg) appeared
in the Bricks variable picker but received no swatch.

Each group is approximated from the already-computed family hex values:
- bg--*: neutral/action mixed over white at the token's effective opacity
- border--*: neutral-source oklch with the same L-clamp offsets as CSS
- link--*: action-source oklch with the light-mode lightness clamps;
  visited applies the CSS +60° hue shift
- text--on-*: L-threshold check (< 0.6 → near-white, ≥ 0.6 → near-black)
- well/raised/inverse: base-source oklch with ±0.02/+0.04/inverted-L
- dim: mid-gray approximation of the 50%-black translucent overlay

https://claude.ai/code/session_01JLvpeyEzNZtgc7TVjVMhzu
Previously the 50-950 shade scale only existed as approximate hex
values computed by the Bricks PHP resolver for picker swatches —
var(--sf-color-primary-300) resolved to nothing outside Bricks.

Shade generation (class-color-resolver.php):
  Switch from sRGB two-pole mixing (source→base-light, source→#1c1c2e)
  to oklch interpolation toward white / black. This matches what
  color-mix(in oklch, ...) computes in CSS, eliminating the purple hue
  drift that the old dark-navy mix target introduced and producing
  perceptually uniform steps across all hues.

Core CSS (tokens.css):
  Add a full COLOR SCALE section (11 families × ~22 vars each):
  · Numeric steps (50–950) via color-mix(in oklch, …-light X%, white/black)
    — stable values that don't flip in dark mode, same DX as Tailwind.
  · Semantic aliases (superlight → superdark, hover, active, subtle,
    muted, ghost) via oklch relative color from the adaptive
    --sf-color-{family} token so they auto-shift when color-scheme
    switches — the safe default for component styles.

Bricks inventory (inventory.json):
  Add the 100 status-family shade tokens that were missing from the
  fallback variable list (success/warning/error/info/danger × 20).

https://claude.ai/code/session_01JLvpeyEzNZtgc7TVjVMhzu
…) tokens

light-dark() resolves at :root declaration time, not at inheritance
time — a child [data-theme="dark"] only sets color-scheme, leaving
every inherited custom property still stuck at the root's resolved
value.

themes.css now re-declares all 35 mode-sensitive tokens (brand,
status, text, borders, links, selection, status-strongs) directly on
[data-theme="dark"] and [data-theme="light"] elements using explicit
dark/light formulas. This makes section-level theming (dark headers on
light pages, light cards in dark pages) work correctly without any
per-project workaround.

Gated in @supports (color: oklch(from red l c h)) for graceful
degradation; older browsers fall back to root values unchanged.

Also updates dark-mode.md (recipes 3-8, gotchas), theming.md (manual
override tier guide), and theme-example.css (section-level usage note).

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

coderabbitai Bot commented Jun 1, 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 49 minutes and 54 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: 60de7c7d-e980-45ff-993b-0336a1979ffa

📥 Commits

Reviewing files that changed from the base of the PR and between 4f6c6f4 and a3aed30.

📒 Files selected for processing (6)
  • core/themes.css
  • docs/dark-mode.md
  • docs/registry.json
  • docs/tokens.md
  • plugins/SLASHED-for-WP/integrations/bricks/data/inventory.json
  • tests/token-api.snapshot.json
📝 Walkthrough

Walkthrough

This PR establishes a new color scale system with numeric shade steps and semantic aliases, implements section-level dark/light mode forcing via the data-theme attribute with scoped token re-declarations, and updates documentation and Bricks integration to support the expanded palette.

Changes

Color Scale Tokens, Section-Level Theming, and Bricks Integration

Layer / File(s) Summary
Color Scale Definitions
core/tokens.css
New COLOR SCALE section defines --sf-color-{family}-{50..950} numeric shade steps derived from -light tokens via color-mix(in oklch), plus semantic aliases (superlight/xlight/lighter/darker/xdark/superdark/hover/active/subtle/muted/ghost) for brand families (primary/secondary/tertiary/action/neutral/base) and status families (success/warning/error/info/danger); token stability docs updated to reflect new palette.
Section-Level Theme Scoping
core/themes.css
@supports (color: oklch(...)) block re-declares all mode-sensitive --sf-* tokens on [data-theme="dark"] and [data-theme="light"] elements with explicit OKLCH formulas and per-token -dark fallbacks, enabling independent dark/light mode switching within any subtree without !important.
Dark Mode Usage Documentation
docs/dark-mode.md
New cookbook sections explain scoping data-theme to any element, manually overriding single dark tokens via --*-dark, combining data-brand palette overrides with data-theme on the same element, and documents relative color syntax browser support with fallback behavior in older browsers.
Theming Architecture Guide
docs/theming.md
Clarifies data-theme re-declaration behavior on any element and combining data-brand with forced data-theme="dark". Defines three override tiers: Tier 1 (source token), Tier 2 (dark source token via --*-dark), and Tier 3 (resolved token with light-dark(...) formulas); instructs placing Tier 3 overrides in slashed.overrides to persist across framework updates.
Theme Example Comments
optional/theme-example.css
Comment-only reference section demonstrating [data-theme] usage with example HTML and optional override snippet.
Bricks Token Inventory Updates
plugins/SLASHED-for-WP/integrations/bricks/data/inventory.json
Reorders shade-step entries for action/base/neutral/primary/secondary/success/tertiary (repositioning -50 and -a5 relative to adjacent steps); expands danger/error/info/warning inventory to include full -100-950 numeric ranges and all semantic-alias variants.
Bricks Color Resolver Implementation
plugins/SLASHED-for-WP/integrations/bricks/includes/class-color-resolver.php
resolve() replaces RGB hex-target mixing with OKLCH-space interpolation for light and dark swatch steps. resolve_semantic_tokens() substantially expanded to define foundational tokens (text, bg, surface, link underline/visited/disabled states), interactive backgrounds (hover/active/selected/focus/disabled), well/raised/inverse/overlay, dim, code background/text, light-mode link approximations with visited hue shift, text-on-color contrast tokens for multiple families, and selection/mark backgrounds.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • codeslash-dev/SLASHED#183: The rewrite of class-color-resolver.php with new OKLCH-based swatch generation and expanded semantic token outputs directly affects the Bricks color-hex mapping used by the variable-picker enhancement.

Possibly related PRs

  • codeslash-dev/SLASHED#25: Both modify core/tokens.css mode-dependent --sf-color-* semantic/resolved token logic to ensure correct dark/light behavior with dark fallbacks and theme-aware color tokens.
  • codeslash-dev/SLASHED#7: Both redefine and derive --sf-color-* tokens for light/dark switching; this PR adds data-theme-scoped re-declarations in core/themes.css and extends core/tokens.css color scales, while retrieved PR rewrites token logic in core/base.css.
  • codeslash-dev/SLASHED#184: Both update SF color token palettes and Bricks integration token inventory, which feeds the swatch feature's server-resolved hex map.
🚥 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 PR title accurately describes the primary change: adding missing semantic token swatches (hex values) for color tokens in the Bricks integration's variable picker. This matches the core objective of extending resolve_semantic_tokens() to generate hex swatches for all registered tokens.
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/color-swatches-ui-variables-lrdQ2

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

🧹 Nitpick comments (1)
plugins/SLASHED-for-WP/integrations/bricks/includes/class-color-resolver.php (1)

312-357: 💤 Low value

Two inventory tokens lack corresponding swatches.

--sf-color-mark-text and --sf-color-selection-text exist in inventory.json but are not generated here. This appears intentional given the PR scope explicitly lists specific tokens, and text colors are typically less visually interesting for swatch previews. Consider adding these in a follow-up if users request them.

Optional: generate mark-text and selection-text swatches
 		$hex_map['--sf-color-mark-bg'] = self::rgb_to_hex( self::mix_rgb( $warning_rgb, $white_rgb, 0.25 ) );
+		$hex_map['--sf-color-mark-text'] = $dark_text; // mark-bg is light → dark text
+		$hex_map['--sf-color-selection-text'] = $hex_map['--sf-color-text--inverse'];

 		return $hex_map;
🤖 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/includes/class-color-resolver.php`
around lines 312 - 357, Add generation for the two missing swatch tokens: if
isset($sources['warning']) set '--sf-color-mark-text' using the same lightness
rule as the on-family loop (use $sources['warning'][0] < 0.6 ? $light_text :
$dark_text) so it contrasts with the mark background (mark-bg uses
$warning_rgb), and set '--sf-color-selection-text' to a contrasting value for
the selection background—preferably use $sources['action'][0] (if isset) with
the same lightness rule, otherwise fall back to
$hex_map['--sf-color-text--on-base']; place these assignments alongside the
other $hex_map entries in class-color-resolver.php so consumers get mark and
selection text swatches.
🤖 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 `@core/themes.css`:
- Line 152: The CSS variable declaration for --sf-color-text--secondary (and
similar occurrences at the other reported spots) violates
function-calc-no-unspaced-operator due to missing/incorrect spacing around
operators inside calc; update the calc expressions (e.g., in the
--sf-color-text--secondary rule using oklch(from var(--sf-color-neutral)
clamp(0.55, calc(l + 0.1 + var(--sf-contrast-bias)), 0.90) c h)) to ensure a
single space on both sides of each operator (e.g., change "calc(l + 0.1  +
var(...))" to "calc(l + 0.1 + var(...))") and apply the same spacing fix to the
other affected declarations referenced in the review.

In `@docs/dark-mode.md`:
- Around line 98-103: The CSS snippet uses invalid nesting `:root&`; replace it
by explicitly writing the selectors instead of using `&`: update the rule that
targets forced dark and OS-dark fallback to use explicit selectors such as
`[data-theme="dark"]` and `:root:not([data-theme])` combined with `@media
(prefers-color-scheme: dark)` (i.e., write the full selector for the inner rule
rather than `:root&`) so the variable assignment for `--sf-color-primary` is
applied correctly in both the forced dark case and the prefers-color-scheme
fallback.

---

Nitpick comments:
In
`@plugins/SLASHED-for-WP/integrations/bricks/includes/class-color-resolver.php`:
- Around line 312-357: Add generation for the two missing swatch tokens: if
isset($sources['warning']) set '--sf-color-mark-text' using the same lightness
rule as the on-family loop (use $sources['warning'][0] < 0.6 ? $light_text :
$dark_text) so it contrasts with the mark background (mark-bg uses
$warning_rgb), and set '--sf-color-selection-text' to a contrasting value for
the selection background—preferably use $sources['action'][0] (if isset) with
the same lightness rule, otherwise fall back to
$hex_map['--sf-color-text--on-base']; place these assignments alongside the
other $hex_map entries in class-color-resolver.php so consumers get mark and
selection text swatches.
🪄 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: 8abc0863-9749-4303-bc03-4c9000fb46ac

📥 Commits

Reviewing files that changed from the base of the PR and between 09336fd and 4f6c6f4.

📒 Files selected for processing (7)
  • core/themes.css
  • core/tokens.css
  • docs/dark-mode.md
  • docs/theming.md
  • optional/theme-example.css
  • plugins/SLASHED-for-WP/integrations/bricks/data/inventory.json
  • plugins/SLASHED-for-WP/integrations/bricks/includes/class-color-resolver.php

Comment thread core/themes.css Outdated
Comment thread docs/dark-mode.md Outdated
- themes.css: remove double spaces in three calc() expressions that
  tripped stylelint function-calc-no-unspaced-operator
- dark-mode.md: replace invalid :root& CSS nesting with explicit
  :root[data-theme="dark"] + @media fallback selector pair (CodeRabbit)
- docs/tokens.md, docs/registry.json, inventory.json: regenerate after
  the status-family shade scale addition (docs were stale)
- tests/token-api.snapshot.json: advance snapshot to include the 100
  status-family shade tokens (danger/error/info/success/warning 50-950
  + semantic aliases) — intentional API expansion, no removals

https://claude.ai/code/session_01JLvpeyEzNZtgc7TVjVMhzu
@jackgranatowski
jackgranatowski merged commit e7843d5 into main Jun 1, 2026
9 checks passed
@jackgranatowski jackgranatowski changed the title fix(bricks): add missing semantic token swatches to variable-picker hex map feat(colors): complete color system — shade scale, section theming, swatch coverage Jun 1, 2026
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