#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
               Reporter:  Thibaud    |          Owner:  nobody
  Colas                              |
                   Type:  Bug        |         Status:  new
              Component:  Forms      |        Version:  master
               Severity:  Normal     |       Keywords:  accessibility,
           Triage Stage:             |  forms, wcag
  Unreviewed                         |      Has patch:  0
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  1          |
-------------------------------------+-------------------------------------
 In the Forms API, there are issues with the default rendering of fields
 that rely on widgets based on multiple radio or checkbox inputs:
 `RadioSelect` and `CheckboxSelectMultiple`. This is with the default
 rendering of those widgets. Here is a form I set up for my testing if it
 helps:

 - Live form: http://django-admin-tests.herokuapp.com/forms/example_form/p/
 - Form fields definitions:
 
https://github.com/thibaudcolas/django_admin_tests/blob/main/django_admin_tests/forms.py
 - Template:
 
https://github.com/thibaudcolas/django_admin_tests/blob/main/django_admin_tests/templates/django_admin_tests/example_form.html

 == RadioSelect issues

 1. The fields aren’t semantically grouped, only visually, so the grouping
 isn’t apparent to assistive technlogies. It’s important that related
 fields are grouped, and that the group has a label attached to it. If I
 was auditing this professionally I would classify this as a fail for
 [https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html
 SC 1.3.1 - Info and Relationships] of WCAG 2.1, as the grouping and label
 are somewhat there, just not done with the appropriate semantics.
 2. Speaking of label – currently the group of fields is labeled by
 preceding it with `<label for="id_radio_choice_required_0">Radio choice
 required:</label>`. This overrides the label of the first field within the
 group (and only labels the first field, not the whole group). This is a
 fail for [https://www.w3.org/WAI/WCAG21/Understanding/labels-or-
 instructions.html 3.3.2: Labels or Instructions] and/or SC 1.3.1.
 3. Wrapping the fields each in their own list item makes the navigation
 more tedious, as screen readers will first announce list items numbering,
 and only then their content.

 Here is a screenshot demonstrating the label issue with macOS VoiceOver’s
 rotor. Note how the first field has the wrong label due to the markup.

 radio-select-first-input-label.png

 [[Image()]]

 == CheckboxSelectMultiple issues

 Almost identical to the above,

 - Lack of a fieldset means no semantic grouping.
 - The label isn’t associated with anything for this widget, so is less
 problematic but no more correct.
 - Wrapping the fields in list items also makes the form more verbose than
 it should be (and the semantics issues are the same).

 == Proposed solution

 Essentially following [https://www.w3.org/TR/WCAG20-TECHS/H71 technique
 H71] of WCAG. The ideal solution is identical for both:

 1. Wrap the group of fields in a `fieldset`, including the group’s label,
 the inputs, the help text, and any errors.
 2. Use a `legend` as the first item in the `fieldset` for the group’s
 label, rather than a `label`.
 3. Replace the list markup with un-semantic div, or remove altogether. If
 the list markup is needed for backwards compatibility, it could also use
 `role="presentation"` so assistive technologies ignore the list & listitem
 semantics, but I would only recommend that as a temporary workaround.

 Here is sample markup to implement the above. The `div` aren’t needed,
 I’ve only added them to preserve the vertical layout of the current
 implementation:

 {{{#!html
 <fieldset>
     <legend>Radio choice required:</legend>
     <div><label for="id_radio_choice_required_0"><input type="radio"
 name="radio_choice_required" value="one" required=""
 id="id_radio_choice_required_0">
  One</label></div>
     <div><label for="id_radio_choice_required_1"><input type="radio"
 name="radio_choice_required" value="two" required=""
 id="id_radio_choice_required_1">
  Two</label></div>
     <div><label for="id_radio_choice_required_2"><input type="radio"
 name="radio_choice_required" value="three" required=""
 id="id_radio_choice_required_2">
  Three</label></div>
     <div><label for="id_radio_choice_required_3"><input type="radio"
 name="radio_choice_required" value="four" required=""
 id="id_radio_choice_required_3">
  Four</label></div>
 <span class="helptext">Help</span>
 </fieldset>
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/32338>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/055.59f430d0a2b3ff3849e3112bb8e1b066%40djangoproject.com.

Reply via email to