Skip to content

Improve navigation for screen readers - #258

Open
david-crespo wants to merge 5 commits into
mainfrom
a11y-nav
Open

Improve navigation for screen readers#258
david-crespo wants to merge 5 commits into
mainfrom
a11y-nav

Conversation

@david-crespo

Copy link
Copy Markdown
Contributor

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-live region announces the new document.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 a tabIndex={-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.

@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
rfd-site Ready Ready Preview Aug 19, 2026 5:06pm

Request Review

@david-crespo
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.
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.

1 participant