Skip to content

Fix layout utilities and dark mode color tokens - #15

Merged
jackgranatowski merged 4 commits into
mainfrom
claude/review-css-tokens-8zAT9
May 18, 2026
Merged

Fix layout utilities and dark mode color tokens#15
jackgranatowski merged 4 commits into
mainfrom
claude/review-css-tokens-8zAT9

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented May 18, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR fixes missing layout utility classes, improves dark mode color token definitions, and corrects animation implementation in the demo page.

Key Changes

Layout Utilities (core/layout.css)

  • Added missing .sf-cluster--m gap modifier class that was referenced throughout the demo but not defined in the CSS

Dark Mode Color Tokens (core/base.css)

  • Added comprehensive dark mode color token overrides for both @media (prefers-color-scheme: dark) and .dark class selector
  • Implemented inverted text color scales using oklch() relative color syntax with adjusted lightness values for dark backgrounds
  • Added dark mode border color adjustments with appropriate contrast levels
  • Increased shadow strength (--sf-shadow-strength: 0.25) for better visibility on dark surfaces

Demo Page Fixes (docs/demo.html)

  • Radius swatches: Adjusted dimensions from 4.5rem × 4.5rem to 6rem × 5rem for better visibility
  • Animation: Converted from CSS translate property to left property with explicit left: 0 positioning for more reliable animation behavior
  • Layout classes: Added missing base class names to all modifier-only elements:
    • Added .sf-cluster to all .sf-cluster--* elements
    • Added .sf-stack to all .sf-stack--* elements
  • Alignment: Changed radius swatch container from align-items: flex-end to align-items: center for better visual balance

Implementation Details

  • Dark mode tokens use relative color syntax (oklch(from ...)) to derive values from the neutral color variable, ensuring consistency
  • Text colors use lightness adjustments: +0.25 for primary text, +0.1 for secondary, -0.1 for placeholder, -0.2 for disabled, -0.4 for inverse
  • Border colors use reduced chroma (0.005 for subtle/normal, 0.02 for strong) to maintain proper contrast on dark backgrounds
  • All layout utility fixes maintain backward compatibility while ensuring proper CSS class inheritance

https://claude.ai/code/session_018HDAUATPwLueARYL8EtLRS

Summary by CodeRabbit

  • Style

    • Expanded dark-mode visuals with additional derived color roles and a subtle shadow adjustment for more consistent dark-theme rendering.
    • Refined color token logic for more stable, contrast-aware text and border colors across themes.
    • Added a medium-spacing layout variant to improve spacing flexibility.
  • Documentation

    • Updated demo page markup, motion, sizing, and layout to reflect updated utilities and showcase the visual changes.

Review Change Stack

…on, swatches

- Add missing .sf-cluster--m modifier to layout.css
- Add base class to all modifier-only sf-cluster/sf-stack usages in demo.html
- Fix @Keyframes demo-slide to use left instead of translate (track-relative movement)
- Add left: 0 to .demo-ease-ball for correct animation start position
- Add dark mode text/heading token overrides to both dark mode blocks in base.css (Bug 4)
- Add dark mode border token overrides with correct luminance direction (Bug 5)
- Increase --sf-shadow-strength to 0.25 in dark mode for visible shadows (Bug 6)
- Enlarge .demo-radius-swatch to 6rem×5rem to distinguish small radii (Bug 7)
- Change Border Radius section container align-items from flex-end to center

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

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3a2cdc7a-b203-4744-a516-293a6583fa69

📥 Commits

Reviewing files that changed from the base of the PR and between 5ef2e76 and eee251f.

📒 Files selected for processing (2)
  • core/base.css
  • core/tokens.css
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/base.css

📝 Walkthrough

Walkthrough

Extends dark-mode semantic CSS variables and tightens token math in core/tokens.css, adds .sf-cluster--m in core/layout.css, and normalizes many demo HTML/CSS sections (utility classes, sidebar sizing, radius swatches) while refactoring the motion demo animation.

Changes

Core theme and demo updates

Layer / File(s) Summary
Dark mode CSS variables
core/base.css
Extends :root:not([data-theme]) and [data-theme="dark"] with dark-specific CSS variables for text (secondary, placeholder, disabled, inverse, heading), borders (subtle, strong, disabled, translucent), status text variants, and --sf-shadow-strength: 0.25.
Token color math and on-color computation
core/tokens.css
Replaces unconstrained calc(l ± …) mappings and fixed on-color triplets with clamp()-bounded luminance math and computed foreground selection (sign-based) for neutral and status-derived text/border tokens.
Cluster medium spacing modifier
core/layout.css
Introduces .sf-cluster--m rule setting gap: var(--sf-space-m) for medium cluster spacing.
Demo layout & motion tweaks
docs/demo.html
Removes flex: 1 from #main, reduces sidebar width (--sf-sidebar-width 14rem→12rem), increases demo-radius-swatch size, and refactors motion easing demo from transform/translate to left-based absolute positioning with updated keyframes.
Demo utility class normalization
docs/demo.html
Adds base utility classes (sf-stack, sf-cluster) alongside existing size modifiers across many demo sections; updates Border Radius alignment and Reset & Base control-group wrappers.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

