Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

a11y issues: page nav doesn't trigger assistive tech #5581

Open
jlengstorf opened this issue May 28, 2018 · 32 comments

Comments

@jlengstorf
Copy link
Member

@jlengstorf jlengstorf commented May 28, 2018

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.

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

  1. Visit https://gatsbyjs.org
  2. Start VoiceOver (command + F5)
  3. Use the tab key to focus on a link in the gatsbyjs.org navigation
  4. "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
@nickcolley

This comment has been minimized.

Copy link

@nickcolley nickcolley commented May 28, 2018

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

@ryanditjia

This comment has been minimized.

Copy link
Contributor

@ryanditjia ryanditjia commented May 29, 2018

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:
safari reader mode

@KyleAMathews

This comment has been minimized.

Copy link
Contributor

@KyleAMathews KyleAMathews commented May 29, 2018

@jlengstorf

This comment has been minimized.

Copy link
Member Author

@jlengstorf jlengstorf commented May 29, 2018

In #5533, @jhackett1 linked to this article, which offers up an idea for React Router: https://medium.com/@robdel12/single-page-apps-routers-are-broken-255daa310cf

I'd also be curious to hear if any SPA best practices have already been vetted by the a11y pros out there: if you've got time, @marcysutton @LJWatson, I'd love to get your perspective.

@LJWatson

This comment has been minimized.

Copy link

@LJWatson LJWatson commented May 29, 2018

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.

jlengstorf added a commit to jlengstorf/gatsby that referenced this issue May 29, 2018
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 to @nickcolley for initially pointing this
out, and to @LJWatson for jumping in with a fix!

fix gatsbyjs#5581
@jlengstorf

This comment has been minimized.

Copy link
Member Author

@jlengstorf jlengstorf commented May 29, 2018

Thanks so much, @LJWatson!

Since Gatsby won't be able to control the <title> or <h1> parts of the app, we'll have to focus on what we can:

  1. Let's add a11y docs to talk about best practices (#5592)
  2. We can (hopefully) patch gatsby-link to focus on the Gatsby container div after page transitions

I just ran a local test that appears to confirm page navigation works as expected after adding a step that focuses the ___gatsby wrapper after navigation. Will open a PR shortly.

@LJWatson

This comment has been minimized.

Copy link

@LJWatson LJWatson commented May 29, 2018

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.

@jlengstorf

This comment has been minimized.

Copy link
Member Author

@jlengstorf jlengstorf commented May 29, 2018

Thanks @LJWatson! I was able to confirm with VoiceOver that the fix I'm proposing in #5593 causes the page title to be announced. If you don't mind, once it's merged I'd love to send you a link for confirmation that it's working as you'd expect.

@nickcolley

This comment has been minimized.

Copy link

@nickcolley nickcolley commented May 29, 2018

@LJWatson I had wondered what users do when faced with no update, is it likely any users would consider this a barrier? Would you say your knowledge of the web helps you understand what has happened, or is this something reasonable to expect most users to figure out?

@LJWatson

This comment has been minimized.

Copy link

@LJWatson LJWatson commented May 30, 2018

@jlengstorf No problem.

@nickcolley
My hunch is that most screen reader users wil think the link (or whatever they activated) is broken, because to all intents and purposes nothing will have happened. Focus will remain on the link and/or be cut loose if the link isn't present in the new view and the page title won't have been announced. Unless they choose to go exploring, people are likely to be oblivious at worst and very confused at best I think. Understanding development certainly helps me figure these things out for sure though!

@KyleAMathews

This comment has been minimized.

Copy link
Contributor

@KyleAMathews KyleAMathews commented Sep 8, 2018

Fixed with @reach/router in v2

@marcysutton

This comment has been minimized.

Copy link
Member

@marcysutton marcysutton commented Feb 26, 2019

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 the aria-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? :)

@marcysutton marcysutton reopened this Feb 26, 2019
@marcysutton

This comment has been minimized.

Copy link
Member

@marcysutton marcysutton commented Mar 14, 2019

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/

@nickcolley

This comment has been minimized.

Copy link

@nickcolley nickcolley commented Mar 14, 2019

@marcysutton thanks Marcy, let me know if you need any help!

@gatsbot gatsbot bot added the stale? label Apr 4, 2019
@gatsbot

This comment has been minimized.

Copy link

@gatsbot gatsbot bot commented Apr 4, 2019

Hiya!

This issue has gone quiet. Spooky quiet. 👻

We get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here.

If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!

Thanks for being a part of the Gatsby community! 💪💜

@marcysutton marcysutton removed the stale? label Apr 4, 2019
@marcysutton

This comment has been minimized.

Copy link
Member

@marcysutton marcysutton commented Apr 4, 2019

Not stale, just juggling priorities. This is still in progress

@jlengstorf jlengstorf added the not stale label Apr 5, 2019
@mattcdowning

