Skip to content

fix(pwa): give the dark topnav its own button skin - #111

Merged
ralyodio merged 1 commit into
masterfrom
worktree-fix-topnav-button-contrast
Jul 30, 2026
Merged

fix(pwa): give the dark topnav its own button skin#111
ralyodio merged 1 commit into
masterfrom
worktree-fix-topnav-button-contrast

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

The bug

.btn in apps/pwa/src/lib/html.mjs is skinned for the light page ground — white fill, ink text, light border. The topnav rail is dark (--rail:#101418), and it had no button variant of its own, so those light-mode buttons were dropped straight onto it.

Two distinct symptoms, matching the report ("washed out", "wrong theme color", "no contrast"):

1. Settings is invisible. .bar a{color:var(--rail-text)} has specificity (0,1,1); .btn's color is (0,1,0). So .bar a wins, and <a class="btn" href="/settings">Settings</a> renders #f6f7f4 text on the #ffffff button fill:

1.08:1 — below even the 3:1 large-text floor. Effectively unreadable.

2. Sign out is the wrong theme. It's a <button>, not an <a>, so it keeps ink text and stays legible — but it's a white light-mode chip sitting on a dark bar.

Confirmed live: curl https://app.logicsrc.com/ serves exactly this CSS.

The fix

A rail-scoped button skin, added right after the existing .bar .brand .app rule. Every selector is .bar-scoped, so body buttons are untouched.

before after
Settings label 1.08:1 17.20:1
Sign out light chip on dark rail 17.20:1, on-theme ✅
Button border --rail-line, 1.4:1 ❌ 38% rail-text, 3.40:1
Focus ring green, 3.60:1 mint, 9.03:1
Email .faint 5.85:1 --rail-dim, 9.67:1 ✅
Sign in (acid) green — already fine unchanged, 5.13:1 ✅

The 38% border alpha isn't arbitrary — it's the floor that clears WCAG's 3:1 non-text minimum against the rail. --rail-line (the bar's own divider color) is only 1.4:1 and disappears entirely as a button outline. Both constants are commented in place.

Sign in deliberately keeps the green accent: .bar .btn.acid (0,3,0) outranks .bar .btn (0,2,0), so the one intentional theme-colored button stays intentional.

Verification

  • npm test in apps/pwa13/13 pass.
  • Rendered the real signed-in appBar() through jsdom to resolve the actual cascade, not my reading of it. It confirms the root cause and the fix: a.btn color comes from .bar a before, and button.btn flips var(--text)var(--rail-text) after, proving .bar .btn (0,2,0) beats .bar a (0,1,1).
  • All contrast figures computed with the WCAG relative-luminance formula, alpha borders composited over the rail first.

Screenshots weren't possible — the cached Chromium is missing libatk-1.0.so.0 and installing it needs sudo. Verification is cascade-resolution plus contrast math rather than a visual diff; worth an eyeball on the rendered bar before merge.

🤖 Generated with Claude Code

The .btn skin is built for the light page ground (white fill, ink text).
The topnav rail is #101418, so those buttons landed there as stray
light-mode chips -- and ".bar a{color:var(--rail-text)}" outranks ".btn"
on color (0,1,1 vs 0,1,0), so <a class="btn">Settings</a> painted
rail-text on a white fill: 1.08:1, effectively invisible.

Scope a rail variant to .bar: transparent fill, rail-text label, and a
border at 38% rail-text (3.40:1, clearing the 3:1 non-text minimum --
--rail-line is only 1.4:1 and vanishes). Sign in keeps the green accent,
the focus ring moves green -> mint (3.60:1 -> 9.03:1), and .faint/.dim
in the bar resolve to --rail-dim.

Settings and Sign out go 1.08:1 and light-chip-on-dark to 17.20:1.
Body buttons are untouched -- every rule is .bar-scoped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio marked this pull request as ready for review July 30, 2026 19:40
@ralyodio
ralyodio merged commit d28f20b into master Jul 30, 2026
5 checks passed
@ralyodio
ralyodio deleted the worktree-fix-topnav-button-contrast branch July 30, 2026 19:41
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.

1 participant