question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Accessibility: Radio/Checkbox groups have semantically improper label associations

See original GitHub issue

Describe the bug

The output for a radio/checkbox group comes out in this structure:

div.formulate-input(data-classification="group" data-type="checkbox")
  div.formulate-input-wrapper
    label.formulate-input-label.formulate-input-label--before(for="formulate--guide-inputs-types-box--2") This is a label for all the options

    div.formulate-input-element.formulate-input-element--group.formulate-input-group

      div.formulate-input-group-item.formulate-input(data-classification="box" data-type="checkbox")
        div.formulate-input-wrapper
          div.formulate-input-element.formulate-input-element--checkbox(data-type="checkbox")
            input#formulate--guide-inputs-types-box--2_first(type="checkbox" value="first")
            label.formulate-input-element-decorator(for="formulate--guide-inputs-types-box--2_first")
          label.formulate-input-label.formulate-input-label--after(for="formulate--guide-inputs-types-box--2_first") First

      div.formulate-input-group-item.formulate-input(data-classification="box" data-type="checkbox")
        div.formulate-input-wrapper
          div.formulate-input-element.formulate-input-element--checkbox(data-type="checkbox")
            input#formulate--guide-inputs-types-box--2_second(type="checkbox" value="second")
            label.formulate-input-element-decorator(for="formulate--guide-inputs-types-box--2_second")
          label.formulate-input-label.formulate-input-label--after(for="formulate--guide-inputs-types-box--2_second") Second

There are a couple of issues with this output from an accessibility perspective.

  1. The first label is not associated with anything. A screen reader user that lands on one of these checkboxes/radios will not necessarily hear the context of the entire question. This is especially problematic for forms that might ask several questions with the same answers (such as “agree, neutral, disagree”). There are two options to properly ensure that the context of the question is linked to the radios/checkboxes.
    • WAI-ARIA: Add :aria-labelledby="uniqueId" and role="group" attributes to either formulate-input or formulate-input-wrapper. Add the uniqueId as the id of the main label. This is probably the path of least resistance.
      div.formulate-input(role="group" aria-labelledby="unique-id" data-classification="group" data-type="checkbox")
        div.formulate-input-wrapper
          label.formulate-input-label.formulate-input-label--before(id="unique-id") This is a label for all the options
      
    • Semantic HTML: Use fieldset and legend. the div.formulate-input-wrapper would then have to be fieldset.formulate-input-wrapper and the first label would become the legend. This comes with its own styling headaches.
      div.formulate-input(data-classification="group" data-type="checkbox")
        fieldset.formulate-input-wrapper
          legend.formulate-input-label.formulate-input-label--before(for="formulate--guide-inputs-types-box--2") This is a label for all the options
      
    Both of these approaches work in VoiceOver in Chrome. Uncertain which is overall better supported and since the screen reader landscape is so glitchy, I doubt either would be 100%, but it’s important to implement one of them.
  2. The formulate-input-element-decorator labels are linked to the input with for. I recognize that this is being done to ensure that clicking on the decorator label toggles the associated input (and that the decorator label is going to be used for styling purposes), but it poses a larger issue for screen readers because most screen readers don’t support multiple labels. It is likely then, that a screen reader would choose the first label to associate with the input, which in this case would provide no valuable information to the user. The simplest solution to this would likely be to place the decorator span or div inside the label that contains the radio button/checkbox’s label content. (These days it’s becoming more and more possible to use pseudo-elements to style these elements, so the decorators may not even be strictly necessary.)
    div.formulate-input-group-item.formulate-input(data-classification="box" data-type="checkbox")
      div.formulate-input-wrapper
        div.formulate-input-element.formulate-input-element--checkbox(data-type="checkbox")
          input#formulate--guide-inputs-types-box--2_first(type="checkbox" value="first")
        label.formulate-input-label.formulate-input-label--after(for="formulate--guide-inputs-types-box--2_first")
          span.formulate-input-element-decorator
          | First
    

To Reproduce

Issue 1: Unassociated legend

  1. Go to Box | Vue Formulate
  2. Turn on a screen reader
  3. Hit tab until landing on a checkbox or radio in a group
  4. Note that the context / main label / legend isn’t read

Issue 2: Multiple associated labels for one input

  1. Install aXe accessibility testing tool into your browser of choice
  2. Go to Box | Vue Formulate
  3. Open dev tools and run aXe on the page
  4. Several instances of “Form field should not have multiple label elements” appear

Expected behavior

Screen reader users should be able to jump to radio/checkbox groups and be able to hear the context of the question being asked of them, as well as the label associated with the radio/checkbox input. Deque’s aXe should not throw warnings/errors.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Comments:9 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
aminimalanimalcommented, Jan 13, 2021

@justin-schroeder Just checked out release/2.5.0. This is a massive improvement. Thanks so much.

This implementation differs from my description above, but it’s potentially better. This implementation assigns role="group to the container directly around the radios/checkboxes whereas I described it being on the outer container, but my inclination of where to place it was based on how fieldset operates, and that may not be the preferred way of handling role="group". In any case, the differences in how a screen reader interprets the two are subtle, and I’m honestly not certain which way would be preferred. I’m quite happy with this approach.

1reaction
aminimalanimalcommented, Nov 18, 2021

I ran the tests. Results here. Not tested: JAWS, Android. https://codepen.io/aminimalanimal/pen/NqbXKX

Overall, of modern browsers, iOS is the one that messes up what otherwise is pretty darn consistent support for both. iOS does not seem to support role="group". I may file a bug for that.

If you’re supporting IE 11 (and hopefully you aren’t), role="group" is actually preferable.

I have definitely run into complications with fieldset styling. I know there are workarounds to it, but that’s not the point. If you’re creating a system where the developer isn’t going to have a lot of control over the HTML output, you need to be aware of the limitations your system imposes upon them, and fieldset is a special case.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Option to disable decorator in InputBox · Issue #313 - GitHub
I'd like the option to disable the rendering of the decorator in the InputBox. Some accessibility checking software flag it as an empty...
Read more >
Grouping Controls | Web Accessibility Initiative (WAI) - W3C
Grouping related form controls makes forms more understandable for all users, ... As the labels in both groups have the same text, the...
Read more >
Navigation in Lists: To Be or Not To Be - CSS-Tricks
Dustin's article talks about semantics, accessibility, and screen readers, but has no research and doesn't explain why the listless navigation ...
Read more >
QTI v3 Best Practices and Implementation Guide - 1EdTech
The following participating organizations have made explicit license commitments to ... QTI 3 brings together QTI 2.x and Accessible Portable Item Protocol® ...
Read more >
#32338 (Accessibility issues with Django forms RadioSelect ...
Note how the first field has the wrong label due to the markup. ... Lack of a fieldset means no semantic grouping. The...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found