🚥 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 accurately captures the main changes: adding layout utilities (.sf-cluster--m) and fixing dark mode color tokens with clamp()-guarded luminance mappings.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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/review-css-tokens-8zAT9

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

🧹 Nitpick comments (1)
docs/demo.html (1)

236-236: ⚡ Quick win

Prefer transform: translateX() over left for animation performance.

The animation now uses left positioning, which triggers layout recalculation on every frame. transform: translateX() is GPU-accelerated and significantly more performant, especially with multiple concurrent animations (6 easing balls). Since this demo showcases best practices for the design system, consider reverting to transform-based animation.

⚡ Proposed fix using transform
 .demo-ease-ball {
   width: 2rem;
   height: 2rem;
   border-radius: var(--sf-radius-full);
   background: var(--sf-color-primary);
   position: absolute;
   top: 0;
-  left: 0;
+  left: 0;  /* Keep for initial positioning */
 }
 
 `@keyframes` demo-slide {
-  from { left: 0; }
-  to   { left: calc(100% - 2rem); }
+  from { transform: translateX(0); }
+  to   { transform: translateX(calc(100% - 2rem)); }
 }

Also applies to: 240-241

🤖 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 `@docs/demo.html` at line 236, The CSS uses left positioning for the animation
(e.g., the rule containing "left: 0;") which causes layout thrashing; replace
the left-based animation with transform-based translation: update the relevant
keyframes and any rules that set or animate left to instead use transform:
translateX(...) or translate3d(...,0,0) for GPU acceleration, and if JavaScript
manipulates element.style.left (or any code referencing left at the demo around
the same block), change it to update a transform (or a CSS custom property used
in transform) so the animation uses translateX and not left; also adjust any
positioning logic so the visual layout and offsets match after switching (apply
the same change to the other occurrences noted at the nearby lines).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@docs/demo.html`:
- Line 236: The CSS uses left positioning for the animation (e.g., the rule
containing "left: 0;") which causes layout thrashing; replace the left-based
animation with transform-based translation: update the relevant keyframes and
any rules that set or animate left to instead use transform: translateX(...) or
translate3d(...,0,0) for GPU acceleration, and if JavaScript manipulates
element.style.left (or any code referencing left at the demo around the same
block), change it to update a transform (or a CSS custom property used in
transform) so the animation uses translateX and not left; also adjust any
positioning logic so the visual layout and offsets match after switching (apply
the same change to the other occurrences noted at the nearby lines).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2c35040e-ab96-42d2-8db9-0e133653ce10

📥 Commits

Reviewing files that changed from the base of the PR and between 4d76d51 and 6a54d62.

📒 Files selected for processing (3)
  • core/base.css
  • core/layout.css
  • docs/demo.html

@jackgranatowski jackgranatowski left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Skipping the transform: translateX() suggestion — the proposed fix reintroduces the exact bug this PR fixed.

In transform: translateX(), 100% resolves to the element's own width (2rem), so calc(100% - 2rem) = calc(2rem - 2rem) = 0 — the ball doesn't move. This was the original bug.

Using left, 100% resolves to the containing block's width (the track), which is what we need. The performance trade-off is negligible for a 6-ball demo page.


Generated by Claude Code

fix(base): dark mode status text contrast — lightened with calc(l + 0.15)

The #main { flex: 1 } rule was overriding .sf-sidebar > :last-child { flex-grow: 999 },
causing nav and main to share space equally (~50/50) instead of 1:999.
Removing flex:1 restores correct framework behavior. Sidebar width reduced
from 14rem to 12rem — sufficient for all nav labels.

Status text tokens (success/warning/error/info/danger) used calc(l - X) which
in dark mode produced l≈0.50 text on l≈0.22 background. Overrides in both
dark mode blocks flip to calc(l + X) giving l≈0.80 — clearly readable.

https://claude.ai/code/session_018HDAUATPwLueARYL8EtLRS

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
core/base.css (2)

92-117: ⚖️ Poor tradeoff

Consider reducing duplication between dark mode blocks.

The derived color declarations (text, borders, shadow, status) in [data-theme="dark"] (lines 92-117) are identical to those in the OS preference block (lines 39-63). This duplication creates maintenance burden and risk of inconsistency.

While CSS doesn't easily support sharing these declarations across different selectors, consider:

  • Using a CSS preprocessor mixin
  • Combining selectors if possible: :where([data-theme="dark"], :root:not([data-theme])):where(...)
  • Documenting that both blocks must be kept in sync
🤖 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 `@core/base.css` around lines 92 - 117, The dark-mode block under
[data-theme="dark"] duplicates many derived CSS variable declarations (e.g.,
--sf-color-text, --sf-color-border, --sf-shadow-strength,
--sf-status-success-text, etc.) that already exist in the OS preference block;
refactor by extracting these shared variable declarations into a single rule
applied to both contexts (for example combine selectors or use
:where([data-theme="dark"], :root:where(...)) to target both dark contexts) or
move them into a reusable preprocessor mixin and include it in both places so
the declarations are maintained in one place and the duplicated blocks are
removed/updated accordingly.

39-46: ⚡ Quick win

Add fallback colors and consider reducing duplication between OS preference and explicit theme blocks.

The oklch(from ...) relative color syntax has strong browser support (Chrome 119+, Firefox 128+, Safari 16.4+). However, for graceful degradation in older browsers and as a best practice, provide fallback declarations before the relative color rules. Additionally, the identical color declarations across lines 39–46 (and 47–63) are duplicated in the [data-theme="dark"] block at lines 93–99 (and 101–116); consider extracting these to a shared rule or using a preprocessor to maintain a single source of truth.

Example fallback pattern
--sf-color-text: `#ffffff`; /* Fallback for unsupported browsers */
--sf-color-text: oklch(from var(--sf-color-neutral) calc(l + 0.25) c h);
🤖 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 `@core/base.css` around lines 39 - 46, Add fallback color declarations before
each oklch(...) variable assignment for graceful degradation (e.g., set
--sf-color-text, --sf-color-text--secondary, --sf-color-text--placeholder,
--sf-color-text--disabled, --sf-color-text--inverse, --sf-color-heading to solid
hex values before their oklch(...) lines), and remove duplicated assignments by
extracting the shared variables into a single shared rule (e.g., :root or a
.theme-common block) used by both the prefers-color-scheme: dark and
[data-theme="dark"] blocks so the same variable names (--sf-color-text,
--sf-color-text--secondary, --sf-color-text--placeholder,
--sf-color-text--disabled, --sf-color-text--inverse, --sf-color-heading) are
defined once and then overridden only where necessary.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@core/base.css`:
- Around line 92-117: The dark-mode block under [data-theme="dark"] duplicates
many derived CSS variable declarations (e.g., --sf-color-text,
--sf-color-border, --sf-shadow-strength, --sf-status-success-text, etc.) that
already exist in the OS preference block; refactor by extracting these shared
variable declarations into a single rule applied to both contexts (for example
combine selectors or use :where([data-theme="dark"], :root:where(...)) to target
both dark contexts) or move them into a reusable preprocessor mixin and include
it in both places so the declarations are maintained in one place and the
duplicated blocks are removed/updated accordingly.
- Around line 39-46: Add fallback color declarations before each oklch(...)
variable assignment for graceful degradation (e.g., set --sf-color-text,
--sf-color-text--secondary, --sf-color-text--placeholder,
--sf-color-text--disabled, --sf-color-text--inverse, --sf-color-heading to solid
hex values before their oklch(...) lines), and remove duplicated assignments by
extracting the shared variables into a single shared rule (e.g., :root or a
.theme-common block) used by both the prefers-color-scheme: dark and
[data-theme="dark"] blocks so the same variable names (--sf-color-text,
--sf-color-text--secondary, --sf-color-text--placeholder,
--sf-color-text--disabled, --sf-color-text--inverse, --sf-color-heading) are
defined once and then overridden only where necessary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e81f4e2c-7a27-455d-a3d7-e8803bea6708

📥 Commits

Reviewing files that changed from the base of the PR and between 6a54d62 and 66df49a.

📒 Files selected for processing (2)
  • core/base.css
  • docs/demo.html
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/demo.html

@jackgranatowski jackgranatowski left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Skipping both nitpicks.

Duplication between @media and [data-theme="dark"] blocks: acknowledged but not fixable in pure CSS without a build tool. There is no CSS selector syntax that can simultaneously be conditional on a media feature and an attribute — the two blocks serve structurally distinct cascade roles and cannot be merged. A Sass mixin would solve it, but this framework has no build step. Added an in-code comment noting both blocks must be kept in sync.

oklch() fallbacks: the entire framework already depends on oklch() relative color syntax throughout tokens.css (brand colors, surface tokens, border tokens, shadows — everything). Adding fallbacks only to the dark mode overrides in base.css would be inconsistent and ineffective — a browser without support for relative oklch() colors would already fail at tokens.css long before reaching these rules. Modern browser support (Chrome 119+, Firefox 128+, Safari 16.4+) is a documented framework requirement.


Generated by Claude Code

claude added 2 commits May 18, 2026 15:48
CSS cannot share declarations across a media condition and a selector,
so the derived semantic overrides are intentionally duplicated.

https://claude.ai/code/session_018HDAUATPwLueARYL8EtLRS
…om palettes

--sf-color-text--on-*: replaced hardcoded white/dark with sign(0.6 - l) * 999
inside clamp(0.1, ..., 0.95). Works for any source color luminance and adapts
automatically in dark mode (tokens reference the active -dark color variant).
Example: yellow primary (l=0.85) → dark text; red primary (l=0.45) → light text.

Added clamp() guards to all directional light-mode formulas in tokens.css
(text, heading, border) and to dark-mode overrides in base.css (both blocks).
Prevents broken output when a developer uses neutral values outside the range
the offset formulas were tuned for, with no change to computed values for the
default palette.

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