manage focus, and use aria-live to announce navigation
See original GitHub issuehttps://github.com/sveltejs/sapper/issues/1083, basically.
There are two separate issues to consider in order to make client-side routing accessible:
1. Managing focus
In a server-rendered app, any time you navigate to a page, the <body>
is focused. In Sapper and SvelteKit apps, we blur the current activeElement
upon navigation, but this doesn’t actually reset the focus, which is what we need — it just removes it temporarily. As soon as you press the tab key (or shift-tab), focus moves to the element after (or before) whichever element was focused prior to the navigation, assuming it still exists in the DOM. This is not desirable.
Went down a bit of a rabbit hole trying to understand current recommendations:
- Marcy Sutton advises focusing a ‘skip back to navigation’ link (i.e. a skip nav link in reverse)
- Ryan Florence thinks the app developer should have control over where focus lands, but that it should be an element inside the innermost route that has changed
- Nick Colley and Daniel Nixon prefer finding some primary focus target, e.g. the
<h1>
- David Luhr suggests resetting focus to the top of the DOM
I’m sure there are other suggestions too. Each comes from an accessibility expert and has a solid argument, but ultimately they are mutually exclusive. Any would be better than the status quo though.
My inclination in the short term is to simply focus <body>
(i.e. David Luhr’s suggestion) — it’s easy to implement, and matches the behaviour of server-rendered apps. In the future perhaps we could investigate alternatives, particularly ones that put power in app developers’ hands rather than making one-size-fits-all decisions for them.
The short term solution ought to be fairly straightforward — we just change this…
…to this:
document.body.setAttribute('tabindex', '-1');
document.body.focus();
(Better still, only set the tabindex
once, when the router initialises.)
2. Announcing the page change
Per the recommendation in https://www.gatsbyjs.com/blog/2019-07-11-user-testing-accessible-client-routing/, we should add an ARIA live region that announces new pages. That looks something like this:
<div
id="svelte-announcer"
class="visually-hidden"
aria-live="assertive"
aria-atomic="true"
>Navigated to {title}</div>
This could live inside the auto-generated root.svelte
component.
It makes sense for title
to simply reflect document.title
, I think, rather than introducing some complicated way to customise it, since a page already needs an informational document title for SEO and accessibility purposes.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:11 (7 by maintainers)
Top GitHub Comments
For future consideration (haven’t really thought this through): Could this be a component that can be imported via
$app/a11y
or something? People could then use it in their layout like this:As an added suggestion: can we add an a11y tag to the issue tags? I’d be happy to help out where i can with anything a11y related. It’s easier to search for those issues if there’s a tag for it.