Skip to content

Tokenize per-theme card/focus/nav/code CSS rules - #6254

Merged
atomantic merged 3 commits into
mainfrom
claim/issue-6248
Sep 4, 2026
Merged

Tokenize per-theme card/focus/nav/code CSS rules#6254
atomantic merged 3 commits into
mainfrom
claim/issue-6248

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Summary

  • Replaces 8 near-identical per-theme selector blocks in client/src/index.css (card border tint + top wash), 3 (focus glow), 4 (active-nav rule), and a Kestrel-only code-glow + caret block with shared html[data-port-theme] rules driven by new CSS custom properties.
  • Each theme's specific value now lives in its tokens object in client/src/themes/portosThemes.js instead of a CSS selector block — the same pattern the earlier Kestrel PR used for heading tokens.
  • New tokens: --port-card-border-color/-alpha, --port-card-surface-image/-size, --port-focus-shadow (generic default) + --port-focus-glow (theme-scoped override), --port-nav-active-rule/-glow, --port-code-glow. Not added to REQUIRED_TOKEN_VARS — they stay optional, as the issue specifies.
  • One deliberate, non-neutral change per the issue's plan: caret-color: rgb(var(--port-accent)) now applies to every theme's inputs/code blocks, not just Kestrel's.
  • docs/themes/README.md gets a "Surface & control tokens" section.

Closes #6248

Review notes

Verified pixel parity across all 10 themes (card border, selected-card border, background-image, focus box-shadow on both a plain input and an accent-colored button, [role="button"] focus, nav border-left/text-shadow/color, code text-shadow) with a headless-Chrome harness that diffs computed styles between the old (origin/main) and new stylesheets — zero diffs except the intentional caret-color change.

Two local reviewers ran (codex, claude), 3 rounds. Codex caught two real cascade regressions in round 1, both fixed and re-verified: an unconditional card-border rule would have out-specificity'd a component's own semantic border class (e.g. MediaCard's "selected" border-port-accent) on every theme instead of just the ones that had it before; and folding a per-theme focus glow into the generic button:focus-visible rule dropped its specificity below .bg-port-accent's interactive shadow. A round-2 token-leak finding (the theme-scoped focus glow bleeding onto [role="button"]/[tabindex="0"] via a shared token) was also fixed by splitting into --port-focus-shadow / --port-focus-glow. Codex was clean by round 3.

Claude repeatedly flagged that removing Lumen Glass Day's dead/redundant aside nav [aria-current="page"] { color: rgb(var(--port-accent)); } rule would regress the active nav link's text color. I verified this is a false positive: every real render site for that element (SingleNavRow, WorkingSetRow, nested NavLink children in Layout.jsx) already applies Tailwind's .text-port-accent class whenever aria-current="page" is set, which resolves to the identical rgb(var(--port-accent)) value — confirmed with the same harness, adding a .text-port-accent class to the test markup and diffing computed color directly (zero diff, all 10 themes). Left as-is rather than adding an unnecessary token.

Test plan

  • node scripts/check-themes.js — theme contract OK for all 10 themes.
  • cd client && npx vitest run — 843 files, 10357 passed, 6 skipped.
  • cd client && npm run build — succeeds.
  • Headless-Chrome computed-style diff harness across all 10 themes (described above) — zero unintended diffs.

Replaces 8 near-identical per-theme selector blocks (card border tint + top
wash), 3 (focus glow), 4 (active-nav rule), and a Kestrel-only code glow +
caret block with shared html[data-port-theme] rules driven by new tokens
(--port-card-border-color/-alpha, --port-card-surface-image/-size,
--port-focus-shadow, --port-field-focus-shadow, --port-nav-active-rule/-glow,
--port-code-glow), each theme now setting values in its portosThemes.js
manifest instead of a CSS selector block — the same pattern the Kestrel PR
used for heading tokens.

Verified pixel-for-pixel identical across all 10 themes (card border,
background-image, focus box-shadow, nav border-left/text-shadow, code
text-shadow) via a headless-Chrome harness diffing computed styles between
the old and new stylesheets, except the one deliberate addition the issue
calls for: caret-color: rgb(var(--port-accent)) now applies to every theme's
inputs/code blocks, not just Kestrel's.

Caught and fixed one regression during that verification: the new shared
nav-active rule unconditionally declared text-shadow, which broke Black ICE
Terminal's inherited body-wide glow reaching the active nav link — fixed by
defaulting --port-nav-active-glow to `inherit` instead of `none`.
…ering

Two cascade regressions caught in local review: the unconditional shared
card-border rule out-specificity'd a component's own semantic border class
(e.g. MediaCard's `border-port-accent` "selected" state, or an error border)
on every theme, not just the ones that used to override it; and folding the
per-theme focus-glow override into the bare `button:focus-visible` rule
dropped its specificity below `.bg-port-accent`'s interactive shadow, so an
accent-colored button lost its focus glow on Lumen Glass/Lumen Glass Day/
Kestrel Neon. Both are fixed by keeping the token-driven values but scoping
the rule to the exact theme(s) that use them, restoring the original
selector specificity rather than widening it. Re-verified pixel parity
across all 10 themes (including a "selected" card and a focused accent
button) with the same headless-Chrome computed-style harness.
Codex review round 2 caught a token leak: reusing --port-focus-shadow for
both the generic button/a/[role=button]/[tabindex=0] rule and the Lumen
Glass/Lumen Glass Day/Kestrel Neon theme-scoped override meant those three
themes' stronger glow bled onto [role="button"]/[tabindex="0"] elements the
original per-theme selector never matched (e.g. tag chips, the MIDI
piano-roll canvas). Split it into --port-focus-shadow (generic rule only,
never overridden per theme) and --port-focus-glow (theme-scoped rule only).

Also fixed a gap the first round-2 fix introduced: Lumen Glass Day's
`.bg-port-card`/`/50` card-border coverage was dropped when the shared
card-border rule's theme list was narrowed to fix the earlier clobbering
finding — added it back alongside its existing wider bg-port-card/NN
selector. Re-verified pixel parity across all 10 themes with the same
headless-Chrome harness, now also covering a role="button" element.
@atomantic
atomantic merged commit 0468c74 into main Sep 4, 2026
7 checks passed
@atomantic
atomantic deleted the claim/issue-6248 branch September 4, 2026 21:04
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.

Tokenize the per-theme card/focus/nav/code rules in index.css (finish what heading tokens started)

1 participant