Skip to content

fix(nav): focus the section after a cross-route nav tap - #275

Merged
yigitdot merged 2 commits into
mainfrom
claude/cross-route-section-focus-613d5a
Oct 9, 2026
Merged

yigitdot merged 2 commits into
mainfrom
claude/cross-route-section-focus-613d5a

Conversation

@yigitdot

@yigitdot yigitdot commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #274

Why

Tapping a section on / moves keyboard and screen-reader focus into it. Tapping one from /blog/* or /legal/* didn't. The viewport landed on the section, but the next Tab started from the top of the nav:

Next's hash scroll doesn't move focus at all. Next 16.3.8 defaults experimental.appNewScrollHandler to true, and that handler (InnerScrollHandlerNew in layout-router.js) intentionally leaves focus untouched. (The issue assumed Next calls domNode.focus(); only the legacy handler does that.)

What changed

  • lib/scroll.ts adds three helpers:
    • focusSection(el) holds the focus rule both navs already inlined: add tabindex="-1" only if the element isn't already focusable, and focus with preventScroll: true.
    • queueSectionFocus(id) and takeSectionFocus(hash) form a single-use, module-level queue. takeSectionFocus returns the id only when the URL actually landed on #id, so a stale id left by an abandoned nav can't focus a section on a later home arrival.
  • Chrome.tsx:
    • A section click from another route queues its id before falling through to <Link>.
    • A new effect keyed on pathname focuses the queued section once home has committed. It is a passive effect, so it runs after Next's hash scroll, which runs in the layout phase.
    • The same-page path now calls focusSection.
  • MobileMenu.tsx:
    • A section tap from another route queues its id and sets focusMovedToSectionRef, so the toggle-refocus effect doesn't take focus back.
    • home is unchanged and still returns focus to the toggle.
    • The same-page path now calls focusSection.
    • Comments that described the old behaviour are updated.
  • lib/scroll.test.ts adds unit tests for both helpers, using fake elements, so no DOM env is needed. They cover:
    • tabindex set before focus()
    • preventScroll
    • the latest queued section winning
    • exact hash matching
    • single use
    • clearing the queue on a mismatch

Verification

  • pnpm test, pnpm typecheck, pnpm lint, pnpm format:check and pnpm build (including check:out and check:og-image) all pass.
  • Checked in a browser against the static export with real clicks:
    • Desktop nav at 1440 wide and drawer at 375×812, from /blog/, /blog/what-were-building/ and /legal/privacy/: document.activeElement is the section and the URL has one hash. Once the scroll settles the section is at the viewport top, and the next Tab reaches the first focusable element inside #faq.
    • #method has no focusable children, so Tab correctly continues to the next tab stop after it.
    • home from another route: lands on / with no hash. In the drawer, focus is on .mm-toggle; #intro never gets focus.
    • Same-page drawer tap on /: unchanged. The same-page desktop path only switched to the shared helper and wasn't clicked through in the browser.
  • In the browser pane, the smooth hash scroll started about 2s after arrival. A control run with section focus disabled showed the same delay and the same final position, so it comes from the test pane, not this change.

🤖 Generated with Claude Code

Closes #274

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 9, 2026 02:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Deploying website with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4a719c4
Status: ✅  Deploy successful!
Preview URL: https://bed5feb1.website-70y.pages.dev
Branch Preview URL: https://claude-cross-route-section-f.website-70y.pages.dev

View logs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yigitdot
yigitdot merged commit e539842 into main Oct 9, 2026
6 checks passed
@yigitdot
yigitdot deleted the claude/cross-route-section-focus-613d5a branch October 9, 2026 03:48
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.

fix(nav): move focus into the section after a cross-route nav tap

2 participants