Skip to content

refactor(tokens): migrate dark mode to light-dark() hybrid architecture - #19

Merged
jackgranatowski merged 4 commits into
mainfrom
claude/add-css-layers-MlejK
May 19, 2026
Merged

refactor(tokens): migrate dark mode to light-dark() hybrid architecture#19
jackgranatowski merged 4 commits into
mainfrom
claude/add-css-layers-MlejK

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented May 19, 2026

Copy link
Copy Markdown
Contributor

Eliminates the 80-line duplicate dark mode block in base.css by moving
all theme logic into tokens.css using the CSS light-dark() function.

Source tokens renamed to explicit -light/-dark pairs (@Property registered).
Resolved tokens (--sf-color-primary etc.) auto-switch via light-dark() —
component API unchanged. Direction-dependent formulas (text, borders,
status text) reference source tokens directly inside light-dark() so
both formula and source value are correct per mode.

--sf-is-dark (0/1) drives non-color dark overrides (shadow-strength).

base.css dark mode section: ~100 lines → 5 lines.

Summary by CodeRabbit

  • Refactor

    • Redesigned theming: explicit light/dark token sources, simplified theme application, and improved dark-mode shadow/contrast behavior.
  • Documentation

    • Demo theme customizer and README updated with token customization, dark-mode usage examples, changelog heading and release badges.
  • Style

    • New/updated Stylelint rules enforcing token naming and modern color/function notation.
  • Chores

    • Added CI and release workflows, commit/pre-commit hooks and linting, release configuration, updated release scripts and bundle header.

Review Change Stack

Eliminates the 80-line duplicate dark mode block in base.css by moving
all theme logic into tokens.css using the CSS light-dark() function.

Source tokens renamed to explicit -light/-dark pairs (@Property registered).
Resolved tokens (--sf-color-primary etc.) auto-switch via light-dark() —
component API unchanged. Direction-dependent formulas (text, borders,
status text) reference source tokens directly inside light-dark() so
both formula and source value are correct per mode.

--sf-is-dark (0/1) drives non-color dark overrides (shadow-strength).

base.css dark mode section: ~100 lines → 5 lines.
@coderabbitai

coderabbitai Bot commented May 19, 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: eb00994d-25bb-45ad-a56f-feb2590aa75f

📥 Commits

Reviewing files that changed from the base of the PR and between 0828d8a and 833e000.

