#32339: Accessibility issues with Django forms as_table, as_ul, as_p rendering
helpers
-------------------------------------+-------------------------------------
     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:

> The Django forms API has three methods available to render whole forms at
> once: `as_table`, `as_ul`, `as_p`. All of these three have issues of
> varying severity. Although it’s not always the case that developers rely
> on those methods when implementing forms, I think it’s important for
> Django to not provide APIs that make it easy for developers to do the
> wrong thing – just like with security, Django should have safe defaults.
>
> Like [https://code.djangoproject.com/ticket/32338 #32338] – those issues
> are with the vanilla rendering of forms, and also with related
> documentation code snippets. If you want to troubleshoot this, here is
> the form I used for testing:
>
> - Live form as_p: ​http://django-admin-
> tests.herokuapp.com/forms/example_form/p/
> - Live form as_ul: ​http://django-admin-
> tests.herokuapp.com/forms/example_form/ul/
> - Live form as_table: http://django-admin-
> tests.herokuapp.com/forms/example_form/table/
> - 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
>
> == as_table
>
> `as_table` is the most problematic of all form output styles. Although
> technically tables for layout can be ok (see WebAIM’s
> [https://webaim.org/techniques/tables/ Creating Accessible Tables]), I
> think the general consensus is that they should be a thing of the past.
> Depending on how screen readers interpret the markup, there is a very
> real chance that they will incorrectly announce the table with tabular
> navigation, made for tabular data. This will make the form at best very
> verbose to navigate, at worst plain confusing.
>
> There are well-established workarounds to this keep on using tables like
> this (like adding [https://w3c.github.io/aria-
> practices/#presentation_role role="presentation"] to the `<table>`), but
> there really is no reason to use tables for layout these days, as
> identical or better layouts can be done with CSS (for example for a
> responsive form). I think this can also potentially be a source of
> confusion for developers – so Django shouldn’t encourage potentially-
> misused markup.
>
> == as_ul
>
> `as_ul` essentially has the same problem as `as_table` but to a lesser
> extent. Wrapping whole forms in list items makes the form more verbose to
> navigate, and can be a bit confusing, but that’s about it. Semantically
> it’s also incorrect (unordered lists means the order of items doesn’t
> matter). I don’t think it’s very problematic, but it’s also not very
> useful to start with.
>
> == as_p
>
> The `as_p` markup doesn’t cause any accessibility issues that I’m aware
> of, but it can be problematic nonetheless because `p` only allows
> [https://developer.mozilla.org/en-
> US/docs/Web/Guide/HTML/Content_categories#phrasing_content phrasing
> content] (see for example [https://code.djangoproject.com/ticket/31189
> #31189]).
>
> From a pure "end-user outcome" perspective there is no problem with this
> to my knowledge, but again I think Django should err on the side of safe
> defaults and not encourage patterns that can lead to invalid HTML, when
> developers use custom widgets that would contain tags invalid in phrasing
> content (for example a date picker). Even if browsers handle the invalid
> HTML just fine, it’s very common for accessibility testing tools to
> implement HTML validation rules, and Django should strive to minimize
> false positives with those tools if there are obvious alternatives.
>
> == Proposed solution
>
> I would recommend removing all three output styles. Even if their output
> can be tweaked to circumvent those issues, they make it too easy to do
> the wrong thing. They also discourage developers from using `<fieldset>`
> and `<legend>` to appropriately group related fields. Although this isn’t
> needed all the time, for larger forms it’s important for there to be
> clear sections.
>
> If Django wants to provide a shortcut to render whole forms, I think it
> should be based on `<div>` (optionally with a class name), so it still
> allows for layout niceties but works as expect for all users otherwise. I
> think the documentation should also be updated to cover why it’s also
> desirable to render fields individually so they can be grouped in
> sections.
>
> ---
>
> I realize this would be a breaking change, with a lengthy deprecation
> period. During this deprecation period, it would also be great to:
>
> - Document the gotchas with the three shortcuts.
> - Recommend marking up `<table>` and `<ul>` with [https://w3c.github.io
> /aria-practices/#presentation_role role="presentation"], so screen
> readers ignore the semantics.

New description:

 The Django forms API has three methods available to render whole forms at
 once: `as_table`, `as_ul`, `as_p`. All of these three have issues of
 varying severity. Although it’s not always the case that developers rely
 on those methods when implementing forms, I think it’s important for
 Django to not provide APIs that make it easy for developers to do the
 wrong thing – just like with security, Django should have safe defaults.

 Like [https://code.djangoproject.com/ticket/32338 #32338] – those issues
 are with the vanilla rendering of forms, and also with related
 documentation code snippets. If you want to troubleshoot this, here is the
 form I used for testing:

 - Live form as_p: ​http://django-admin-
 tests.herokuapp.com/forms/example_form/p/
 - Live form as_ul: ​http://django-admin-
 tests.herokuapp.com/forms/example_form/ul/
 - Live form as_table: http://django-admin-
 tests.herokuapp.com/forms/example_form/table/
 - 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

 == as_table

 `as_table` is the most problematic of all form output styles. Although
 technically tables for layout can be ok (see WebAIM’s
 [https://webaim.org/techniques/tables/ Creating Accessible Tables]), I
 think the general consensus is that they should be a thing of the past.
 Depending on how screen readers interpret the markup, there is a very real
 chance that they will incorrectly announce the table with tabular
 navigation, made for tabular data. This will make the form at best very
 verbose to navigate, at worst plain confusing.

 There are well-established workarounds to this keep on using tables like
 this (like adding [https://w3c.github.io/aria-practices/#presentation_role
 role="presentation"] to the `<table>`), but there really is no reason to
 use tables for layout these days, as identical or better layouts can be
 done with CSS (for example for a responsive form). I think this can also
 potentially be a source of confusion for developers – so Django shouldn’t
 encourage potentially-misused markup.

 == as_ul

 `as_ul` essentially has the same problem as `as_table` but to a lesser
 extent. Wrapping whole forms in list items makes the form more verbose to
 navigate, and can be a bit confusing, but that’s about it. Semantically
 it’s also incorrect (unordered lists means the order of items doesn’t
 matter). I don’t think it’s very problematic, but it’s also not very
 useful to start with.

 == as_p

 The `as_p` markup doesn’t cause any accessibility issues that I’m aware
 of, but it can be problematic nonetheless because `p` only allows
 [https://developer.mozilla.org/en-
 US/docs/Web/Guide/HTML/Content_categories#phrasing_content phrasing
 content] (see for example [https://code.djangoproject.com/ticket/31189
 #31189]).

 From a pure "end-user outcome" perspective there is no problem with this
 to my knowledge, but again I think Django should err on the side of safe
 defaults and not encourage patterns that can lead to invalid HTML, when
 developers use custom widgets that would contain tags invalid in phrasing
 content (for example a date picker). Even if browsers handle the invalid
 HTML just fine, it’s very common for accessibility testing tools to
 implement HTML validation rules, and Django should strive to minimize
 false positives with those tools if there are obvious alternatives.

 == Proposed solution

 I would recommend removing all three output styles. Even if their output
 can be tweaked to circumvent those issues, they make it too easy to do the
 wrong thing. They also discourage developers from using `<fieldset>` and
 `<legend>` to appropriately group related fields. Although this isn’t
 needed all the time, for larger forms it’s important for there to be clear
 sections. I think they also have an impact on how likely developers are to
 properly label fields as either required or optional where relevant –
 Django’s default output has no such labeling, so developers either have to
 remember to add a custom label for each field suffixed with `(required)`,
 or skip those output styles.

 If Django wants to provide a shortcut to render whole forms, I think it
 should be based on `<div>` (optionally with a class name), so it still
 allows for layout niceties but works as expect for all users otherwise. I
 think the documentation should also be updated to cover why it’s also
 desirable to render fields individually so they can be grouped in
 sections.

 ---

 I realize this would be a breaking change, with a lengthy deprecation
 period. During this deprecation period, it would also be great to:

 - Document the gotchas with the three shortcuts.
 - Recommend marking up `<table>` and `<ul>` with [https://w3c.github.io
 /aria-practices/#presentation_role role="presentation"], so screen readers
 ignore the semantics.

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/32339#comment:1>
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/070.3c2b097f6069014eaebd1c0da9cb9400%40djangoproject.com.

Reply via email to