[Django] #32338: Accessibility issues with Django forms RadioSelect and CheckboxSelectMultiple

13 views
Skip to first unread message

Django

unread,
Jan 10, 2021, 10:48:42 AM1/10/21
to django-...@googlegroups.com
#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.

Django

unread,
Jan 10, 2021, 10:50:37 AM1/10/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: nobody

Type: Bug | Status: new
Component: Forms | Version: master
Severity: Normal | Resolution:

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
-------------------------------------+-------------------------------------
Changes (by Thibaud Colas):

* Attachment "radio-select-first-input-label.png" added.

Screenshot of a RadioSelect widget, with DevTools open to see the markup,
and VoiceOver rotor menu showing the Form Controls’ labels

Django

unread,
Jan 10, 2021, 10:51:45 AM1/10/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: nobody

Type: Bug | Status: new
Component: Forms | Version: master
Severity: Normal | Resolution:

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
-------------------------------------+-------------------------------------
Description changed by Thibaud Colas:

Old description:

New description:

== RadioSelect issues

[[Image(https://code.djangoproject.com/attachment/ticket/32338/radio-
select-first-input-label.png)]]

== CheckboxSelectMultiple issues

== Proposed solution

--

--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:1>

Django

unread,
Jan 10, 2021, 10:52:46 AM1/10/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: nobody

Type: Bug | Status: new
Component: Forms | Version: master
Severity: Normal | Resolution:

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
-------------------------------------+-------------------------------------
Description changed by Thibaud Colas:

Old description:

> In the Forms API, there are issues with the default rendering of fields

> [[Image(https://code.djangoproject.com/attachment/ticket/32338/radio-
> select-first-input-label.png)]]

New description:

== RadioSelect issues

[[Image(https://code.djangoproject.com/raw-attachment/ticket/32338/radio-
select-first-input-label.png)]]

== 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:

