Problem/Motivation
We make widespread use of the aria-describedby attribute to programmatically associate an HTML <input with the Form API #description. There are also some other uses of the aria-describedby attribute, to describe things like buttons and dialogs.
The WAI-ARIA recommendation defines the aria-labelledby aria-describedby attribute values as an ID reference list. In other words, these can point to multiple element IDs, like so:
aria-describedby="description-1 description-2"
When multliple IDREFs are used like this, the AccessibleName and AccessibleDescription (in the accessibility tree made available to assistive tech) are computed by concatenating all the referenced elements.
However, the forms system mistakenly assumes that there will only be a single IDREF in the aria-describedby attribute. FormBuilder::doBuildForm() sets up the element description like so:
// Add the aria-describedby attribute to associate the form control with its
$element['#attributes']['aria-describedby'] = $element['#id'] . '--description';
This makes it tricky to manipulate aria-describedby attribute properly in form alter hooks, and other areas of form rendering, without risking a conflict with something that another module might do.
Initial use case: We want to make use of multiple IDREFs in aria-describedby to associate form error messages with inputs, in addition to the existing FAPI descriptions. See #3083103: Programmatically associate error messages with inputs. We don't want to break existing #description associations.
However there are other possible uses for multiple IDREFs, so I'm filing this issue as a separate task.
Proposed resolution
Treat aria-labelledby and aria-describedby as arrays, to make handling multiple IDREFs much easier.
Remaining tasks
- Update
FormBuilder::doBuildForm()to use an array for aria-describedby. - Review other places where
aria-describedbyis used, and ensure they are also flexible enough to accept multiple IDREFs. - Review places where
aria-labelledbyis used, and ensure they are also flexible enough to accept multiple IDREFs. - Assess this for backwards compatibility.
User interface changes
None.
API changes
TBD.
Data model changes
None.
Comments
Comment #2
andrewmacpherson commentedTreating this as major, because the WCAG technique ARIA1: Using the aria-describedby property to provide a descriptive label for user interface controls relates to TWO success criteria at level A, and we already have a high-impact use case.
Comment #3
mgiffordIn the interim, it is good to look at this module and see what can be brought forward from it into Core https://www.drupal.org/project/a11y_form_helpers
Comment #4
andrewmacpherson commentedYeah, I've been aware of the a11y_form_helpers module for a while. That works around the problem by concatenating the IDREFs into one string, and overwriting the original value. That's a bit fragile, because any other module or theme might also want to overwrite the original value.
Comment #5
andrewmacpherson commentedGiven that many render elements will already be setting this to a string, it's probably worth putting a BC routine in the render pipeline somewhere, which checks if a string was passed, and wraps it up as an array with one item. That's a fairly trivial pattern, I think?
My first attempt at a backwards-compatibility review...
The
['#attributes']['aria-describedby']is currently treated as a string. If any existing sites and/or contrib modules wanted to change it, what approaches can they currently use?aria-describedbyto a different IDREF. Here the alter hook would do a brute force over-write of the string:$element['#attributes']['aria-describedby'] = "NEW_ID_REF";.aria-describedbyattribute that was passed into the Twig template. Something like<fieldset aria-describedby="my_new_idref" {{ attributes|without('aria-describedby') }}>aria-describedbyattribute in client-side JS.In all these cases, the site won't benefit from the change to an array, but I don't think they would break either. They're all brute-force over-writes, just in different places.
Are there any approaches I have missed?
Comment #6
andrewmacpherson commentedAll of this applies equally to
aria-labelledby. Updating issue scope.Note that we're not using
aria-errormessageyet, but that the ARIA 1.1 rec. says it takes a single IDREF, not multipleIDREFs. So that attribute is NOT in scope here.The full list of ARIA attributes (from ARIA 1.1) which are defined as an "ID reference list" is:
aria-controlsaria-describedbyaria-flowto(I don't think we use this yet)aria-labelledbyaria-ownsIt would be good to treat all of these as arrays in the render pipeline, like we currently do for the class attribute.