-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
sveltejs/sapper#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...
kit/packages/kit/src/runtime/internal/router/index.js
Lines 180 to 182 in e09b6be
if (document.activeElement instanceof HTMLElement) { | |
document.activeElement.blur(); | |
} |
...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.