fix: focus ring rendering + light theme regressions (v0.39.1)#4
Merged
Conversation
The v0.39.0 :focus-visible rule used outline-width: 2px solid var(--accent) but Chromium has a long-standing quirk where the UA :focus-visible outline-style: auto 1px ring isn't fully replaced by an author outline-style: solid — the rendered width stays at 1px no matter what the author specifies. Headless UI testing surfaced this: every focused button showed a Chromium-default 1px ring instead of the designed 2px accent ring. Switch the focus indicator from `outline` to `box-shadow`. box-shadow has no cascade quirk with the UA layer, is the modern recommendation for keyboard focus rings, and benefits from the existing component-level transition: all rules so the ring fades in smoothly on focus. Verified via the new scripts/test-ui-headless.py: every focused interactive element now reports box-shadow as exactly "rgb(99, 102, 241) 0px 0px 0px 2px". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SetupWizard.css had a hardcoded Catppuccin palette (#1e1e2e, #cdd6f4, #89b4fa, etc), so the first-launch modal stayed dark even when the user flipped data-theme=light. Headless UI testing caught this: the screenshot of the wizard in light mode showed it floating dark against a light app. Rewrite the wizard entirely in --bg-*/--text-*/--accent tokens. Verified in both themes. The "Skip for now" button (previously caught by my own :focus-visible test as having a slightly-different focus color) is now explicitly themed via .setup-btn.skip selector matching the existing .setup-btn.secondary styling. Toast.css used hardcoded RGBA values (rgba(52, 211, 153, ...)) for success/error/warning/info tints. Replace with color-mix(in srgb, var(--success) 15%, transparent) so the tint follows the status tokens when the theme flips. Status sweep: 17 component CSS files still hardcode colors; see docs/superpowers/specs/2026-05-28-theme-token-sweep.md for the full classification (Category A = bug, B = intentional like MarkdownRenderer syntax highlighting, C = TSX inline styles). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
scripts/test-ui-headless.py — boots the Vite dev server in headless Chromium with a Tauri shim, walks through the default view, focus ring, light theme, Serial sidebar, and Plot view toggle. Captures screenshots under scripts/ui-screenshots/ (gitignored). Useful for catching CSS regressions before release. Required the shim because TitleBar.tsx:6 calls getCurrentWindow() at module-import time which crashes the React tree outside the Tauri runtime. scripts/test-pure-logic.mjs — compiles serial-parsing.ts and ai-pricing.ts via esbuild and runs 30 assertions covering CSV/JSON/ key-value parsing, pricing math, and formatter rounding. Catches the edge cases that are easy to break and hard to spot in a smoke test. .gitignore — exclude scripts/ui-screenshots/ since those are test artifacts. The .py/.mjs scripts themselves are tracked. docs/superpowers/specs/2026-05-28-theme-token-sweep.md — the rest of the theme audit. 17 component files still have hardcoded hex values; classifies them into Category A (replace with tokens), B (intentional domain palettes), C (TSX inline styles) so a future PR can sweep them mechanically. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hotfix on top of v0.39.0 — caught by post-merge headless UI testing: - fix(design): focus ring rendered as 1px Chromium-UA default instead of the designed 2px accent ring (CSS outline quirk; switched to box-shadow) - fix(theme): SetupWizard and Toast had hardcoded color palettes so light theme only restyled the outer chrome Plus tooling additions and the theme-token sweep spec for follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Hotfix on top of v0.39.0 — caught by post-merge headless UI testing in a Playwright Chromium with a Tauri shim.
What broke and how
Focus ring rendered as 1px UA default instead of 2px accent. The v0.39.0
:focus-visiblerule usedoutline: 2px solid var(--accent). Chromium has a long-standing quirk where the UA:focus-visibleoutline-style: auto1px ring can't be fully replaced by an authoroutline-style: solid— the rendered width stays at 1px regardless of!importantor specificity. Confirmed via CDPgetMatchedStylesForNode: my rule was in the cascade winning specificity, but the engine's computed outline-width stayed at the UA's 1px. Switched the focus indicator fromoutlinetobox-shadow. After the fix: every focused interactive element reportsbox-shadow: rgb(99, 102, 241) 0px 0px 0px 2px— clean 2px accent ring.Light theme only flipped the outer chrome. The SetupWizard modal stayed dark in light theme because its CSS hardcoded a Catppuccin palette (
#1e1e2e,#cdd6f4,#89b4fa, ...). Toast tints similarly used hardcoded RGBA literals. Rewrote both to use design tokens (SetupWizard) andcolor-mix(in srgb, var(--success) 15%, transparent)(Toast). Verified in both themes.Test infrastructure added
scripts/test-ui-headless.py— Playwright walk through the dev server with a Tauri shim. Catches CSS regressions, focus-ring breakage, missing empty states. Required because TitleBar.tsx callsgetCurrentWindow()at module-import — crashes the React tree outside a Tauri runtime; the shim fixes that for testing.scripts/test-pure-logic.mjs— esbuild + Node 30-assertion suite forserial-parsing.ts+ai-pricing.ts.Follow-up spec
docs/superpowers/specs/2026-05-28-theme-token-sweep.mdclassifies the remaining 17 component files with hardcoded colors. Category A (replace with tokens) is ~9 files, Category B (intentional palettes like MarkdownRenderer's syntax-highlighting colors) is correct as-is, Category C is TSX inline styles.Verification
npm run check:versions→ OK at v0.39.1npm run build→ cleancargo clippy --all-targets -- -D warnings→ cleancargo test --all-targets→ 16/16 passingpython scripts/test-ui-headless.py→ focus ring 2px confirmed on every Tabbed element, SetupWizard renders correctly in dark + light, Plot view toggle reaches empty statepython scripts/test-pure-logic.mjs→ 30/30🤖 Generated with Claude Code