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

Radio controls component has accessibility issues #218

Closed
StommePoes opened this issue Sep 24, 2020 · 9 comments
Closed

Radio controls component has accessibility issues #218

StommePoes opened this issue Sep 24, 2020 · 9 comments
Assignees
Labels
a11y Anything relating to accessibility.

Comments

@StommePoes
Copy link

StommePoes commented Sep 24, 2020

The radio controls component has several accessibility issues. I thought it was easier to lump them all together for the single component rather than separate issues for each.

I threw together a test page https://stommepoes.nl/work/shoelace/shoelace_demo.html and, on Windows only, ran 2 screen readers (NVDA latest, JAWS 2019) on 3 browsers (Edge latest, Chrome latest, Firefox latest... as of 23 September 2020). I don't own any iThings or I would have run VoiceOver on Safari (this is the combination for MacOS that is preferred for testing) as well.

  • The docs show a single radio control as an example of the component. While there is some logical sense in showing this, developers using this library may not realise radios cannot be used singley.
  • The grouped radios example simply has several individual radio controls placed together, sharing a name attribute. However, radio controls must be within a radiogroup element (such as a <fieldset> which has this role natively, or an element with a manually-added radiogroup ARIA role).
  • Since there is not an example of radio controls within a radiogroup, it is unclear (if impossible?) to assign a name to the radiogroup. For example, in a form which asks a question where each radio's label exposes a possible option, the question is the "name" of the radiogroup. In native HTML, this is a <legend>, which must be the first child of a <fieldset>. With non-native elements, this might be an element with a unique id, and the element with the radiogroup role is named by having aria-labelledby pointing to that id.
  • For reasons I could not determine, in Firefox each radio control was announced as being "1 of 1" (instead of "1 of [total]"). In Chrome, there was no announcement of placement either way. I suspect adding a radiogroup would fix this but not 100% certain.
  • In Firefox only, each of the radio controls could be Tabbed to with the Tab key, instead of the entire radio group acting as a single Tab stop.
    ** A note about Firefox: I tested a native set of ungrouped and a native set of grouped radio controls, to see if it was the lack of grouping causing the Tab issue. In Firefox, the grouping (or lack thereof) seems to have no direct effect. Firefox seems to allow Tabbing to each radio so long as none of them are selected. Once a selection is made, the typical native keyboard behaviour occurs. Yet in the tested Shoelace version, while there appears to always be at least one pre-selected radio, even manually mouse-clicking or using Enter to select a particular control does not bring the required "1 Tab stop per radio group" behaviour on the Shoelace radios.

It is expected that

  • radio controls are only ever shown in radiogroups, even in documentation.
  • radiogroups can (must) be named.
  • announce their position-in-set ("one of four, two of four," etc)
  • are treated as a single Tab stop, with the possible exception of in Firefox when there is no default selection.

Desktop:

  • OS: Windows 10
  • Browsers: Edge latest, Chrome latest, Firefox latest (as of 23 September 2020)
  • Screen readers: NVDA latest, JAWS 2019

Note: Bug #215 is also applicable

@StommePoes StommePoes added the bug Things that aren't working right in the library. label Sep 24, 2020
@claviska claviska added the a11y Anything relating to accessibility. label Sep 24, 2020
@claviska claviska added this to the v2.0.0 (stable) milestone Mar 21, 2021
@claviska claviska removed the bug Things that aren't working right in the library. label Mar 21, 2021
@claviska
Copy link
Member

claviska commented Apr 9, 2021

This is mostly solved by b4fc01e. The only outstanding issue I'm aware of is that VoiceOver is reading "1 of 1" instead of "1 of (total)". This Chromium bug is related, but I'm still exploring ways around it in the meantime.

@StommePoes
Copy link
Author

From the ticket:

It would seem that the role=radiogroup and role=radio does not cross the shadow boundary, which kind of makes sense.

Yeah, looks like they need to figure this out for shadow-DOM, however I'm not sure if they can fix it? It's a known issue in the accessibility community in general that we can't do associations across shadow boundaries. For example, a component such as a Listbox using aria-activedescendent where the container exposes the id of the currently-focused item cannot "see" which currently-focused item it is inside a shadow DOM.

@tobias-edwards
Copy link

Will this issue remain open until the Accessibility Object Model is ready? Other Web Components libraries are resorting to rendering form elements in the light DOM to work around these issues.

@claviska
Copy link
Member

I recall seeing some activity in a Chrome bug that implied a fix was being worked on. I don’t think we’ll need to wait for AOM to land. I’ll investigate more when I get back.

@claviska
Copy link
Member

This doesn't appear fixed in Canary. It's still announcing "1 of 1" whereas Safari announces "1 of 3" as expected. Still seems like a Chrome bug to me, and I'm reluctant to refactor everything or use light DOM hacks when most VoiceOver users seem to prefer Safari.

@tobias-edwards
Copy link

tobias-edwards commented May 24, 2022

Also, VoiceOver in Firefox will announce twice as many radio buttons. I think this is because the shadow DOM <input type="radio" /> has an implicit role="radio", and <sl-radio> (in the light DOM) also has role="radio". Somehow accessible roles in the shadow DOM are breaching the shadow boundary and then being picked up by VoiceOver? I can see nested role="radiobutton" in Firefox's generated accessibility tree for https://shoelace.style/components/radio.

For now, I think there's a couple of solutions:
a) As you said, render the elements in the light DOM and rely on the native HTML semantics for accessibility
b) Use non-semantic HTML elements inside the shadow DOM like <div> or <span> and fully rely on attaching the accessible attributes to <sl-radio>. I think this also helps with the mental model we should have when building accessiblity for Web Components.

(b) is taken from Adobe's Spectrum Web Components.

I have found some issues with (b). For example, if you want to support aria-describedby in your radio buttons by offering 'hints' (descriptive text alongside the radio button label), then the aria-describedby attribute must be placed within the same DOM type as the thing that refers to it. This causes an issue because

The aria-describedby attribute can be used with semantic HTML elements and with elements that have an ARIA role.
(https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby)

And so you wouldn't be able to do something like:

<sl-radio role="radio">
  <!-- #shadow-root -->
  <span aria-describedby="hint" id="radio"></span>
  <span id="label">Label</span>
  <span id="hint">Hint</span>
  <!-- -->
</sl-radio>

because <span> doesn't have a role, and you wouldn't be able to attach the aria-describedby attribute to <sl-radio> because id references don't pierce the shadow boundary.

Perhaps you could work around this by only rendering the 'hint' in the light DOM, but it feels messy:

<sl-radio aria-describedby="hint" role="radio">
  <!-- #shadow-root -->
   <span id="radio"></span>
  <span id="label">Label</span>
  <!-- -->
  <span id="hint">Hint</span>
</sl-radio>

So I am leaning towards rendering in the light DOM until the Accessibility Object Model is ready.

@claviska
Copy link
Member

Interestingly, Firefox + VoiceOver doesn't read the labels but it does announce twice as many radios. This also seems like a browser bug to me, but adding aria-hidden="true" to the internal radio input seems to alleviate it without any ill effects on Chrome and Safari.

I'll try to look into this more soon. Thanks so much for the detailed info!

@felipeferreirasilva
Copy link

I'm having the same problem using and , on chrome and firefox, voice over always reads 1 of 1, but in Safari it works well.

@claviska
Copy link
Member

claviska commented Aug 4, 2022

Radios have been refactored in #833 and #847 to solve all known remaining issues. 🎉

@claviska claviska closed this as completed Aug 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a11y Anything relating to accessibility.
Projects
None yet
Development

No branches or pull requests

4 participants