{{{


<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#comment:2>

Django

unread,
Jan 10, 2021, 10:58:14 AM1/10/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: nobody

Type: Bug | Status: new
Component: Forms | Version: master
Severity: Normal | Resolution:

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
-------------------------------------+-------------------------------------
Description changed by Thibaud Colas:

Old description:

> In the Forms API, there are issues with the default rendering of fields

> [[Image(https://code.djangoproject.com/raw-attachment/ticket/32338/radio-
> select-first-input-label.png)]]


>
> == 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:
>
> {{{

> <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>
> }}}

New description:

== RadioSelect issues

[[Image(https://code.djangoproject.com/raw-attachment/ticket/32338/radio-
select-first-input-label.png)]]

== 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).

== Documentation issues

Additionally to the above, there are a few occurences of custom
radio/checkbox markup in the documentation that will lead to the same
issues:

- https://docs.djangoproject.com/en/3.1/ref/forms/widgets/#radioselect
- https://docs.djangoproject.com/en/3.1/intro/tutorial04/#write-a-minimal-
form

== Proposed solution

Essentially following [https://www.w3.org/TR/WCAG20-TECHS/H71 technique

H71] of WCAG. The ideal solution is identical for both widgets, and also
for documentation code snippets:

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:

{{{


<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#comment:3>

Django

unread,
Jan 10, 2021, 10:59:18 AM1/10/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: nobody

Type: Bug | Status: new
Component: Forms | Version: master
Severity: Normal | Resolution:

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
-------------------------------------+-------------------------------------
Description changed by Thibaud Colas:

Old description:

> In the Forms API, there are issues with the default rendering of fields

> [[Image(https://code.djangoproject.com/raw-attachment/ticket/32338/radio-
> select-first-input-label.png)]]


>
> == 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).
>

> == Documentation issues
>
> Additionally to the above, there are a few occurences of custom
> radio/checkbox markup in the documentation that will lead to the same
> issues:
>
> - https://docs.djangoproject.com/en/3.1/ref/forms/widgets/#radioselect
> - https://docs.djangoproject.com/en/3.1/intro/tutorial04/#write-a
> -minimal-form
>

> == Proposed solution
>
> Essentially following [https://www.w3.org/TR/WCAG20-TECHS/H71 technique

> H71] of WCAG. The ideal solution is identical for both widgets, and also
> for documentation code snippets:
>

> 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:
>
> {{{

> <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>
> }}}

New description:

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, and with the custom rendering examples in the
official documentation. Here is a form I set up for my testing of the
default rendering:

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

[[Image(https://code.djangoproject.com/raw-attachment/ticket/32338/radio-
select-first-input-label.png)]]

== 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).

== Documentation issues

Additionally to the above, there are a few occurences of custom
radio/checkbox markup in the documentation that will lead to the same
issues:

== Proposed solution

Essentially following [https://www.w3.org/TR/WCAG20-TECHS/H71 technique

H71] of WCAG. The ideal solution is identical for both widgets, and also
for documentation code snippets:

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:

{{{


<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#comment:4>

Django

unread,
Jan 11, 2021, 11:57:02 AM1/11/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: nobody

Type: Bug | Status: new
Component: Forms | Version: master
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* stage: Unreviewed => Accepted


Comment:

Thanks for detailed report.

--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:5>

Django

unread,
Jan 12, 2021, 6:40:31 AM1/12/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: jcoombes
Type: Bug | Status: assigned
Component: Forms | Version: master

Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by jcoombes):

* owner: nobody => jcoombes
* status: new => assigned


--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:6>

Django

unread,
Jan 21, 2021, 12:49:09 PM1/21/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: jcoombes
Type: Bug | Status: assigned
Component: Forms | Version: master

Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------

Comment (by jcoombes):

21/01/21

Okay, so I can reproduce this bug.
I found the relevant template which fills the content of a relevant widget
at:

django/django/forms/templates/django/forms/widgets/multiple_input.html
[The optional version of this template is at
.../django/forms/input_option.html]

This template is used to render the html form using:

django/django/forms/forms.py

BaseForm.as_p,
BaseForm.as_ul,
BaseForm.as_table.

I got stuck today - as_p, as_ul and as_table call _html_output() with
different parameters.
Changing _html_output() is risky as it would be easy to break single-
choice form fields. The code is already quite abstracted.

Easier approach:
Create and document a new method as_fieldset() for this new use case.
Avoid _html_output() entirely.

Harder approach:
Rewrite _html_output() to include the html tags necessary to render
RadioSelect and CheckboxSelectMultiple accessibly.
Avoid breaking existing code.

I think I will take the easier approach, but I'm open to feedback on how
to do it the harder way without introducing regressions.

--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:7>

Django

unread,
Jan 22, 2021, 2:51:56 AM1/22/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: jcoombes
Type: Bug | Status: assigned
Component: Forms | Version: master

Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------

Comment (by Claude Paroz):

Note you should be aware of current work in #31026 (template-based
rendering).

--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:8>

Django

unread,
Jan 22, 2021, 9:45:09 AM1/22/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: (none)

Type: Bug | Status: new
Component: Forms | Version: master
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by jcoombes):

* owner: jcoombes => (none)
* status: assigned => new


--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:9>

Django

unread,
May 13, 2021, 2:01:29 AM5/13/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev

Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by David Smith):

* owner: (none) => David Smith


* status: new => assigned


--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:10>

Django

unread,
May 14, 2021, 1:41:19 AM5/14/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by David Smith):

* has_patch: 0 => 1


--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:11>

Django

unread,
Jun 4, 2021, 4:46:01 AM6/4/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------

Comment (by Mariusz Felisiak <felisiak.mariusz@…>):

In [changeset:"32b366a86482b06db417aec67e42ca23d69da48a" 32b366a8]:
{{{
#!CommitTicketReference repository=""
revision="32b366a86482b06db417aec67e42ca23d69da48a"
[3.2.x] Refs #32338 -- Improved accessibility of RadioSelect examples in
docs.

Co-authored-by: Thibaud Colas <thibau...@gmail.com>

Backport of d8c17aa10c7f41e692fb6f5d0bf2fab7a90b9374 from main
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:12>

Django

unread,
Jun 4, 2021, 4:46:02 AM6/4/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------

Comment (by Mariusz Felisiak <felisiak.mariusz@…>):

In [changeset:"d8c17aa10c7f41e692fb6f5d0bf2fab7a90b9374" d8c17aa]:
{{{
#!CommitTicketReference repository=""
revision="d8c17aa10c7f41e692fb6f5d0bf2fab7a90b9374"


Refs #32338 -- Improved accessibility of RadioSelect examples in docs.

Co-authored-by: Thibaud Colas <thibau...@gmail.com>
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:13>

Django

unread,
Jun 4, 2021, 4:46:35 AM6/4/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Description changed by Mariusz Felisiak:

Old description:

> 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, and with the custom rendering examples in the
> official documentation. Here is a form I set up for my testing of the
> default rendering:
>
> - 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.
>

> [[Image(https://code.djangoproject.com/raw-attachment/ticket/32338/radio-
> select-first-input-label.png)]]


>
> == 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).
>

> == Documentation issues
>
> Additionally to the above, there are a few occurences of custom
> radio/checkbox markup in the documentation that will lead to the same
> issues:
>
> - https://docs.djangoproject.com/en/3.1/ref/forms/widgets/#radioselect
> - https://docs.djangoproject.com/en/3.1/intro/tutorial04/#write-a
> -minimal-form
>

> == Proposed solution
>
> Essentially following [https://www.w3.org/TR/WCAG20-TECHS/H71 technique

> H71] of WCAG. The ideal solution is identical for both widgets, and also
> for documentation code snippets:
>

> 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:
>
> {{{

> <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>
> }}}

New description:

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, and with the custom rendering examples in the

official documentation. Here is a form I set up for my testing of the
default rendering:

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

[[Image(https://code.djangoproject.com/raw-attachment/ticket/32338/radio-
select-first-input-label.png)]]

== 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).

== Documentation issues (fixed in
d8c17aa10c7f41e692fb6f5d0bf2fab7a90b9374)

Additionally to the above, there are a few occurences of custom
radio/checkbox markup in the documentation that will lead to the same
issues:

== Proposed solution

Essentially following [https://www.w3.org/TR/WCAG20-TECHS/H71 technique

H71] of WCAG. The ideal solution is identical for both widgets, and also
for documentation code snippets:

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:

{{{


<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#comment:14>

Django

unread,
Jun 22, 2021, 6:03:44 AM6/22/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------

Comment (by Mariusz Felisiak <felisiak.mariusz@…>):

In [changeset:"b9e872b59329393f615c440c54f632a49ab05b78" b9e872b5]:
{{{
#!CommitTicketReference repository=""
revision="b9e872b59329393f615c440c54f632a49ab05b78"
Refs #32338 -- Removed 'for ="..."' from RadioSelect's <label>.

This improves accessibility for screen reader users.

Co-authored-by: Thibaud Colas <thibau...@gmail.com>
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:15>

Django

unread,
Jun 22, 2021, 6:06:15 AM6/22/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Description changed by Mariusz Felisiak:

Old description:

> 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, and with the custom rendering examples in the
> official documentation. Here is a form I set up for my testing of the
> default rendering:
>
> - 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.
>

> [[Image(https://code.djangoproject.com/raw-attachment/ticket/32338/radio-
> select-first-input-label.png)]]


>
> == 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).
>

> == Documentation issues (fixed in
> d8c17aa10c7f41e692fb6f5d0bf2fab7a90b9374)
>
> Additionally to the above, there are a few occurences of custom
> radio/checkbox markup in the documentation that will lead to the same
> issues:
>
> - https://docs.djangoproject.com/en/3.1/ref/forms/widgets/#radioselect
> - https://docs.djangoproject.com/en/3.1/intro/tutorial04/#write-a
> -minimal-form
>

> == Proposed solution
>
> Essentially following [https://www.w3.org/TR/WCAG20-TECHS/H71 technique

> H71] of WCAG. The ideal solution is identical for both widgets, and also
> for documentation code snippets:
>

> 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:
>
> {{{

> <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>
> }}}

New description:

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, and with the custom rendering examples in the

official documentation. Here is a form I set up for my testing of the
default rendering:

== 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. (**fixed in b9e872b59329393f615c440c54f632a49ab05b78**) 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.

[[Image(https://code.djangoproject.com/raw-attachment/ticket/32338/radio-
select-first-input-label.png)]]

== 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).

== Documentation issues (fixed in
d8c17aa10c7f41e692fb6f5d0bf2fab7a90b9374)

Additionally to the above, there are a few occurences of custom
radio/checkbox markup in the documentation that will lead to the same
issues:

== Proposed solution

Essentially following [https://www.w3.org/TR/WCAG20-TECHS/H71 technique

H71] of WCAG. The ideal solution is identical for both widgets, and also
for documentation code snippets:

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:

{{{


<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#comment:16>

Django

unread,
Aug 26, 2021, 7:08:51 AM8/26/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Ready for
forms, wcag | checkin
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* stage: Accepted => Ready for checkin


--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:17>

Django

unread,
Aug 27, 2021, 12:46:19 AM8/27/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Ready for
forms, wcag | checkin
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------

Comment (by Mariusz Felisiak <felisiak.mariusz@…>):

In [changeset:"5942ab5eb165ee2e759174e297148a40dd855920" 5942ab5]:
{{{
#!CommitTicketReference repository=""
revision="5942ab5eb165ee2e759174e297148a40dd855920"
Refs #32338 -- Made RadioSelect/CheckboxSelectMultiple render in <div>
tags.

This improves accessibility for screen reader users.
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:18>

Django

unread,
Aug 27, 2021, 12:52:28 AM8/27/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* has_patch: 1 => 0
* stage: Ready for checkin => Accepted


Old description:

> 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, and with the custom rendering examples in the
> official documentation. Here is a form I set up for my testing of the
> default rendering:
>
> - 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. (**fixed in b9e872b59329393f615c440c54f632a49ab05b78**) 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.
>

> [[Image(https://code.djangoproject.com/raw-attachment/ticket/32338/radio-
> select-first-input-label.png)]]


>
> == 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).
>

> == Documentation issues (fixed in
> d8c17aa10c7f41e692fb6f5d0bf2fab7a90b9374)
>
> Additionally to the above, there are a few occurences of custom
> radio/checkbox markup in the documentation that will lead to the same
> issues:
>
> - https://docs.djangoproject.com/en/3.1/ref/forms/widgets/#radioselect
> - https://docs.djangoproject.com/en/3.1/intro/tutorial04/#write-a
> -minimal-form
>

> == Proposed solution
>
> Essentially following [https://www.w3.org/TR/WCAG20-TECHS/H71 technique

> H71] of WCAG. The ideal solution is identical for both widgets, and also
> for documentation code snippets:
>

> 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:
>
> {{{

> <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>
> }}}

New description:

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, and with the custom rendering examples in the

official documentation. Here is a form I set up for my testing of the
default rendering:

== 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. (**fixed in b9e872b59329393f615c440c54f632a49ab05b78**) 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. (**fixed in 5942ab5eb165ee2e759174e297148a40dd855920**) 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.

[[Image(https://code.djangoproject.com/raw-attachment/ticket/32338/radio-
select-first-input-label.png)]]

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

- (**fixed in 5942ab5eb165ee2e759174e297148a40dd855920**) Wrapping the


fields in list items also makes the form more verbose than it should be
(and the semantics issues are the same).

== Documentation issues (fixed in
d8c17aa10c7f41e692fb6f5d0bf2fab7a90b9374)

Additionally to the above, there are a few occurences of custom
radio/checkbox markup in the documentation that will lead to the same
issues:

== Proposed solution

Essentially following [https://www.w3.org/TR/WCAG20-TECHS/H71 technique

H71] of WCAG. The ideal solution is identical for both widgets, and also
for documentation code snippets:

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. (**fixed in 5942ab5eb165ee2e759174e297148a40dd855920**) 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:

{{{


<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#comment:19>

Django

unread,
Nov 7, 2021, 8:11:13 AM11/7/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by David Smith):

* has_patch: 0 => 1


--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:20>

Django

unread,
Nov 18, 2021, 1:33:28 AM11/18/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1

Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* needs_better_patch: 0 => 1


--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:21>

Django

unread,
Dec 9, 2021, 1:19:14 AM12/9/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Ready for
forms, wcag | checkin
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* needs_better_patch: 1 => 0


* stage: Accepted => Ready for checkin


--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:22>

Django

unread,
Dec 9, 2021, 3:23:15 AM12/9/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Ready for
forms, wcag | checkin
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------

Comment (by Mariusz Felisiak <felisiak.mariusz@…>):

In [changeset:"eba9a9b7f72995206af867600d6685b5405f172a" eba9a9b]:
{{{
#!CommitTicketReference repository=""
revision="eba9a9b7f72995206af867600d6685b5405f172a"
Refs #32338 -- Added Boundfield.legend_tag().
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:23>

Django

unread,
Dec 9, 2021, 3:23:41 AM12/9/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* has_patch: 1 => 0


* stage: Ready for checkin => Accepted


--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:24>

Django

unread,
Dec 27, 2021, 4:02:36 PM12/27/21
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by David Smith):

* has_patch: 0 => 1


--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:25>

Django

unread,
Jan 24, 2022, 7:16:34 AM1/24/22
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Normal | Resolution:
Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1

Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* needs_better_patch: 0 => 1


--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:26>

Django

unread,
May 5, 2022, 8:35:42 AM5/5/22
to django-...@googlegroups.com
#32338: Accessibility issues with Django forms RadioSelect and
CheckboxSelectMultiple
-------------------------------------+-------------------------------------
Reporter: Thibaud Colas | Owner: David
| Smith
Type: Bug | Status: closed
Component: Forms | Version: dev
Severity: Normal | Resolution: fixed

Keywords: accessibility, | Triage Stage: Accepted
forms, wcag |
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1
Easy pickings: 0 | UI/UX: 1
-------------------------------------+-------------------------------------
Changes (by Carlton Gibson):

* status: assigned => closed
* resolution: => fixed


Comment:

Fixed by ec5659382a5f5fc2daf0c87ccc89d0fb07534874 for #32339.

It may be possible to followup improving the `p`, `table`, and `ul`
templates but see https://code.djangoproject.com/ticket/32339#comment:3.

--
Ticket URL: <https://code.djangoproject.com/ticket/32338#comment:27>

Reply all
Reply to author
Forward
0 new messages