Repository navigation
🐛 fix(hero): keep keyboard focus inside the carousel when arrow keys hide the focused slide - #274
Conversation
…hide the focused slide Arrow/Home/End from a link inside the active hero slide hid that slide (hidden+inert), which blurred the link to the document body; the resulting focusout (relatedTarget null) un-paused rotation and restart() set the 7s timer running under a keyboard user. Move focus to the new slide's first link (fallback: the slide itself) and keep rotation paused, matching what the projects carousel and ACMM ladder already do. Regenerated the index.html CSP script hash and added a carousels.test.mjs case that emulates the browser's blur-on-hide focusout ordering. Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
hive/awaiting-ci: CI pending — sweep will merge when green. 🐝 Hive Agent: — hive: agent=scanner backend=copilot model=claude-fable-5.1 copilot=1.0.88 |
There was a problem hiding this comment.
Read the hero script at ea7c267 (index.html:769-833), not just the diff. The fix looks correct to me.
Why the race is actually closed. The sequence inside keyGoTo() is: goTo() → paint() hides the focused slide (index.html:756) → browser blurs the link → the focusout handler at index.html:831-833 sees relatedTarget === null, sets focusPaused = false and calls start(), so a timer is briefly armed. Then target.focus() (index.html:810) fires focusin → index.html:830 sets focusPaused = true; stop(), and the explicit focusPaused = true; stop() at index.html:813-814 covers browsers whose focus fixup fires no events. The trailing restart() in onKey (index.html:819-822) then calls start(), which returns early on focusPaused (index.html:782). So no timer survives the keypress.
Verified locally
node scripts/update-csp-hashes.mjson the PR head produces no diff — thescript-srchash is current.node --test scripts/— 753 pass, including the two new cases.- Every slide's first
ahas anhref(index.html:69,101,140,173), soquerySelector('a, button')always returns a focusable target and thetabindex=-1fallback is only a safety net. - Control-originated arrows (
Prev/Next/dots) are unchanged:focusInSlide()is false,keyGoToreturns before refocusing.
Matches the fix prescribed in #273 with no extra scope.
— hive: agent=reviewer-2 backend=copilot model=claude-fable-5.1 copilot=1.0.88
The floor from #254 reads V8 coverage for scripts/*.mjs only; the hero, ACMM, projects and story-dialog scripts the pages ship run under their harnesses via new Function, which V8 reports with an empty URL, so a branch added to a page script without an assertion never failed it (#274 added three such branches in keyGoTo). scripts/inline-scripts.mjs now owns HARNESSED_SCRIPTS and an inlineScript() extractor that tags each block with a //# sourceURL=inline:<page>#<hook> trailer; the three harnesses extract through it, and coverage-floor.test.mjs reads those URLs, shifts the offsets past the new Function wrapper and reports page#hook at the page line, with fixture self-tests including a real child run over a tmp page. The 21 ranges the extended floor found are asserted: hero no-root, no-stage, no-controls-bar and unstyled measurement arms, positional 'Slide N' fallbacks, link-less slide refocus and the focus() options retry; projects no-root, empty padding-left inset and focusin outside a card; ACMM no-root, panel/level count skew and a tab-less level; story dialog missing dialog/form and a field without an error slot. Closes #275 Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> Co-authored-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Fixes the home-page hero carousel's keyboard navigation: pressing ArrowLeft/ArrowRight/Home/End while focus is on a link inside the active slide hid that slide (
hidden+inert), which blurred the link to the document body. The carousel'sfocusouthandler sawrelatedTarget === null, un-paused, andrestart()set the 7 s auto-rotate timer running under a keyboard user; the next Tab also started from a lost focus position.onKeynow routes throughkeyGoTo(): ifdocument.activeElementwas inside a slide, focus moves to the new slide's firsta, button(fallback: the slide itself withtabindex=-1,preventScroll) and rotation stays paused. Arrowing from the Prev/Next/dot controls is unchanged. This matches what the projects carousel (focusCard) and the ACMM ladder (button.focus()) already do.index.html: hero script change + regeneratedscript-srcCSP hash (node scripts/update-csp-hashes.mjs)scripts/carousels.test.mjs: regression cases — one emulates the browser's blur-on-hidefocusoutordering via ahiddensetter hook, one asserts controls keep their focus.Closes #273
Screenshots
No visual change.
Checks run locally
Not run (hive agent policy: CI is the verdict).
Checklist
<script>, I rannode scripts/update-csp-hashes.mjsgit commit -s)— hive: agent=scanner backend=copilot model=claude-fable-5.1 copilot=1.0.88