Repository navigation
fix(details): keep the page focus ring off after pointer focus - #2189
Conversation
Clicking into a detail page focuses its scroll region (the shell host, tabindex 0). Chromium promotes that pointer focus to :focus-visible on the next keypress, so arrow-key seeking during inline playback outlined the whole page in windowed mode. Record whether focus arrived by pointer and suppress the ring for that focus; keyboard (Tab) focus keeps it. A window blur/refocus keeps the verdict, since the shell stays the active element across it. Closes 4gray#2170. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[Medium impact] The PR appears safe to merge; no actionable defects were found. SummaryKeeps the detail page’s focus ring hidden after pointer focus, including later seek keys and window refocus.
No actionable issues were found. Tests and visual checks were not run during this review. Reviews (5) · Last reviewed commit: "Merge master into the detail focus fix" · Reviewed by Greptile |
|
@codex review |
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. 👍 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #2189 +/- ##
========================================
Coverage 81.60% 81.60%
========================================
Files 1884 1884
Lines 98518 98533 +15
Branches 23705 22973 -732
========================================
+ Hits 80392 80407 +15
Misses 11103 11103
Partials 7023 7023
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. |
|
@codex review |
|
Codex Review: Didn't find any major issues. Can't wait for the next one! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Expose the clipboard helper through a dedicated services entry point so only the lazy Xtream details and download actions import it. On the PR merge tree, renderer.initialBytes drops from 1,200,590 to 1,200,062 bytes against the unchanged 1,200,301-byte limit.
|
@codex review |
|
Codex Review: Didn't find any major issues. More of your lovely PRs please. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex review |
|
Codex Review: Didn't find any major issues. Breezy! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Fixes #2170.
What changed
PortalDetailShellComponentrecords, when its host receives focus, whether Chromium already treats that focus as:focus-visible. A pointer focus setsshell-host--pointer-focus, and&.shell-host--pointer-focus:focus-visible { outline: none; }keeps the ring off for that focus. Keyboard (Tab) focus, and entry focus after keyboard navigation, still draw the ring.document.activeElementacross it, so the returningfocusevent does not re-read:focus-visible, which the earlier keypress has already promoted.portal-detail-shell.component.spec.ts. Both fail onmasterand pass with this change..changes/details-player-focus-ring.md.Why
The detail shell host is focusable (
tabindex="0",role="region") and owns the page scroll. Clicking anywhere inside it that is not a control (the inline player's video, the hero, the episode list) focuses the host without a ring. Chromium then promotes the already-focused element to:focus-visibleon the next keypress. ArrowLeft/ArrowRight are not handled byonScrollKey, so they reach the player's seek shortcut, and the host's:focus-visiblering outlines the whole page: player, header and episode list, as reported. The shell is shared, so this also applies to Xtream series/VOD, M3U VOD, downloads, recordings and collection detail.Reproduced in headless Chrome over CDP with the app's global focus CSS and the
main[tabindex=-1] > app-portal-detail-shell[tabindex=0]structure:masterbody, script refocus (unwind()after Escape)Release note
.changes/Checks
Tests added or updated for the changed behavior
pnpm run lintand the affectedpnpm nx test <project>passjest libs/ui/components/src/lib/portal-detail-shell: 31 tests passed. The two new tests fail onmaster.Full
componentsproject: 2 suites fail (channel-scroll-focus.directive.spec.ts,season-container.component.spec.ts), and they fail the same way onmaster.pnpm nx lint components: 0 errors. Changed files are Prettier-clean.node tools/nx/check-focus-visible.mjsandcheck-focus-ring-colour.mjs: pass.pnpm run release:notes:validate: valid.Not run: Electron E2E. A "click the shell, press ArrowRight, expect no outline" case would fit next to
expectNoRingAfterClickinapps/electron-backend-e2e/src/keyboard-focus-ring.e2e.ts, and I'm happy to add it if you want it in this PR. I also couldn't run the Codex/Greptile local review gate fromAGENTS.md, because I don't have those CLIs as an outside contributor.🤖 Generated with Claude Code