⛔ Files ignored due to path filters (1)
  • dist/slashed.essential.css is excluded by !**/dist/**
📒 Files selected for processing (2)
  • .release-it.json
  • package.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • .release-it.json
  • package.json

📝 Walkthrough

Walkthrough

This PR restructures CSS theming into explicit light/dark source tokens with semantic tokens resolved via light-dark(), adds an integer --sf-is-dark flag, makes shadows mode-aware, simplifies base mode switching to only set color-scheme/--sf-is-dark, and updates the demo customizer to edit -light source tokens.

Changes

Theme Architecture Refactor

Layer / File(s) Summary
Source Token Definitions and Mode Flag
core/tokens.css
Brand color @property declarations introduce light source tokens and status colors are split into light/dark pairs; adds --sf-is-dark integer property.
Semantic Token Resolution via light-dark()
core/tokens.css
Resolved semantic tokens for brand, status, text, and border colors are computed from -light/-dark source tokens using light-dark().
Mode-Aware Shadow Strength
core/tokens.css
Shadow strength is now calc(0.08 + var(--sf-is-dark) * 0.17) and the fixed baseline is removed from the non-color section.
Simplified Base Stylesheet Mode Switching
core/base.css
prefers-color-scheme: dark and [data-theme] selectors now only set color-scheme and --sf-is-dark; in-file semantic reassignments and light resets were removed.
Demo Theme Customizer Light Mode Controls
docs/demo.html
Light column inputs and the randomize handler now target --sf-color-*-light source tokens (dark values remain --sf-color-*-dark).

Tooling, CI, and Docs

Layer / File(s) Summary
Git hooks: commit-msg and pre-commit
.githooks/commit-msg, .githooks/pre-commit
Adds commit-msg hook to run commitlint and a pre-commit step that lints staged .css files with stylelint before rebuilding the dist bundle.
GitHub Actions: CI and Release workflows
.github/workflows/ci.yml, .github/workflows/release.yml
Adds CI workflow (lint + build jobs with artifact upload) and a Release workflow that builds on tag, extracts changelog notes, and creates a GitHub Release with the built CSS artifact.
Release & linting configuration
.release-it.json, .stylelintrc.json
Adds release-it configuration and a Stylelint config enforcing sf- naming and allowing light-dark and sign.
Docs, changelog, and README updates
CHANGELOG.md, README.md
Adds # Changelog heading and updates README (badges, quick start, bundles, token customization, dark-mode docs, development commands).
package.json, commitlint, and bundler tweak
commitlint.config.js, package.json, scripts/bundle.js
Exports default commitlint config, updates prepare and scripts (lint/build/release), and prefixes bundle header with SLASHED v<version>.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 clearly and accurately summarizes the main refactoring objective: migrating dark mode theming from duplicated CSS blocks in base.css to a unified light-dark() hybrid architecture in tokens.css.
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/add-css-layers-MlejK

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/demo.html (2)

546-546: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update description to reflect new token naming.

The text mentions --sf-color-* for light mode, but inputs now write to --sf-color-*-light source tokens.

📝 Suggested text update
-          Pick a color for any brand or status token in light or dark mode. Changes are applied live to <code>:root</code> via <code>--sf-color-*</code> and <code>--sf-color-*-dark</code>. Toggle dark mode in the header to preview the dark palette in context.
+          Pick a color for any brand or status token in light or dark mode. Changes are applied live to <code>:root</code> via <code>--sf-color-*-light</code> and <code>--sf-color-*-dark</code>. Toggle dark mode in the header to preview the dark palette in context.
🤖 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 546, Update the descriptive text to reflect the new
token names: change the mention of light-mode tokens from "--sf-color-*" to the
new "--sf-color-*-light" naming while keeping the dark-mode mention of
"--sf-color-*-dark" and the note that changes are applied to :root; also keep
the instruction about toggling dark mode in the header to preview the dark
palette so the sentence references "--sf-color-*-light" and "--sf-color-*-dark"
explicitly.

1573-1574: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Randomize function sets wrong token names for light mode.

The randomize function sets --sf-color-{key} (resolved tokens), but light mode inputs now bind to --sf-color-{key}-light (source tokens). This breaks the randomize feature—light mode inputs won't reflect the randomized values since the re-sync reads from the wrong tokens.

🐛 Proposed fix
-          applyToken('--sf-color-' + key,        light);
+          applyToken('--sf-color-' + key + '-light', light);
           applyToken('--sf-color-' + key + '-dark', dark);
🤖 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` around lines 1573 - 1574, The randomize function is writing
the light value to the resolved token name '--sf-color-{key}' instead of the
source token used by light-mode inputs; update the calls in randomize (where
applyToken is invoked) to set '--sf-color-' + key + '-light' for the light value
and keep '--sf-color-' + key + '-dark' for the dark value so the light-mode
inputs (which bind to the '-light' source tokens) will reflect randomized
values.
🤖 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.

Outside diff comments:
In `@docs/demo.html`:
- Line 546: Update the descriptive text to reflect the new token names: change
the mention of light-mode tokens from "--sf-color-*" to the new
"--sf-color-*-light" naming while keeping the dark-mode mention of
"--sf-color-*-dark" and the note that changes are applied to :root; also keep
the instruction about toggling dark mode in the header to preview the dark
palette so the sentence references "--sf-color-*-light" and "--sf-color-*-dark"
explicitly.
- Around line 1573-1574: The randomize function is writing the light value to
the resolved token name '--sf-color-{key}' instead of the source token used by
light-mode inputs; update the calls in randomize (where applyToken is invoked)
to set '--sf-color-' + key + '-light' for the light value and keep '--sf-color-'
+ key + '-dark' for the dark value so the light-mode inputs (which bind to the
'-light' source tokens) will reflect randomized values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1368e81e-d087-45bb-b7f1-06ec79308bcc

📥 Commits

Reviewing files that changed from the base of the PR and between 8414b8d and 1ef30e6.

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

- stylelint 17 + stylelint-config-standard; zero errors on full codebase
- release-it + @release-it/conventional-changelog: `npm run release` bumps
  version in package.json, writes CHANGELOG.md, creates annotated tag, pushes
- commitlint enforces Conventional Commits via .githooks/commit-msg
- pre-commit hook: lint staged CSS files then rebuild dist bundle
- GitHub Actions: ci.yml (lint + build on push/PR), release.yml (publishes
  GitHub Release with dist artifact when a v* tag is pushed)
- dist bundle header now includes version from package.json
- README: version/CI/license badges (always current from GitHub), token
  customisation examples, dark mode usage, dev commands

@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

🤖 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 @.github/workflows/release.yml:
- Around line 27-31: The awk pattern uses ${VERSION} as a regex which treats
dots and other metacharacters specially; fix by escaping VERSION before using it
in awk. Create an escaped variable (e.g. ESC_VERSION=$(printf '%s' "$VERSION" |
sed -e 's/[][^$.*/\\]/\\&/g') or similar) and then use ESC_VERSION in the awk
invocation that sets NOTES so the pattern matches the literal version string
(referencing VERSION, ESC_VERSION, and the NOTES/awk line).
- Around line 15-33: The workflow uses mutable major-version tags for three
actions—actions/checkout@v4, actions/setup-node@v4, and
softprops/action-gh-release@v2—so replace each tag with the corresponding
immutable full commit SHA for that action (e.g., actions/checkout@<full-sha>,
actions/setup-node@<full-sha>, softprops/action-gh-release@<full-sha>) to pin
behavior; locate the three occurrences in the release.yml step list and update
the version strings to the full SHAs (you can fetch the exact SHAs from each
action's GitHub repository releases/tags) and rotate them periodically when you
intentionally update the workflow.
🪄 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: 77007d4b-d066-4229-90ce-3d1f1542611f

📥 Commits

Reviewing files that changed from the base of the PR and between 1ef30e6 and 14e620a.

⛔ Files ignored due to path filters (2)
  • dist/slashed.essential.css is excluded by !**/dist/**
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (11)
  • .githooks/commit-msg
  • .githooks/pre-commit
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • .release-it.json
  • .stylelintrc.json
  • CHANGELOG.md
  • README.md
  • commitlint.config.js
  • package.json
  • scripts/bundle.js
✅ Files skipped from review due to trivial changes (5)
  • CHANGELOG.md
  • .githooks/commit-msg
  • commitlint.config.js
  • .githooks/pre-commit
  • README.md

Comment on lines +15 to +33
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- name: Extract changelog for this version
id: changelog
run: |
VERSION="${GITHUB_REF_NAME#v}"
NOTES=$(awk "/^## \[?${VERSION}\]?/{found=1; next} found && /^## /{exit} found{print}" CHANGELOG.md)
echo "notes<<EOF" >> "$GITHUB_OUTPUT"
echo "$NOTES" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
- uses: softprops/action-gh-release@v2
with:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify workflow action refs are immutable SHAs.
# Expected after fix: no output.
rg -nP '^\s*-\s*uses:\s*[^@\s]+@(?![0-9a-f]{40}\b)[^\s]+' .github/workflows/*.yml

Repository: codeslash-dev/SLASHED

Length of output: 600


Pin workflow actions to immutable commit SHAs.

Three actions in this file use mutable major version tags (@v4, @v2) instead of full commit SHAs (lines 15, 18, 32). Mutable tags allow upstream changes to alter your release pipeline without code review. Pin to commit SHAs and rotate them periodically:

  • actions/checkout@v4actions/checkout@<full-sha>
  • actions/setup-node@v4actions/setup-node@<full-sha>
  • softprops/action-gh-release@v2softprops/action-gh-release@<full-sha>
🤖 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 @.github/workflows/release.yml around lines 15 - 33, The workflow uses
mutable major-version tags for three actions—actions/checkout@v4,
actions/setup-node@v4, and softprops/action-gh-release@v2—so replace each tag
with the corresponding immutable full commit SHA for that action (e.g.,
actions/checkout@<full-sha>, actions/setup-node@<full-sha>,
softprops/action-gh-release@<full-sha>) to pin behavior; locate the three
occurrences in the release.yml step list and update the version strings to the
full SHAs (you can fetch the exact SHAs from each action's GitHub repository
releases/tags) and rotate them periodically when you intentionally update the
workflow.

Comment thread .github/workflows/release.yml
claude added 2 commits May 19, 2026 07:21
- demo.html: randomize() wrote to resolved --sf-color-{key} instead of
  source --sf-color-{key}-light; light-mode inputs now reflect randomized values
- demo.html: update customizer description to reference --sf-color-*-light
- release.yml: replace regex awk with awk -v target for exact version matching;
  add warning when CHANGELOG section is missing (dots/brackets no longer
  treated as regex metacharacters)
- version: 1.0.0 → 0.1.0
- release-it: increment defaults to patch, ignoreRecommendedBump: true
  (feat: and BREAKING CHANGE commits no longer silently bump minor/major)
- added release:minor and release:major scripts for intentional bumps
@jackgranatowski
jackgranatowski merged commit 85ad6d1 into main May 19, 2026
3 checks passed
@jackgranatowski
jackgranatowski deleted the claude/add-css-layers-MlejK branch May 31, 2026 18:00
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