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:

  1. 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.
  2. 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

  1. 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
  2. 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:

  1. Fix invalid HTML
  2. Convey required checkboxes element correctly to assistive tech.

API changes

None.

Data model changes

none.

Comments

mfairchild365 created an issue. See original summary.

mfairchild365’s picture

Issue summary: View changes
andrewmacpherson’s picture

Issue tags: -a11y
StatusFileSize
new50.85 KB

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

Screenshot of multiple required checkboxes, with validation error for unchecked box

Removing a11y tag; accessibility is the preferred tag.

mfairchild365’s picture

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

andrewmacpherson’s picture

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.

There are some problems with this idea...

  • Many (most?) users complete form fields as they go along, but some users may take time to familiarize themselves with the form first. If all inputs in #checkboxes are initially marked as required, this could create confusing expectations of the form.
  • In particular, the required attribute is conveyed to assistive technology. Screen reader users will be told all individual checkboxes are required.
  • Dynamically removing the required attribute is like moving the goalposts during the game. ("Hang on, wasn't this required a moment ago?")
  • If JS is disabled (or fails to run for some other reason), the browser's native validation will still kick in - all checkboxes must be ticked. (Confirmed this in Firefox 58 by changing the javascript.enabled settign in about:config.)

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.

andrewmacpherson’s picture

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

andrewmacpherson’s picture

Issue summary: View changes
andrewmacpherson’s picture

Title: radios and checkboxes form elements do not have an accessible required attribute » FAPI #checkboxes form elements do not have an accessible required attribute
mfairchild365’s picture

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

andrewmacpherson’s picture

Issue summary: View changes

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

dpi’s picture

Title: FAPI #checkboxes form elements do not have an accessible required attribute » Checkboxes element missing "required" attribute
Version: 8.9.x-dev » 9.1.x-dev
Component: forms system » render system
Issue summary: View changes

Not sure where "#radios" / "#checkboxes" came from.

To be honest I think this issue and #2951317: Radios element missing "required" attribute should be merged.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

acbramley’s picture

Issue tags: +Bug Smash Initiative

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

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

kentr’s picture

After #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 fieldset and referenced by aria-describedby on the fieldset.

drupalite1411’s picture

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

kentr’s picture

@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 legend for checkboxes, so let's see where that goes first.

kentr’s picture

Fun fact: Core adds the text "(Required)" to the title attribute for the individual checkboxes in a checkboxes element.

If we end up not adding the required attribute to the individual checkboxes in a group based on #3, I think we should also remove that line that adds "(Required)" to the title attributes.

drupalite1411’s picture

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

mukeshaddweb’s picture

Status: Active » Needs review
StatusFileSize
new1.07 KB
on the <fieldset> tag <fieldset required="required" ...> HTML spec does not allow required on <fieldset> elements, causing W3C validation errors and accessibility issues for screen readers. fix: <fieldset aria-required="true" ...>


diff --git a/core/lib/Drupal/Core/Form/FormPreprocess.php b/core/lib/Drupal/Core/Form/FormPreprocess.php
index 2a22d44e4a7..e8f4d108bc8 100644
--- a/core/lib/Drupal/Core/Form/FormPreprocess.php
+++ b/core/lib/Drupal/Core/Form/FormPreprocess.php
@@ -297,6 +297,13 @@ public function preprocessFieldset(array &$variables): void {
     Element::setAttributes($element, ['id']);
     RenderElementBase::setAttributes($element);
     $variables['attributes'] = $element['#attributes'] ?? [];
+    if (isset($variables['attributes']['required'])) {
+      unset($variables['attributes']['required']);
+      $variables['attributes']['aria-required'] = 'true';
+    }
     $variables['prefix'] = $element['#field_prefix'] ?? NULL;
     $variables['suffix'] = $element['#field_suffix'] ?? NULL;
     $variables['title_display'] = $element['#title_display'] ?? NULL;

kentr’s picture

Issue summary: View changes
Status: Needs review » Active
Issue tags: +Needs accessibility review
Related issues: +#3174459: [PP-1] W3C Validation: required attribute not allowed on fieldset tag

@mukeshaddweb Thanks for your contribution!

It looks like your patch exchanges the required attribute with aria-required="true" on fieldset elements in general.

There's a separate issue for removing the required attribute from fieldset elements in general: #3174459: [PP-1] W3C Validation: required attribute not allowed on fieldset tag . I believe that issue will cover checkboxes elements.

That issue has work on it and is a higher-level discussion, so for now I think we should do the work for fieldset there. I'm striking that component from the issue summary.

Also, a couple of other comments:

I'm flagging for accessibility review for a double-check if the team agrees with #3 about not adding the required attribute 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.

kentr’s picture

@drupalite1411:

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?

It will add a hidden text of " (required)" to the legend of the containing fieldset. I'm working on it now.

but it does not add literal (Required) text to each individual checkbox option in a checkboxes group.

I saw case where a literal "(Required)" text was added to the title attributes of the individual checkbox elements in the checkboxes group. There's even a test for it.

kentr’s picture

I didn't dig deeply enough and misinterpreted.

Core adds " (Required)" to the title attribute of the fieldset element and a wrapper div, not to the individual checkbox elements.

<fieldset data-drupal-selector="edit-form-checkboxes-title-attribute" title="Checkboxes test (Required)"
  id="edit-form-checkboxes-title-attribute--wrapper"
  class="fieldgroup form-composite required js-form-item form-item js-form-wrapper form-wrapper" required="required">
  <legend>
    <span class="fieldset-legend js-form-required form-required">Checkboxes test</span>
  </legend>
  <div class="fieldset-wrapper">
    <div id="edit-form-checkboxes-title-attribute" title="Checkboxes test (Required)" class="form-checkboxes">
      <div
        class="js-form-item form-item form-type-checkbox js-form-type-checkbox form-item-form-checkboxes-title-attribute-first-checkbox js-form-item-form-checkboxes-title-attribute-first-checkbox">
        <input data-drupal-selector="edit-form-checkboxes-title-attribute-first-checkbox" type="checkbox"
          id="edit-form-checkboxes-title-attribute-first-checkbox"
          name="form_checkboxes_title_attribute[first-checkbox]" value="first-checkbox" class="form-checkbox">

        <label for="edit-form-checkboxes-title-attribute-first-checkbox" class="option">First checkbox</label>
      </div>
      <div
        class="js-form-item form-item form-type-checkbox js-form-type-checkbox form-item-form-checkboxes-title-attribute-second-checkbox js-form-item-form-checkboxes-title-attribute-second-checkbox">
        <input data-drupal-selector="edit-form-checkboxes-title-attribute-second-checkbox" type="checkbox"
          id="edit-form-checkboxes-title-attribute-second-checkbox"
          name="form_checkboxes_title_attribute[second-checkbox]" value="second-checkbox" class="form-checkbox">

        <label for="edit-form-checkboxes-title-attribute-second-checkbox" class="option">Second checkbox</label>
      </div>
    </div>

  </div>
</fieldset>