Needs work
Project:
Drupal core
Version:
main
Component:
forms system
Priority:
Normal
Category:
Feature request
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
23 Sep 2019 at 15:03 UTC
Updated:
7 May 2026 at 07:47 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
mgiffordCan you provide a sample of code with this and some instructions to repeat this?
We'll also want to know it's a core issue and not a webform issue.
Thanks!
Comment #3
lus commentedComment #4
lus commentedComment #5
andrewmacpherson commentedThanks for filing this @Lus. Adding IDs to error messages is a good idea. It already occurred to me that we can programmatically associate error messages to their inputs. I've been meaning to file a feature request for it myself but never got around to it. Lets flesh this out into a plan.
There are 3 ways of doing this:
aria-describedby. This has wide support, but isn't always treated as important by assistive tech (depends on user settings in some cases). Since inputs might have an associated error message AND some help text, it's probably a good idea to get the IDs in order so the error message is announced first. That's because when aria-describedby has multiple IDREFs, they are concatenated into a single string. Putting the error message first should reduce the likelihood of it being skipped.aria-errormessage, introduced by the ARIA 1.1 recommendation. This doesn't have as wide support yet, but has the advantage of better semantics. So assistive tech (in theory) can treat error messages as things to be announced even if users don't want to hear descriptions.aria-describedbyandaria-errormessage. The first points at the error text and the description, and the second just points at the error. I've read encouraging things about this approach as a short-term approach. It works very well, at the cost of some extra verbosity in browser/screenreader combinations which support both properties. Later when support foraria-errormessageis more widespread, we can change to keeping the description and error separated.Bonus points if we can make this work regardless of whether inline form errors module is enables. Programmatic associations like this don't depend on DOM order. It will be great if screen reader users can get the error message even though it's far away at the top of the page.
Comment #6
andrewmacpherson commentedUpdating the vague title.
Also, this probably isn't specific to webform or inline form errors module. It'll probably be best to make it work at lower level in Drupal's Form API.
Comment #7
lus commentedWell, I meant in particular the inline form errors, since the drupal Error messages (at the top) are in fact a list of errors in a form of anchor links. So we would add an ID for every

<li>?I have managed to add the ID to the aria-describedby but I'm stuck with the inline form error, since the module adds a container (with a class) and a