This comment has been minimized.

Copy link
Member

@mattcdowning mattcdowning commented May 26, 2019

@marcysutton Can confirm not hearing page changes announced on MacOS with VoiceOver in Chrome/Safari.

@kencoxdesign

This comment has been minimized.

Copy link

@kencoxdesign kencoxdesign commented Jun 30, 2019

@marcysutton Any update on this? Just finished my first Gatsby site and really don't feel comfortable releasing it with such a huge accessibility hole. Safari/Voiceover is not managing page change focus at all for Links

@marcysutton

This comment has been minimized.

Copy link
Member

@marcysutton marcysutton commented Jul 1, 2019

@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

This comment has been minimized.

Copy link
Member

@marcysutton marcysutton commented Jul 3, 2019

This issue can be closed with the merge of #13197! But this effort is not over yet–stay tuned for more information about what I uncovered with user testing and how to make routing even more accessible.

@marcysutton marcysutton closed this Jul 3, 2019
@mattcdowning

This comment has been minimized.

Copy link
Member

@mattcdowning mattcdowning commented Jul 3, 2019

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

@marcysutton

This comment has been minimized.

Copy link
Member

@marcysutton marcysutton commented Jul 3, 2019

@mattcdowning bummer! I can reproduce that now with an upgraded test project, too. I tested this more fully before to support assistive technology beyond Chrome and Voiceover, and I'm not sure where it went wrong. I'll reopen the issue for now since it still isn't addressed, and needs to be. Thanks for the heads up.

@marcysutton marcysutton reopened this Jul 3, 2019
@marcysutton

This comment has been minimized.

Copy link
Member

@marcysutton marcysutton commented Jul 8, 2019

To follow up on this: we are investigating and working on some improvements. Stay tuned.

@marcysutton

This comment has been minimized.

Copy link
Member

@marcysutton marcysutton commented Jul 9, 2019

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" and tabindex="-1", but Voiceover needs an aria-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 with role="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). #15579

@kishba

This comment has been minimized.

Copy link

@kishba kishba commented Jul 10, 2019

@marcysutton I loved that blog post. It really has me thinking about how a button could be added programmatically to receive focus. I wonder if it would be distracting or too repetitive if the button's label was automatically informed by the first heading within the new content (if one existed)?

@marcysutton

This comment has been minimized.

Copy link
Member

@marcysutton marcysutton commented Jul 10, 2019

@kishba that's sort-of what I was thinking–using aria-labelledby pointed to a nearby heading to label it. But we'll need some more testing on that part. Once I've got a solution working I'll circulate it to get some input from users.

@kencoxdesign

This comment has been minimized.

Copy link

@kencoxdesign kencoxdesign commented Jul 10, 2019

@marcysutton Great blog post, and I'm glad your testing also caught the aria-current issue. I was already looking into what it would take to add that myself. Would need to add aria-current under the same conditions that the existing css class is added to visually highlight the currently active link.

@m-allanson m-allanson added this to To do in Next sprint via automation Jul 29, 2019
@m-allanson m-allanson added this to To prioritize in OSS Roadmap via automation Aug 1, 2019
@PigeardSylvain

This comment has been minimized.

Copy link

@PigeardSylvain PigeardSylvain commented Nov 18, 2019

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.

@nickcolley

This comment has been minimized.

Copy link

@nickcolley nickcolley commented Nov 18, 2019

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:

  • when a page has loaded
  • a summary of what can be interacted with (landmarks, headings etc)
  • if a page is taking a while to load it will indicate the progress

It also will:

  • set focus to the document
  • allow for ‘backwards-forwards’ caching of pages
  • allow for streaming the page to the browser before it’s fully rendered

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:

"There’s a chance I believe client side routing on the web is usually not preferred. Which is ironic.
Might be best for screens where the majority of the UI persists, which is the edge case.
Browsers handle page transitions really well.
Still working through my thoughts 🤔"

Ryan Florence tweet

@PigeardSylvain

This comment has been minimized.

Copy link

@PigeardSylvain PigeardSylvain commented Nov 18, 2019

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.

@eps1lon

This comment has been minimized.

Copy link

@eps1lon eps1lon commented Nov 18, 2019

This way users can get really quick pages while getting the benefits of full page refreshes that I have listed above.

To be honest I personally would prefer standard navigation anyway. Client side routing without a signifier (vanilla routing has this by adding the loading spinner in the tab, client side routing needs transitions) can be confusing ("did something actually happen?").

Wasn't there some research that suggested that some actions can be too fast i.e. users should have to wait a minimal amount of time otherwise they don't recognize that an action happened?

@m-allanson

This comment has been minimized.

Copy link
Member

@m-allanson m-allanson commented Nov 21, 2019

See #19290 for our current research into accessible routing in Gatsby.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
OSS Roadmap
  
To prioritize
Next sprint
  
To do
You can’t perform that action at this time.