Problem/Motivation
The checkboxes element provided by core does not apply the `required` attribute to the generated <input> elements. Instead the required attribute is applied to the wrapping <fieldset>.
There are two issues with this:
- screen readers use `required` attribute on
<input>s for checkboxes. If the attribute is not present on the<input>, then the required state will not be communicated to the screen reader user. - the `required` attribute is not valid when applied to a
<fieldset>, thus causing the page to fail W3C validation.
To reproduce this, create a form that uses the Checkboxes element. Notice that the required attribute is attached to the wrapping <fieldset> and not the <input> elements.
@mfairchild365 created a hacky module to fix the issue. It might be helpful in diagnosing and fixing the issue in core: https://github.com/mfairchild365/a11yformsfix/blob/master/readme.md
Proposed resolution
Minimum: avoid the invalid HTML - remove the required attribute from the fieldset element.See #3174459: [PP-1] W3C Validation: required attribute not allowed on fieldset tag- Ideally: figure out a good approach to improve the checkboxes, and convey the requirements to assistive tech. Mandatory checkboxes mean "tick at least one of these", which isn't easily expressed by HTML.
Remaining tasks
Evaluate impact on templates - does this qualify as a bugfix which can go in Stable theme?
Write a patch.
Tests?
User interface changes
Markup changes to:
- Fix invalid HTML
- Convey required checkboxes element correctly to assistive tech.
API changes
None.
Data model changes
none.
| Comment | File | Size | Author |
|---|---|---|---|
| #3 | required-checkboxes-a11yformsfix-2950999.png | 50.85 KB | andrewmacpherson |
Comments
Comment #2
mfairchild365 commentedComment #3
andrewmacpherson commentedThanks for reporting this @mfairchild365.
Sure enough
<fieldset required>is invalid HTML. We should aim to fix that.I tried your a11yformsfix module. I think the approach for #radios element is a good start, though it has some styling issues that should be addressed. The red asterisk is applied by CSS, for example. Visually, I think we would still want it to be a single asterisk on the fieldset legend. (Note that it's only necessary to add the required attribute to ONE of the radio buttons in a required group - unless there are some browser quirks about this?)
For the #checkboxes element, I think your module takes the wrong approach. When a #checkboxes FAPI element is required, it means at least one checkbox must be ticked - and this isn't easily expressed in HTML. Instead, a11yformsfix adds the required attribute to every checkbox, so you have to tick all of them!
Here's a screenshot of required #radios and #checkboxes with the a11yformsfix module enabled. It shows the styling problem with required #radios, and the validation problem for required #checkboxes.
Removing a11y tag; accessibility is the preferred tag.
Comment #4
mfairchild365 commentedI agree that each radio button does not need a visual asterisk, only the legend needs it.
Good catch about the checkboxes. I had known that HTML5 doesn't natively support that, but I had forgotten. I removed the fix from my module. I had disabled js validation for the webforms I was using, so I didn't see this error client side (but it worked correctly with server side validation). A possible solution would be to apply the required attribute to all of the grouped checkboxes by default, then when one is selected, remove the required attribute from the other checkboxes with js.
Comment #5
andrewmacpherson commentedThere are some problems with this idea...
We can spilt this into two separate issues. It sounds like the #radios will be an easy thing to fix, but #checkboxes are tricky. So let's make a separate issue to fix #radios first, and keep discussing #checkboxes here.
Comment #6
andrewmacpherson commentedI created the separate issue for the #radios element. Let's use this one to figure out what to do with #checkboxes. I've updated the issue summary here.
Comment #7
andrewmacpherson commentedComment #8
andrewmacpherson commentedComment #9
mfairchild365 commentedAll of those are fair points. I like your plan to split this into two issues. I obviously didn't think through the checkbox issue very well.
Comment #10
andrewmacpherson commentedComment #14
dpiNot sure where "#radios" / "#checkboxes" came from.
To be honest I think this issue and #2951317: Radios element missing "required" attribute should be merged.
Comment #21
acbramley commentedThis is still valid on HEAD, tested with the default options checkboxes on the NodeTypeForm. AFAICT there is still no solution for this in raw HTML.
Comment #23
kentr commentedComment #24
kentr commentedAfter #1797438: HTML5 validation is preventing form submit and not fully accessible lands, this could possibly be done with custom validation combined with some language similar to "At least one choice is required".
I'm not sure where should that text go, though. Perhaps in an individual element within the
fieldsetand referenced byaria-describedbyon thefieldset.Comment #25
drupalite1411 commentedHi All, I am suggesting as way to fix it for screen readers-
In /core/lib/Drupal/Core/Render/Element/Checkboxes.php
modify Checkboxes::processCheckboxes() to append visually-hidden text to the element's #title when the checkboxes group is required. Since #title renders as the inside the fieldset wrapper, screen readers announce it when users enter the group.
After processing, the legend will contain a hidden span: (at least one option must be selected) — so a screen reader user hears something like "title (at least one option must be selected), group" when navigating into a required checkbox fieldset.
Comment #26
kentr commented@drupalite1411 thanks for the input.
#2921627: Do not use a CSS-only required marker in forms per WCAG 2.0 adds a hidden "(required)" to the
legendfor checkboxes, so let's see where that goes first.Comment #27
kentr commentedFun fact: Core adds the text "(Required)" to the
titleattribute for the individual checkboxes in acheckboxeselement.If we end up not adding the
requiredattribute to the individual checkboxes in a group based on #3, I think we should also remove that line that adds "(Required)" to thetitleattributes.Comment #28
drupalite1411 commentedHi Kentr,
Do you mean if https://www.drupal.org/project/drupal/issues/2921627 this issue is merged than we will have text(Required) to the checkboxes?
Core marks required form labels/titles with required metadata (and typically a visual marker), but it does not add literal (Required) text to each individual checkbox option in a checkboxes group.
Comment #29
mukeshaddweb commentedComment #30
kentr commented@mukeshaddweb Thanks for your contribution!
It looks like your patch exchanges the
requiredattribute witharia-required="true"onfieldsetelements in general.There's a separate issue for removing the
requiredattribute fromfieldsetelements in general: #3174459: [PP-1] W3C Validation: required attribute not allowed on fieldset tag . I believe that issue will covercheckboxeselements.That issue has work on it and is a higher-level discussion, so for now I think we should do the work for
fieldsetthere. I'm striking that component from the issue summary.Also, a couple of other comments:
aria-requiredis appropriate for fieldset elements. As far as I can tell, it is disallowed by the spec, and a request to allow it was declined. The following comment by @scottohara is especially informative.I'm flagging for accessibility review for a double-check if the team agrees with #3 about not adding the
requiredattribute to the individual checkbox elements. The comment from @scottohara on the github thread almost clinches it for me, but I want sign-off from the others and a recommended approach.Comment #31
kentr commented@drupalite1411:
It will add a hidden text of " (required)" to the
legendof the containingfieldset. I'm working on it now.I saw case where a literal "(Required)" text was added to the
titleattributes of the individualcheckboxelements in thecheckboxesgroup. There's even a test for it.Comment #32
kentr commentedI didn't dig deeply enough and misinterpreted.
Core adds " (Required)" to the
titleattribute of thefieldsetelement and a wrapperdiv, not to the individualcheckboxelements.