gatsby: a11y issues: page nav doesn't trigger assistive tech
Description
Right now, using assistive tech to navigate between pages works, but doesn’t announce that the user has reached the next page. This is confusing, so we should find a way to mimic standard navigation behavior when using gatsby-link
for navigation.
Thanks to @nickcolley for pointing this out! Here were a few suggestions he came up with:
- Focus the page after routing
- Use ARIA live regions
- Use a Service Worker to simulate server routing, but with less lag
- This seems like the least desirable options, since it would require a full page re-render for all navigation; if someone wants this behavior, they could omit
gatsby-link
to get it.
- This seems like the least desirable options, since it would require a full page re-render for all navigation; if someone wants this behavior, they could omit
Nick has done some research and mentioned he’ll follow up on this issue with his findings.
This may be an upstream fix for React Router, but let’s make sure it’s at least working with a11y in mind in Gatsby.
Steps to reproduce
- Visit https://gatsbyjs.org
- Start VoiceOver (
command
+F5
) - Use the tab key to focus on a link in the gatsbyjs.org navigation
- “Click” the link using
control
+option
+space
It will announce that it is following the link, but will not announce the next page.
Expected result
After navigation, the new page should be announced. For a working example, visit https://smashingmagazine.com, activate VoiceOver, and navigate to the Articles page.
Actual result
After navigation, nothing happens.
Environment
- Gatsby version (
npm list gatsby
): V1 & V2 - Operating System: OS X
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 11
- Comments: 36 (20 by maintainers)
Commits related to this issue
- fix: focus on the root div after navigation This fix applies focus to the main app div after page navigation, which triggers screen readers and other assistive tech to announce the new page. Thanks t... — committed to jlengstorf/gatsby by jlengstorf 6 years ago
- add links to issues for upcoming features also add descriptive text for issue #5581 — committed to gatsbyjs/gatsby by madalynrose 4 years ago
- Blog 2020-02-05 Accessible Routing (#21018) * WIP draft * rough draft * Apply stylistic suggestions from code review Co-Authored-By: LB <barth.laurie@gmail.com> * add links to issues for ... — committed to gatsbyjs/gatsby by madalynrose 4 years ago
Update:
Our first iteration of accessible client-side routing has been released! Because this issue goes so far back and it’s taken us a while to create a deliverable, I’d like to spend some time highlighting the work that went into this improvement, roadblocks that came up during development, and our hopes for future iterations.
With #19290, Gatsby announces routing updates using a live region embedded within our Router. This behavior is confirmed for the following technologies:
Work so far
The largest effort in implementing this improvement was in the research phase. @marcysutton has written about her efforts in the comments above, but I’ll summarize here as well. Gatsby worked closely with Fable Labs in July 2019 to gather feedback from users with disabilities on a set of prototypes with navigation techniques for JavaScript web apps. You can read more in this blog post, which was updated just recently (January 2020). Based on the July 2019 testing and additional testing done at the Inclusive Design 24 Virtual Conference in October 2019, it was determined that we should:
I was hired in October 2019 to pick up these efforts. Additional constraints applied to this project, as implementing these changes in Gatsby core is different than implementing them in “userland.” This was evidenced by differences in behavior between Marcy’s prototypes that handled routing themselves and testing sites using Gatsby’s core routing.
Once the MVP was created, extensive testing was conducted for as many combinations of free tooling as possible.
Next steps
It’s clear that our work is not done here. I want to keep iterating and keep improvin go we can raise the baseline accessibility that each Gatsby site ships with by default.
In order for this issue to not continually expand, we ask that you create new issues for scoped chunks of work (e.g. If you feel especially strongly about Gatsby having an option to turn off client side routing, creating a new issue for that would be fantastic and would help us gauge impact). I’m already creating issues for:
Thank you all for caring so much about this and helping us make the web better for everyone, one incremental improvement at a time. ❤️ 🚀
Client side routers re-implement navigation between pages (views) in the browser. Assistive technologies rely on this browser behaviour to announce important information such as:
It also will:
I think that Gatsby should take a step back and review the user needs that led them to making this decision in the first place.
For example, one of the drivers for client side routing is for fast page transitions, this can be achieved with better caching via Service Workers.
This way users can get really quick pages while getting the benefits of full page refreshes that I have listed above.
To quote the author of the client side routing library used in this project:
– Ryan Florence tweet
I agree that the speed of loading is a positive point to work on. However, there are accessibility laws in Europe (and in other countries) that cannot be satisfied by the current functioning and that prevent us from using the product.
@madalynrose awesome work ❤️ great to see this shipped 👏
Alright folks, here’s what I have to report:
This issue isn’t actually Gatsby-specific: it stems from @reach/router not providing consistent announcements when a wrapper DIV is focused (Test case in a Codesandbox). The wrapper element currently has
role="group"
andtabindex="-1"
, but Voiceover needs anaria-label
to expose an accessible name and NVDA wants a different role:"status"
and"application"
are two working candidates right now, but neither of these solutions are particularly great. I could possibly live withrole="status"
to get announcements working in NVDA, but there are likely side-effects in Talkback and we’d want to test it thoroughly.role="application"
is an absolute no-go as it would sabotage the screen reader experience for all Gatsby sites and isn’t worth it in my opinion.Focusing on a heading in the newly changed content area would be fantastic but it’s in the realm of “userland” where we can’t control what goes in there. Hence the idea to focus on a wrapper element. It’s worth adding that this wrapper focus approach is intended to be a fallback method as more component-driven approach is introduced that provides better functionality and access to a range of users with disabilities. For more on that approach, my blog post is up in PR form now (and open to comments). https://github.com/gatsbyjs/gatsby/pull/15579
I believe this issue is related to why Safari reader view—both macOS and iOS—gets stuck to the blog post you were previously on.
Sometimes the reader button doesn’t show up at all, for example when going from /blog/ to /blog/post/, likely because the browser doesn’t fully know the page has changed.
Gif showing the bug:
Update: I’ll do some more testing of my own once the CSUN Assistive Tech conference is over, but I’m also doing prototype testing with people with disabilities to try and establish a best practice: https://marcysutton.com/prototype-testing-accessible-clientside-routing/
I think we need to reopen this, as I’m not hearing page changes announced on my website or gatsbyjs.org now that @reach/router is in place. The resetting of focus to the top of the page works fine, but I don’t hear anything announced when I navigate through top-level pages in Voiceover with Safari and Chrome, IE11 and JAWS, or Edge and NVDA. I can open an issue with Reach Router as well, but rather than open a new Gatsby issue I thought I’d keep the discussion here. It seems like we need to do more to communicate to screen reader users here (and hence tweets like this one).
One odd thing with NVDA or JAWS on gatsbyjs.org is when I navigate to
/docs
, I hear probably thearia-current
value for link for some reason, like it’s rendering before focus is reset to the top of the page. But it only happens on/docs
.Can someone test a bit with one of the above screen readers as well to make sure I’m not imagining this regression? 😃
Thanks so much for this work, this is groundbreaking work to make SPAs accessible in general. I can’t believe it’s only happening just now. We’re building a router for Svelte and closely watching this work. If we can contribute back we will.
See https://github.com/gatsbyjs/gatsby/pull/19290 for our current research into accessible routing in Gatsby.
Is it possible to disabled frontend router to keep a standard way of loading pages ? I think it’s the safest way for everyone to access the content.
@marcysutton I’m still not hearing page change announcements with Gatsby 2.13.2 using Voiceover in Safari on MacOS in @pieh’s and my gatsby-starter-default example.
@kencoxdesign yes! I’ve been conducting user testing on accessible client-side routing for the past month, determining the best way forward to serve people with disabilities beyond screen reader usage only. The latest code development is that we have #13197 to merge, which will make an incremental improvement to at least announce page changes in assistive technology–but we want to improve this even further to handle focus and announcements in a way that better supports keyboard users and screen magnification. So stay tuned for more on that front.
@marcysutton Can confirm not hearing page changes announced on MacOS with VoiceOver in Chrome/Safari.
Fixed with @reach/router in v2
One thing I should have mentioned, is that when a conventional page is loaded and keyboard focus is returned to the top of the viewport, screen readers will automatically announce the
<title>
element and then start to read the content of the page. When faking it for an SPA, the content of the page may not be automatically read by the screen reader - but this is ok, because focus is in the expected place and the user can explore the content for themselves from this logical starting point. Worth mentioning in case it comes up when you test your fix.Thanks @jlengstorf for the ping.
The trick is to mimic the default behaviour of the browser (and therefore the AT) as closely as possible, and to adopt good practices for authoring content to support this behaviour.
When a conventional page loads in the browser, keyboard focus is returned to the top of the viewport. This enables keyboard users (whether they use an AT or not), to explore the page from the top using whatever techniques they’re used to. It also causes screen readers to announce the content of the
<title>
element - the first indication a blind person has that they’ve reached their intended destination.It is good practice to give each page in a website/webapp a unique title that concisely describes the primary purpose of the content. It’s also useful if the page title is reflected in an
<h1>
heading at the start of the main content area.To make this happen in an SPA you need to fake it: use JS to take keyboard focus to the
<body>
element (or nearest sensible container) when the view is replaced/updated, replace the<title>
element with something unique to the current SPA view, and update the<h1>
(assuming it’s there) at the start of the main content area accordingly.Thanks so much for putting this together!
I’m going to put together the research on the possible options we can go down (that you mentioned).
This will include how they perform when using the most commonly used assistive technologies.
Update: As far as I know, this is a fairly well documented problem but no projects attempt to fix it by default.
This means it requires pre-existing knowledge of the issue, so the majority of projects are broken.
Ember.js community have a great add-on: https://github.com/ember-a11y/ember-a11y.
While this is not very scientific, if we check how often this is downloaded compared to the
ember-cli
package, you can see how many people actually opt-in to this.127 downloads vs 85k downloads http://www.npmtrends.com/ember-a11y-vs-ember-cli