Improve navigation for screen readers - #258
Open
david-crespo wants to merge 5 commits into
Open
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
david-crespo
requested review from
benjaminleonard and
fakemonster
and removed request for
benjaminleonard
August 18, 2026 15:23
david-crespo
added a commit
to oxidecomputer/console
that referenced
this pull request
Aug 18, 2026
Similar deal to oxidecomputer/rfd-site#258 ## 🤖 summary A client-side nav doesn't tell a screen reader anything: React Router swaps the DOM in place and focus stays on the link that was clicked, which usually isn't in the document anymore. Next.js ships a route announcer for this; RR leaves it to the app. `useRouteAnnouncer` in `RootLayout` announces the crumbs deepest-first ("Instances, mock-project, Projects") through the react-aria live announcer we already use for toasts and field errors. Polite rather than assertive so it doesn't preempt the toast on flows that toast and then navigate. It also puts focus on the existing skip link target, but only when focus has fallen to `<body>` — if you clicked a sidebar link that's still sitting there, leave it alone. Use `preventScroll` to avoid conflicts with `useScrollRestoration` on back/forward. Side modal forms get their own routes, so the announcer has to know that a form opening on top of a page isn't a page change. It keys off `titleOnly` crumbs, which already mark exactly those routes (had to fix `ip-pool-edit` and `subnet-pool-edit`, which were using `makeCrumb`). Detecting the open dialog in the DOM instead seemed simpler, but it ran into races.
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.
Prompted by feedback that RFD links here don't work with screen readers. Next.js includes a route announcer for this; React Router leaves it to the app, so here we add one.
🤖 summary
After each client-side navigation, a visually hidden
aria-liveregion announces the newdocument.title. Nothing is announced on initial load, since a real page load announces itself. Same approach as Next.js's built-in announcer:https://github.com/vercel/next.js/blob/08b1916/packages/next/src/client/route-announcer.tsx
If navigation dropped focus on
<body>, focus moves to atabIndex={-1}content wrapper so the reading position starts at the top of the new page, like a real page load. Routes that set their own focus on mount (the index page autofocuses its filter input) win, because their effects run before the announcer's. Gatsby's user testing with screen reader users recommended this announce + move-focus combination (the Next.js implementation also cites it):https://www.gatsbyjs.com/blog/2019-07-11-user-testing-accessible-client-routing/
Second commit: a "Skip to content" link, visually hidden until keyboard-focused, targeting a
<main id="content">on the index and RFD pages. The index page's content wrapper is now a<main>landmark; the RFD page already had one.New e2e tests cover the announcement, both focus behaviors (moved to content after navigating to an RFD, left alone on the filter input after navigating home), and the skip link being first in tab order.