fix(shell): clean keyboard-only focus ring on Compliance toggle - #10
Merged
Merged
Conversation
The Compliance toggle (a button role="switch") had no local focus styling, so it fell back to the global button:focus-visible rule in globals.css, which adds ring-offset-2 and squares the pill corners. On macOS Safari, clicking a role="switch" button can leave :focus-visible applied after a mouse click, so the heavy ring looked stuck in both the on and off states. Match the adjacent LabSwitcher's cleaner treatment by adding focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action locally, and drop focus on pointer-driven activation only (onPointerUp, gated on a real pointerType) so a mouse click leaves no stuck ring while keyboard Tab still shows a visible, AA focus ring (WCAG 2.4.7). Scope is ComplianceToggle.tsx only. No change to globals.css or other controls. Toggle behavior, aria-checked, size, colors, and layout are unchanged. Verified in-browser (Chromium via Playwright): mouse click leaves no ring and flips aria-checked; keyboard Tab shows the same ring as the LabSwitcher; Enter and Space toggle without blurring. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
build-with-dhiraj
added a commit
that referenced
this pull request
Jun 23, 2026
The switch knob span had a vertical anchor (top-0.5) but no horizontal anchor, so left:auto resolved its base position to mid-track (~14px) instead of 0. Measured live: OFF rendered the knob 16px from the track left (reading like ON), and ON pushed the knob to 28px, overflowing the 28px track by 12px (knob off the right edge, looks broken). Add left-0 to the knob so its base is 0. OFF now translates 2px (knob at the left), ON translates 14px (knob right, right edge at 26px, fully inside the 28px track, no overflow). Verified in a headless browser: OFF knob.left - track.left = 2px, ON = 14px, right-overflow 0 in both, aria-checked flips correctly, no console errors. Follow-up to the focus-ring fix shipped in #10. Track, colors, sizes, the translate values, and the focus treatment are unchanged. Co-authored-by: Claude Opus 4.8 (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.
What
Fixes the Compliance toggle's heavy, stuck focus ring in the LabOS top nav. The pill showed a thick blue ring around the whole control in both on and off states, including after a mouse click, while the adjacent McClean Lab switcher did not.
Root cause
src/components/shell/ComplianceToggle.tsxis a<button role="switch">with no local focus styling, so it fell back to the globalbutton:focus-visiblerule insrc/app/globals.css(ring-2 ring-action ring-offset-2 ring-offset-whiteplus aborder-radius: 0.5remthat squares the pill). On macOS Safari, clicking arole="switch"button can leave:focus-visibleapplied after mouse activation, so the ring looked stuck. The siblingLabSwitcheralready overrides the global rule locally, which is why it looked clean.Fix (one file,
ComplianceToggle.tsxonly)focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-action.onPointerUp, gated on a realpointerType(empty for keyboard-synthesized clicks), so a mouse click leaves no stuck ring while keyboard Tab still shows a visible, AA focus ring (WCAG 2.4.7).No change to
globals.css(other controls still rely on the global rule) and no change tomcclean.ts. Toggle behavior,aria-checked, size, colors, and layout are unchanged.Verification (in-browser, Chromium via Playwright)
aria-checked(false to true to false) and leaves no ring; the pill is as clean as the McClean switcher in both states.aria-checkedand keep the element focused with the ring. The pointer-blur guard never fires for keyboard activation (nopointerup, emptypointerType).Engine note: the stuck-on-mouse-click ring is a WebKit/Safari
:focus-visiblequirk and does not reproduce in Chromium (a mouse click there never leaves:focus-visible), so the pointer-blur guard is applied as belt-and-suspenders. Keyboard focus visibility was confirmed preserved.CI parity
npx tsc --noEmit: passnpm run build(Next build): passlintscript; Next's build-time type and lint step passed.🤖 Generated with Claude Code