<strong>around the error in the template and not in the module variable.(Screenshot with inputs aria-describedby pointing on the inline form error added by me in the FormValidator)
So I wanted to make it this way:
Then delete the container and
<strong>from the templates, unless theres is an other solution possible ?Comment #9
andrewmacpherson commentedWell, I meant in particular the inline form errors, since the drupal Error messages (at the top) are in fact a list of errors in a form of anchor links. So we would add an ID for every <li> ?You only get the list of anchor links when the Inline Form Errors module is enabled. That's not what I meant.
When Inline Form Errors module is disabled, you actually get the error messages at the top of the page.
So what I meant is, the error message can be associated by IDREF whether it is near the form element, or far away at the top of the page.
I'll give your code a closer look soon.
Comment #10
lus commentedYe I have misunderstood your comment. Here is a patch that work at the lower level - in Drupal core FormErrorHandler. It adds to the form input fields an attribute
aria-describedbyand creates the list of messages but this time with an id on the<li>elements.Comment #11
lus commentedComment #13
lus commentedComment #14
lus commentedComment #15
lus commentedComment #16
lus commentedI'm bad at adding patches ...
Comment #17
lus commentedComment #18
brunorios1 commented@Lus,
I got this error:
Error: Call to a member function renderPlain() on null in Drupal\Core\Form\FormErrorHandler->displayErrorMessages() (line 99 of /home/myuser/www/myproject/web/core/lib/Drupal/Core/Form/FormErrorHandler.php)
Thanks.
Comment #19
lus commentedHi @brunorios1,
Could you please specify when you had this error, was it when you submitted a simple Drupal Web form? Was the Inline Form Errors module activated ?
Comment #20
brunorios1 commentedHi @Lus,
Yes, a Webform with Ajax enabled.
And yes, Inline Form Errors module is activated.
Comment #21
lus commented@brunorios1 I made some manually tests but I can't see the same error, however the Drupal Tests are not passing by.
Comment #22
lus commentedFix constructor error.
Comment #24
lus commentedRe-test.
Comment #25
lus commentedComment #26
andrewmacpherson commentedA contrib module is trying this too: A11Y: Form helpers.
It's worth studying, but it assumes Inline Form Errors module is enabled. This core issue should still seek to make the IDREFs work regardless of whether the Inline Form Errors module is enabled.
@Lus - Thanks for working on this. I see many more patches have been posted since I was last here. I haven't tested or studied them in depth, but will try to do that soon.
Do you know how to create an interdiff? Those help reviewers follow the progress of a patch as it evolves.
I skimmed the latest patch from #24....
I like the idea of using
#wrapper_attributes, because that's flexible enough for any wrapper.Nitpick: we are talking about errors, so maybe the ID suffix would be better as
'--error-message'. It would make the markup easier to understand.This looks fragile. Remember that the aria-describedby attribute accepts an ID reference list. In other words, it can point to multiple IDs. So it should be treated as an array of values, and we are adding an additional IDREF.
A majority of our inputs will already have another IDREF pointing to the description. See
Drupal\Core\Form\FormBuilder::doBuildForm()for this. We don't want to over-write the description IDREF.What we're aiming for is something like this:
aria-describedby="edit-foo--error-message edit-foo--description". The intended effect is that screen readers will announce the error message AND still announce the extra help description for the input. The error message is the most important, so that comes first. We'll need something like this:array_unshift($elements['#attributes']['aria-describedby'], $elements['#id'] . '--error-message';A snag is that the existing logic in
Drupal\Core\Form\FormBuilder::doBuildForm()also makes this mistake, and assumes there will only be a single IDREF value. I'll file a separate issue about that, because it's a wider problem than error messages IDs.Comment #27
andrewmacpherson commentedComment #28
neelam_wadhwani commentedComment #29
neelam_wadhwani commentedI have updated patch.
Kindly review patch.
Comment #30
neelam_wadhwani commentedComment #32
aleevasTrying to fix latest patch
Comment #34
vsujeetkumar commentedFix more test cases, Please review.
Comment #36
tim bozeman commentedOne hard thing is going to be multi-value fields. Errors on sub-items like street name or telephone number are set on the address or telephone parent elements. I don't think putting the aria-describedby on the parent elements is going to be useful for screen readers and it's hard to discern which child element the error applies to.
Comment #37
andrewmacpherson commentedYes, there are certainly going to be special cases for some kinds of complex Form API elements. Address, image fields, various date/time fields, and perhaps Form API
'#type' => '#checkboxes'. There is already some special-case code for some of these to manage labels and descriptions."Multi-value" fields are different from these.
Comment #38
andrewmacpherson commentedComment #41
liam morlandI have created an issue fork. I started with the patch in #34. I made the new
idvalues both end in--error-message(one was still--status-message). I made it usearia-errormessageinstead ofaria-describedby. I have it setaria-invalidwherearia-errormessageis set (this was already being set elsewhere later, but it makes sense to ensure that both are set together).In testing from Webform, it still doesn't set the
--error-messagein theidof the error. This will probably require a change toform-element.html.twig.Comment #43
joachim commentedThese comments about DI aren't necessary.
We've lost this comment about setting an error message.
Comment #44
ankithashettyRe-rolled the patch in #34, addressed the changes specified in #43 and added a missing
use Symfony\Component\DependencyInjection\ContainerInterface;statement incore/lib/Drupal/Core/Form/FormErrorHandler.phpfile.Thank you!
Comment #45
joachim commentedThanks!
Comment #47
liam morlandUpdated merge request with changes in #44.
Comment #48
tostinni commentedI'm reviewing this issue (both path #44 and MR1063) and I see correctly the new
aria-describedbyoraria-errormessagebut they link to non-existent ID.The error message only has this content:
Is there something that needs to be set up ?
Comment #49
alexpottTHere's been loads of activity since the rtbc in #45 so this issue can't really be RTBC as this activity has not been reviewed.
Plus I've just added some more comments to the code.
The issue summary could do with an update to outline the problem and how it is being fixed.
The MR also seems light on test coverage - I would expect some test coverage of the new HTML that is being output.
Comment #52
sukanya.ramakrishnan commentedThanks for the patch, but i am unable to see the id for the error message after applying this patch (aria-described-by attribute is added correctly though to the element that didnt validate correctly). Am I missing anything here?
Comment #54
mgiffordTagging for 3.3.1.
Comment #57
neclimdulRebased and address the feedback from alex.
Unfortunately this doesn't work. It was really broken and the ID attachement didn't seem to work at all. I fixed that but it only works for simple elements. When trying to attach to groups like radios or checkboxes, the ID in displayErrorMessages is the collection, but the ID in setElementErrorsFromFormState is the ID of the specific input and these don't match.
For example testing on umami search,
but the element is
referencing
Definitely needs a lot of tests.
Comment #59
neclimdulgot some tests in place that identify some of the pending issues. not passing because we've got some hard problems to solves still.
Comment #60
liam morlandI have rebased both merge requests.
Comment #64
kentr commentedI rebased !1063.
What's the difference between the two MRs? It looks like they've both been kept up-to-date.
Comment #65
kentr commentedComment #67
minoroffense commentedUpdated 7080 to fix the merge conflict in the test
And as for the differences between the branches looks like 7080 takes care of template changes and other items required for the new attributes to work. The other has the base changes but not template changes.
Comment #69
joshahubbers commentedIn 1063 i added the aria-errormessage in RenderElementBase too, because I wasn't getting it in de form output else.
Comment #70
joarferme commentedThe patch works, but it shows two duplicate error messages in my theme.
Comment #73
oily commentedHid 2 of 3 of visible branches. The one I have left visible (active one) contains the most changed files. So I assume it built on the other MR which I have hidden. The '11.x' MR contained no changes so seems like it should definitely be hidden.
Comment #74
oily commentedSeem to have fixed one PHPSTAN/ PHPCS error. There is still one PHPSTAN error:
https://git.drupalcode.org/issue/drupal-3083103/-/jobs/9376769
Comment #76
sasanikolic commentedI just created MR from branch
3083103-improve-error-accessibilityagainst3083103-programmatically-associate-error-11with improvements to the current patch. The changes address three areas:<div>wrappers — fieldset and details had no class at all, and the id was built directly fromelement['#id']in Twig rather than passed from preprocess. NowFormPreprocesspasseserror_idas a template variable, all three templates usecreate_attribute()with consistentform-item--error-messageclass, and errors are no longer blindly suppressed.form-elementplaced them after. Standardized all three to render errors after children, before the description.aria-errormessagealone is not sufficient — VoiceOver+Safari and TalkBack don't support it. Addedaria-describedbyas a fallback (with deduplication to avoid duplicates when bothFormErrorHandlerandRenderElementBaseset it). Also stripsaria-errormessageandaria-invalidfrom child radios/checkboxes to prevent invalid attributes on individual inputs. Introduced#error_idproperty so elements can override the default ID convention. FixeddisplayErrorMessages()to use a proper render array (#type container) instead of raw#prefix/#suffixHTML.renderPlainbut the code callsrenderInIsolation, and the Prophecy renderer wasn't initialized insetUp().Comment #77
rkollerthank you! could you please also create a MR for the feature branch (3083103-improve-error-accessibility) you've created for your changes so it could become testable?
Comment #78
smustgrave commentedIs this not what inline form errors does?
Comment #79
kentr commented@smustgrave
I tested IFE for this yesterday.
My observation was that IFE doesn't currently link invalid fields to error messages with
aria-describedby, though it was supposed to.I saw that the MR here touches IFE tests, so am just about to test it. Postponing until there's more data.
Comment #80
kentr commentedHere's what I got for
requirederrors with a hacked version of theform_testmodule's#requiredtest form.Test cases
Note: I merged
maininto the MR for these tests (locally only; did not push to gitlab).main, without Inline Form Errorsmain, with Inline Form ErrorsResult
So far, without Inline Form Errors, the MR appears to work as expected.
I still observed that Inline Form Errors doesn't do this currently, and AFAICT the MR fixes it. See the result details for the markup.
Discussion
Questions for the accessibility experts:
Do we want to use
aria-errormessageinstead ofaria-describedby?In #accessibility Slack yesterday, the recommendation was to use
aria-describedby:Adrian Roselli says this about
aria-errormessageas of Aug 15, 2023:If we do go with
aria-describedby, is it sufficient to prepend the error messageidtoaria-describedbyinstead of prepending the error message itself to the description text?Either way, the pipeline is failing. This also could use a rebase to bring in #1797438: HTML5 validation is preventing form submit and not fully accessible for easier manual testing.
With regard to Inline Form Errors, this might be blocked on #3557245: Use $element['#attributes]['id'] instead of $element['#id'] to create a link anchor. Not sure yet.
Result details
1. On
main, without Inline Form ErrorsPage-top errors markup
Field markup
2. With MR, without Inline Form Errors
Page-top errors markup
Field markup
3. On
main, with Inline Form ErrorsPage-top errors block
Field markup
4. With MR, with Inline Form Errors
Page-top errors markup
Field markup
Comment #82
smustgrave commentedI still think the fix should be in the inline_form_error module, especially since the test coverage is located there. Shouldn't add test coverage for a module we aren't fixing :)
Also by putting there we could do away with the service and template updates probably, or least change it to an attributes array so the module could dynamically add the ID without much twig change, which need a CR.