Skip to content

fix(theme): polish Matrix + HAL 9000 (and other dark) presets to WCAG AA - #4897

Merged
senamakel merged 3 commits into
tinyhumansai:mainfrom
M3gA-Mind:feat/4811-polish-dark-themes
Jul 15, 2026
Merged

senamakel merged 3 commits into
tinyhumansai:mainfrom
M3gA-Mind:feat/4811-polish-dark-themes

Conversation

@M3gA-Mind

@M3gA-Mind M3gA-Mind commented Jul 15, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #4811

Summary

Take Option A (polish, not removal): keep every theme + variant id so persisted selections still resolve, and raise the weak dark palettes to the WCAG AA bar.

Changes

  • HAL 9000 — deepen resting primary (230 40 40 → 214 30 30) so the white button label clears 4.5:1 (was 4.47); accent text keeps the lighter 300/400 shades so the glowing-red identity holds.
  • Matrix — nudge content-faint up so faint/placeholder text clears 3:1 on the brighter hover/strong green surfaces (was ~2.6:1).
  • Ocean Dark / Sepia Dark — brighten content-muted (≥4.5) and content-faint (≥3); set a dark content-inverted so labels over the light primary fills clear AA.
  • Add channelContrast() helper + presets.contrast.test.ts: a WCAG AA gate over every shipped dark preset (body/muted ≥4.5:1, faint ≥3:1 across surface states, button label ≥4.5:1, accent ≥4.5:1, primary-500 ≥3:1 as UI).

Verification

  • All 5 dark presets clear AA (luminance math, mirrored in the new test).
  • pnpm typecheck: changed files clean.

Notes

  • Kept all theme/variant ids stable. One disabled-state transient can't reach AA without a Button-component redesign (out of token scope) — flagged, not changed.

Summary by CodeRabbit

  • Accessibility
    • Improved contrast in several built-in dark themes to better support WCAG readability standards.
    • Adjusted muted, faint, inverted, and primary colors in select presets for clearer text and interface elements.
    • Added contrast validation across dark theme combinations to help ensure accessible text, controls, and links.

Option A (polish, not removal): keep every theme + variant id so persisted
selections still resolve, and raise the weak dark palettes to the AA bar.

- HAL 9000: deepen resting primary (230 40 40 -> 214 30 30) so the white
  button label clears 4.5:1 (was 4.47); accent text keeps the lighter 300/400
  shades so the glowing-red identity holds.
- Matrix: nudge content-faint up so faint/placeholder text clears 3:1 on the
  brighter hover/strong green surfaces (was ~2.6:1).
- Ocean Dark / Sepia Dark: brighten content-muted (>=4.5) and content-faint
  (>=3) so they hold on the lighter elevated surfaces, and set a dark
  content-inverted so labels over the light-blue / tan primary fills clear AA
  (white was ~2.5-2.6:1) — matching the pattern Matrix already uses for green.

Add channelContrast() helper + presets.contrast.test.ts, a WCAG AA gate over
every shipped dark preset: body/muted text >=4.5:1 and faint text >=3:1 across
all surface states, primary button label >=4.5:1 on its resting/active fills,
accent text >=4.5:1, and primary-500 >=3:1 as a UI element.
@M3gA-Mind
M3gA-Mind requested a review from a team July 15, 2026 12:31
@coderabbitai

coderabbitai Bot commented Jul 15, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 24 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6d765ad8-b10b-492e-b3ce-af220bef3066

📥 Commits

Reviewing files that changed from the base of the PR and between 364a0fe and 2e7d150.

📒 Files selected for processing (2)
  • app/src/lib/theme/presets.contrast.test.ts
  • app/src/lib/theme/presets.ts
📝 Walkthrough

Walkthrough

Adds a WCAG contrast helper, updates selected dark theme tokens, and introduces automated checks covering contrast requirements across all built-in dark presets.

Changes

Dark theme contrast compliance

Layer / File(s) Summary
Contrast utility and unit coverage
app/src/lib/theme/color.ts, app/src/lib/theme/color.test.ts
Exports channelContrast and tests WCAG ratio boundaries, symmetry, identity, and threshold behavior.
Dark preset token adjustments
app/src/lib/theme/presets.ts
Adjusts contrast-related tokens in Ocean Dark, Sepia Dark, Matrix, and HAL 9000 presets.
Built-in preset contrast gate
app/src/lib/theme/presets.contrast.test.ts
Resolves every dark preset and validates text, surface, button, accent, link, and UI-element contrast thresholds.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

I’m a rabbit with colors bright,
Checking dark themes day and night.
Ratios hop from one to twenty-one,
Muted shades now greet the sun.
WCAG gates all neatly run!

