-
-
Notifications
You must be signed in to change notification settings - Fork 408
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
Accessible Routing #433
Accessible Routing #433
Conversation
RFC to help routes be accessible in Ember
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am so so so so so so so so excited for this
text/0000-a11y-routing.md
Outdated
|
||
## Alternatives | ||
1. We could try to implement something like Apple’s first responder pattern: https://developer.apple.com/documentation/uikit/uiresponder/1621113-becomefirstresponder | ||
2. Wait for a SPA solution to be implemented in assistive technology. I would note that this option seems less than ideal, since this specific issue has been reported to the different assistive technologies available, and has been an on-going issue for many years now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This, and I bet AT vendors will try and push the burden onto the browsers since AT operates at the OS level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
text/0000-a11y-routing.md
Outdated
|
||
## How we teach this | ||
|
||
For phase one, the guides should include information about skip links, since the application author will need to add those in themselves (just as they would for a static site). For phase two, we would explain that skip links are no longer needed because we have integrated that functionality in a more machine intelligent way. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For phase two, we would explain that skip links are no longer needed because we have integrated that functionality in a more machine intelligent way.
Is this true? I usually still keep nav skip links in my SPAs because it's helpful to move past the giant headers / nav bars web apps have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Robdel12 the idea for phase 2 is that we'd make it so AT would automatically implement skip link functionality so it was a cohesive experience- I think of it as an upgrade to skip links.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skip links are also readily utilized by keyboard-only users, so it would still be a concern outside of ATs. Additionally, as an author, I would expect a means of disabling the generated skip link in case where we want to implement something much more custom and robust.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, AT is a red herring as far as skip-links are concerned. The main group who rely on skip links are keyboard users without AT. Screen reader users have a variety of alternative navigation mechanisms available.
Skip-links will still be necessary, until browsers provide at least one built-in mechanism for reaching the main content (e.g. heading navigation, landmark navigation, or spatial navigation).
So we have a puzzle to figure out: why don't screen readers read out the new content or appropriately move focus when the user navigates to a new route within an Ember application. In researching the issue more thoroughly, we discovered that NVDA doesn't seem to have a way to handle the history API. (Which makes sense- screen readers were released before the history API was released.) | ||
|
||
One of the challenges is that screen reader technology is all closed-source except for [NVDA](https://www.nvaccess.org/), the open source screen reader for Windows (and typically used with Firefox). The source code for NVDA is available on [GitHub](https://github.com/nvaccess/nvda/) and is written in Python. It seems useful, at least initially, to try to solve for SPAs + NVDA, since both can be done in open source. (Related: [NVDA Issue #6606](https://github.com/nvaccess/nvda/issues/6606)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've long had a dream to take NVDA (since it's OSS) and turn make a "headless" screen reader version of it. Just like we have a headless Chrome we push around in tests, I'd like a headless AT to send commands to and asset the right thing has happened. Like "press enter on this link, now assert the spoken output of the AT is correct & it focuses the correct element in the DOM"
It's a neat idea, but I have no idea if its even possible.
This is probably an unpopular point of view, but I think there are other areas that should be prioritized over accessibility. Here are three examples:
Obviously one thing doesn't prohibit another, one can work on both screen reader support and localization functionality at the same time. But in a world of limited resources, I think efforts should be concentrated on other things. |
Co-Authored-By: MelSumner <[email protected]>
Thankfully, it's not a zero sum game! Since my primary area of expertise is accessibility, this is where I can propose specific improvements to Ember. If your area of expertise is in any of the things you suggested, please do feel free to write RFCs to address those issues- there's room here for everyone! 👍 |
@sandstrom I have to wholly disagree with that statement based on one thing: every single new ember app out there right now is broken to AT. There's nothing the user can do to try and work around or fix it. I know it's not ideal, but if you need to translate something to a different language, you have the ability to. AT users are simply stuck high and dry. If they're an advanced AT user they might be able to figure out what's going on in a SPA, but probably won't bother. I think every SPA framework / lib needs to take accessibility seriously & prioritize the work now that everyone is building SPAs. Until those framework / lib authors take it seriously, all SPAs will be broken to AT. And, as @MelSumner said, it's not zero sum. You can get the ball rolling on first class i18n support if you'd like. Maybe learn some lessons from Angular when they took on that effort. Lastly, this work might save everyone's companies from being sued for not being accessible. |
This is roughly a duplicate of #66. Having already gone through the process I believe that Ember should be a thin core with extension points for the functionality that we need for accessibility which would then be provided by an addon. If (and only if) we can't implement things in user space we should figure out how to enable those things by way of changing core. Those changes should still be minimal and not focused on accessibility so much as exposing the internal Ember.js state required to accomplish the goal. Benefits to being external:
My proposed path forward:
This will be far less painful for maintenance over a longer time scale, allow us to better serve AT users, and enable more people to contribute to the success of the effort. As a person who is incredibly familiar with this space (and the author of one of the referenced examples) I am strongly opposed to landing this RFC. |
|
||
One of the challenges is that screen reader technology is all closed-source except for [NVDA](https://www.nvaccess.org/), the open source screen reader for Windows (and typically used with Firefox). The source code for NVDA is available on [GitHub](https://github.com/nvaccess/nvda/) and is written in Python. It seems useful, at least initially, to try to solve for SPAs + NVDA, since both can be done in open source. (Related: [NVDA Issue #6606](https://github.com/nvaccess/nvda/issues/6606)) | ||
|
||
## Detailed design |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This design should be explicit. As it stands this is not a concrete proposal; it cannot be implemented using the RFC as a guide.
text/0000-a11y-routing.md
Outdated
2. When the user navigates to a new page in that website, the screen reader starts to read the content of that page | ||
3. (repeat) | ||
|
||
Perhaps we could achieve this by adding a function to set focus on the body element when a route transition has occurred. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't account for modals as well as non-body
-hosted applications.
text/0000-a11y-routing.md
Outdated
It's possible that enterprise Ember users have already implemented their own solution, since governments in many countries require this by law. An out of the box solution would need to be well-advertised and documented to avoid any potential conflicts for users. | ||
|
||
## Alternatives | ||
1. We could try to implement something like Apple’s first responder pattern: https://developer.apple.com/documentation/uikit/uiresponder/1621113-becomefirstresponder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is possible to implement this, but it's a really wonky experience on the Web. To do it right you would likely need inert
.
2. Wait for a SPA solution to be implemented in assistive technology. I would note that this option seems less than ideal, since this specific issue has been reported to the different assistive technologies available, and has been an on-going issue for many years now. | ||
|
||
## Unresolved Questions | ||
- how would we handle loading states? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ember-a11y
works with loading states out of the box with exactly one caveat: when the loading state is below the pivot route.
text/0000-a11y-routing.md
Outdated
|
||
## Unresolved Questions | ||
- how would we handle loading states? | ||
- how does/could/would this fit in with the router helpers work that Chad did? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is likely just an implementation detail and doesn't really need to be addressed in this RFC. This would clean up maybe a dozen lines of code in ember-a11y
except that I want for ember-a11y
to remain backwards compatible, so it won't adopt those until the old APIs are removed.
Given that, ideally any solution would not leverage those helpers unless it had to.
updated related references with a more complete list of accessibility APIs.
Lots of updates, technical solutions included.
updated original date
@nathanhammond I believe I have attempted to address your concerns with the updates I have made to this RFC. |
Update navigation message
@MelSumner thanks for taking the time doing the investigation and providing paths forward. I think I would prefer that we do something with What I mean by making outlets smarter is that RFC#93 talked about exposing the I'm a bit cautious about exposing ember specific IDs that we use to bring focus to elements. The reason is that those IDs effectively become public API, but public API with no transition path if we decided to change the name or remove it. We have seen this with Overall though I think this is heading in the right direction. |
typo fix
text/0000-a11y-routing.md
Outdated
|
||
Perhaps we could achieve this by adding a function to set focus on the body element when a route transition has occurred. | ||
- A component is created that provides a screen-reader only message, informing the user that "Page transition is complete. You may now navigate as you wish." The text of this component could be internationalized. | ||
- Focus is reset to the top left of the page |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rest of your description doesn't specify position by location, but instead by DOM hierarchy. This one slipped through.
text/0000-a11y-routing.md
Outdated
|
||
Perhaps we could achieve this by adding a function to set focus on the body element when a route transition has occurred. | ||
- A component is created that provides a screen-reader only message, informing the user that "Page transition is complete. You may now navigate as you wish." The text of this component could be internationalized. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing that I've experimented with is an off screen "page change" log inside of an aria-live
component. Every element in that log would provide information about what is happening, and the ability to jump to the section flagged by it. This has numerous problems:
- It's a new paradigm.
- It can't be done in a fully-automated way (the description for each page change area must be manually specified).
- It doesn't support all AT use cases.
text/0000-a11y-routing.md
Outdated
|
||
### Phase Two - Intelligent content focus | ||
```hbs | ||
<div tabindex="-1" class="ember-sr-only" id="nav-message"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- How should this handle partial renders?
/foo/loading
=>/foo/bar/loading
=>/foo/bar/baz
- How do you prevent this from being read at other times?
- If you remove it, how do you prevent this from disappearing before AT has a chance to communicate it to the user?
- If left in the page, stale information in this component could in theory be read during a transition to something new (especially if a large portion of content gets popped out of the page). This could make it seem as if you have landed on the previous state while the next state is still transitioning in.
- How do you provide a human-understandable page name?
- If it requires configuration, how do you ensure that users properly configure it?
- Ember doesn't support localization out of the box, how do you localize the message?
text/0000-a11y-routing.md
Outdated
this.router.on('routeDidChange', () => { | ||
// we need to put this inside of something async so we can make sure it really happens **after everything else** | ||
schedule('afterRender', this, function() { | ||
document.body.querySelector('#ember-a11y-refocus-nav-message').focus(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Can we make this use a reference from inside of Ember?
- How does this support Ember being used in a place where it is not in control of the entire application? The idea here seems to be to focus the "first" thing, similar to default AT behavior. However, if
rootElement
is set this doesn't play quite as nicely.
text/0000-a11y-routing.md
Outdated
The addon would attempt to provide a sensible resolution for all involved: | ||
- the performance gains from `pushState` remain in place | ||
- users with assistive technology are informed that a page transition has occurred | ||
- the focus is reset to the message itself, which also resets the focus of the page (as is desired for the screen-reader user) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This element should probably provide a jump link back to the section of the page that changed.
text/0000-a11y-routing.md
Outdated
|
||
This allows the user with assistive technology to skip directly to the main content and is useful for not needing to repeatedly navigate through a navbar, for example. | ||
```hbs | ||
{{application-outlet}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I much prefer having a single outlet
type. @ef4 and I have created a few over time:
liquid-outlet
... fromliquid-fire
.focusing-outlet
... fromember-a11y
which lifts the outlet stuff fromliquid-fire
.animated-outlet
... in Apple Music which is a few dozen lines of code for hardcoded CSS fade transitions.
The problem with this strategy is that none of those solutions by default play nicely together: they're fighting over the same extension point. To wire them together you have to make one dependent upon another and wrap things. You end up (approximately) with:
{{! custom-outlet.hbs }}
{{#animated-outlet}}
{{#focusing-outlet}}
{{outlet}}
{{/focusing-outlet}}
{{/animated-outlet}}
I feel like the API for the outlet
itself should be extensible instead of needing to wrap it. I'm thinking something like element modifiers. It's a little hard to figure out what it should do since and outlet
doesn't exactly have the same lifecycle hooks that a component has.
text/0000-a11y-routing.md
Outdated
|
||
In this way, if an application author did not want to use the `{{application-outlet}}`, for whatever reason (maybe they are nesting apps, or maybe they already have a focus solution) they could remove the `application-` and be left with a classic outlet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like the behavior in this scenario should be opt out, not opt in. Start with outlet
that does the accessible thing, opting out requires changing things in the application. Presumably the person who knows how to opt out will also be more familiar with what they're doing.
This still seems very important. I'm curious whether the routing spikes around Polaris would address this. |
👋 As such, I'm going to close this RFC. For the time being, I encourage developers to check out ember-a11y-refocus, an addon that provides accessible routing for Ember apps. |
Rendered
Updated on Jan 12, 2020 to reflect conversations during framework f2f weekend.