🚥 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 summarizes the main change: polishing dark presets for WCAG AA compliance.
Linked Issues check ✅ Passed The PR polishes Matrix, HAL 9000, Ocean Dark, and Sepia Dark and adds contrast tests, matching the Option A accessibility goals.
Out of Scope Changes check ✅ Passed All changes stay within dark-theme accessibility polishing and contrast validation; no unrelated scope was introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@app/src/lib/theme/presets.contrast.test.ts`:
- Around line 10-35: Eliminate the duplicated dark token values in DARK_BASE by
exposing a shared dark-defaults source used by both runtime theme resolution and
this test. Update the test to consume that shared export, or add an automated
parity check against tokens.css, while preserving the existing merged-theme
behavior.
- Around line 40-48: Expand the contrast test matrix around SURFACES and its
related assertions to include the documented surface-overlay state, primary-400
for hover button labels alongside the existing primary-500/600 checks, and every
relevant surface rather than only surface. Ensure all runtime states documented
in presets.ts are validated for both button-label and UI-element contrast
requirements.
- Around line 37-39: Update the contrast assertions for content-faint in the
relevant test section to require the AA_TEXT threshold of 4.5:1, rather than
AA_LARGE at 3:1. Keep AA_LARGE available only for separately identified exempt
tokens or tests.
🪄 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

Run ID: feb33575-cfbc-4113-94b5-df55b7ac727d

📥 Commits

Reviewing files that changed from the base of the PR and between 0e86b85 and 364a0fe.

📒 Files selected for processing (4)
  • app/src/lib/theme/color.test.ts
  • app/src/lib/theme/color.ts
  • app/src/lib/theme/presets.contrast.test.ts
  • app/src/lib/theme/presets.ts

- Format presets.contrast.test.ts (Prettier).
- DARK_BASE single-source-of-truth: add a parity test that parses tokens.css
  (`:root` overlaid by `:root.dark`) and fails if the JS mirror drifts from the
  CSS, so a token edit can't leave the gate green while runtime colours change.
- Expand the gate: include `surface-overlay` in the surface matrix and hold the
  primary-500 UI-element ratio (>=3:1) on every surface, not just `surface`.
- Document why the transient dark hover fill (dark:hover:bg-primary-400) is not
  gated: it lightens the fill app-wide (even the untouched `dark` preset sits at
  ~2.5:1 white-on-primary-400), so it's a Button behaviour, not a token.
- Correct the HAL primary-500 comment (active fill primary-600 clears AA; drop
  the inaccurate claim about the hover shade).
@M3gA-Mind

Copy link
Copy Markdown
Collaborator Author

Pushed 5f8350b — Prettier fix + CodeRabbit review addressed

CI (Frontend Checks / Prettier)
prettier --write on the changed theme files; presets.contrast.test.ts was the unformatted file. prettier --check now clean across all changed files.

CodeRabbit review (3 actionable):

  1. Single source of truth for dark defaults — fixed. Added a DARK_BASE parity with tokens.css test that parses :root + :root.dark from tokens.css and asserts the JS mirror matches the CSS cascade, so a future token edit fails the gate instead of drifting silently.
  2. content-faint at 3:1 — declined with reasoning. It's the placeholder/disabled tier (WCAG 1.4.3-exempt); the normal-text tiers (content/content-secondary/content-muted) are already gated at 4.5:1; and the untouched historical dark preset itself ships faint at 3.78:1, so a 4.5 floor would fail it and is out of scope.
  3. Expand the gate — partially fixed. Added surface-overlay to the surface matrix and extended the primary-500 UI-element check (≥3:1) to every surface (min 3.34, all pass). Declined gating the transient primary-400 hover button label — dark:hover:bg-primary-400 is a shared Button behaviour (~2.5:1 even in the default dark preset), not a token; documented inline and corrected the HAL comment.

No product-code palette values changed in this push beyond the earlier polish; changes here are test coverage + a doc-comment correction. All dark presets still clear AA under the (now stricter) gate.

Option A (polish, not removal) unchanged — every theme + variant id preserved.

@senamakel
senamakel merged commit bfaa261 into tinyhumansai:main Jul 15, 2026
17 checks passed
senamakel added a commit to M3gA-Mind/openhuman that referenced this pull request Jul 18, 2026
… AA (tinyhumansai#4897)

Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
senamakel pushed a commit to nocstah/openhuman that referenced this pull request Sep 11, 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.

Polish dark themes (Matrix, HAL 9000) — consolidate theme set to fewer, higher-quality options

2 participants