Exposing Field Errors
This post is about exposing field errors programmatically. I have already shared some opinions (such as a caution about displaying messages below fields or avoiding default browser field validation), but this post dives into using ARIA to convey them to screen reader users.
With fields that produce error messages on blur, I compare two types of live regions along with aria-describedby
and aria-errormessage
. This post does not address whether or not it is ideal to validate fields on blur. You can find plenty of opinions elsewhere.
ARIA Bits
An intro / recap of the ARIA bits I use in my samples.
aria-describedby
Last year (April 2022) I wrote Accessible Description Exposure to compare how aria-describedby
on a link and button is presented to screen reader users across navigation methods. I encourage you to read it because I spent a lot of time on it. It may be useful to you as well. It was, of course, limited in scope.
I failed to note within the body of that post that Chromium browsers treat aria-describedby
as a live region. The Core AAM 1.2 specification section 4.8.1 State and Property Change Events lists aria-describedby
as one of the properties that should fire a change event if its value is updated.
aria-invalid
Set aria-invalid="true"
on a field that is in error, and consider removing it completely when the field has no error. At the risk of embedding an opinion on when fields should be marked invalid, this nugget for aria-invalid
is informative:
…if the user has not attempted to submit the form, authors SHOULD NOT set the
aria-invalid
attribute on required widgets simply because the user has not yet entered data.
aria-errormessage
The aria-errormessage
property references the id
(or multiple space-separated id
s) of a node with an error message. That error message will be conveyed as a flat string (so the structure of lists, links, and so on will not be conveyed).
This value will only be conveyed when aria-invalid="true"
is set on the field, and the author must hide the error message (from all users) until then.
aria-live
The aria-live
property is a signal that a node will get some sort of update and that the update should be conveyed to users — regardless of where the user’s current focus or virtual cursor is on the page. Essentially, this is how you make a screen reader talk to a user regardless of what they are doing.
You can set three values on the property. If you set it assertive
then you are telling the screen reader to present it to the user immediately, probably interrupting what they are doing and clearing the speech queue. Conversely, polite
tells it to wait until current announcements are over, which means it should not interrupt nor clear the queue. Finally, off
is the same as removing the property.
Examples
I made a form with a handful of fields. The intent is to compare how fields with the three ARIA properties identified above are exposed to screen reader users. Ideally as a designer, developer, tester, researcher, tenex whatever, you can use this to inform how you build inline field-level error handling for your audience.
I did not perform any tests with a Braille display. Please keep in mind that for NVDA, according to this 2017 open issue, #7756 Live Regions are Not Displayed in Braille. Otherwise, I encourage you to test at least in Braille emulators, about which you can learn more in my post JAWS, NVDA, and VoiceOver Braille Viewers.
For my examples (debug mode), each field has an onblur
function that sets aria-invalid="true"
on it. Each field also has an oninput
function that sets aria-invalid="false"
on it, so if you focus a field and type anything its error state is removed. The text for the error messages never changes and does not dynamically populate; I toggle it strictly with CSS visibility
.
See the Pen Testing aria-errormessage, aria-invalid, and aria-describedby Exposure with & without Live Regions by Adrian Roselli (@aardrian) on CodePen.
In addition to testing on blur, I also test how (or if) the error is conveyed when navigating to the field after the error has already been triggered. This is meant to reflect a user who may navigate away from the field and come back to it later as part of addressing errors.
Testing kit:
- Firefox 111 / NVDA 2023.1
- Chrome 111 / JAWS 2023
- Edge 111 / Narrator Win11
- Safari 16.4 / VoiceOver macOS 12.6.4
- Chrome 111 / TalkBack 13.1 / Android 13
- Safari / VoiceOver iPadOS 16.4
1. Assertive live region with aria-errormessage
<label for="Field01">
1. Assertive live region with <code>aria-errormessage</code>
</label>
<input id="Field01" type="text" aria-errormessage="Msg01" aria-invalid="false">
<span id="Msg01" aria-live="assertive">
<span>Field 1 error using assertive live region.</span>
</span>
Browser / Screen Reader | When triggered | When navigated |
---|---|---|
Firefox / NVDA | Announces error message onblur, before next field accName is announced. | Error message not announced when otherwise focusing / navigating by field. |
Chrome / JAWS | Announces error message onblur, after next field accName is announced. | Error message announced when focusing field, prepended with “Has error”; but error message not announced when navigating by field (E), only announces “Has error”. |
Edge / Narrator | Live region not honored. | Error message not announced when otherwise focusing / navigating by field. |
Safari / VoiceOver macOS | Live region not honored. | Error message not announced when otherwise focusing / navigating by field. |
Chrome / TalkBack | Live region not honored. | Error message not announced when otherwise focusing / navigating by field. |
Safari / VoiceOver iPadOS | Announces “Invalid data” onblur, eventually followed by the error message and interrupting next field accName announcement. | Error message is announced when otherwise focusing / navigating by field. |
2. Polite live region with aria-errormessage
<label for="Field02">
2. Polite live region with <code>aria-errormessage</code>
</label>
<input id="Field02" type="text" aria-errormessage="Msg02" aria-invalid="false">
<span id="Msg02" aria-live="polite">
<span>Field 2 error using polite live region.</span>
</span>
Browser / Screen Reader | When triggered | When navigated |
---|---|---|
Firefox / NVDA | Announces error message onblur, before next field accName is announced. | Error message not announced when otherwise focusing / navigating by field. |
Chrome / JAWS | Announces error message onblur, after next field accName is announced. | Error message announced when focusing field, prepended with “Has error”; but error message not announced when navigating by field (E), only announces “Has error”. |
Edge / Narrator | Live region not honored. | Error message not announced when otherwise focusing / navigating by field. |
Safari / VoiceOver macOS | Live region not honored. | Error message not announced when otherwise focusing / navigating by field. |
Chrome / TalkBack | Live region not honored. | Error message not announced when otherwise focusing / navigating by field. |
Safari / VoiceOver iPadOS | Announces “Invalid data” onblur, eventually followed by the error message and interrupting next field accName announcement. | Error message is announced when otherwise focusing / navigating by field. |
3. No live region with aria-errormessage
<label for="Field03">
3. No live region with <code>aria-errormessage</code>
</label>
<input id="Field03" type="text" aria-errormessage="Msg03" aria-invalid="false">
<span id="Msg03">
<span>Field 3 error with no live region.</span>
</span>
Browser / Screen Reader | When triggered | When navigated |
---|---|---|
Firefox / NVDA | No announcement. | Error message not announced when otherwise focusing / navigating by field. |
Chrome / JAWS | No announcement. | Error message announced when focusing field, prepended with “Has error”; but error message not announced when navigating by field (E), only announces “Has error”. |
Edge / Narrator | No announcement. | Error message not announced when otherwise focusing / navigating by field. |
Safari / VoiceOver macOS | No announcement. | Error message not announced when otherwise focusing / navigating by field. |
Chrome / TalkBack | No announcement. | Error message not announced when otherwise focusing / navigating by field. |
Safari / VoiceOver iPadOS | Announces “Invalid data” onblur, eventually followed by the error message and interrupting next field accName announcement. | Error message is announced when otherwise focusing / navigating by field. |
4. No live region, uses aria-describedby
with aria-errormessage
<label for="Field04">
4. No live region, uses <code>aria-describedby</code> with <code>aria-errormessage</code>
</label>
<input id="Field04" type="text" aria-errormessage="Msg04" aria-describedby="Msg04" aria-invalid="false">
<span id="Msg04">
<span>Field 4 error with no live region.</span>
</span>
Browser / Screen Reader | When triggered | When navigated |
---|---|---|
Firefox / NVDA | No announcement. | Error message is announced when otherwise focusing / navigating by field. |
Chrome / JAWS | Announces error message onblur, before next field accName is announced. | Error message announced twice when focusing field, prepended with “Has error”; error message announced when navigating by field (E), also announced “Has error”. |
Edge / Narrator | No announcement. | Error message is announced when otherwise focusing / navigating by field. |
Safari / VoiceOver macOS | No announcement. | Error message is announced when otherwise focusing / navigating by field. |
Chrome / TalkBack | No announcement. | Error message is announced when otherwise focusing / navigating by field. |
Safari / VoiceOver iPadOS | Announces “Invalid data” onblur, eventually followed by the error message and interrupting next field accName announcement. | Error message is announced when otherwise focusing / navigating by field. |
5. Assertive live region with aria-describedby
<label for="Field05">
5. Assertive live region with <code>aria-describedby</code>
</label>
<input id="Field05" type="text" aria-describedby="Msg05" aria-invalid="false">
<span id="Msg05" aria-live="assertive">
<span>Field 5 error using assertive live region.</span>
</span>
Browser / Screen Reader | When triggered | When navigated |
---|---|---|
Firefox / NVDA | Announces error message onblur, before next field accName is announced. | Error message is announced when otherwise focusing / navigating by field. |
Chrome / JAWS | Announces error message onblur, before next field accName is announced, then again after next field accName. | Error message is announced when otherwise focusing / navigating by field. |
Edge / Narrator | Live region not honored. | Error message is announced when otherwise focusing / navigating by field. |
Safari / VoiceOver macOS | Live region not honored. | Error message is announced when otherwise focusing / navigating by field. |
Chrome / TalkBack | Live region not honored. | Error message is announced when otherwise focusing / navigating by field. |
Safari / VoiceOver iPadOS | Announces “Invalid data” (not the error message) onblur, interrupting next field accName announcement | Error message is announced when otherwise focusing / navigating by field. |
6. Polite live region with aria-describedby
<label for="Field06">
6. Polite live region with <code>aria-describedby</code>
</label>
<input id="Field06" type="text" aria-describedby="Msg06" aria-invalid="false">
<span id="Msg06" aria-live="polite">
<span>Field 6 error using polite live region.</span>
</span>
Browser / Screen Reader | When triggered | When navigated |
---|---|---|
Firefox / NVDA | Announces error message onblur, before next field accName is announced. | Error message is announced when otherwise focusing / navigating by field. |
Chrome / JAWS | Announces error message onblur, before next field accName is announced, then again after next field accName. | Error message is announced when otherwise focusing / navigating by field. |
Edge / Narrator | Live region not honored. | Error message is announced when otherwise focusing / navigating by field. |
Safari / VoiceOver macOS | Live region not honored. | Error message is announced when otherwise focusing / navigating by field. |
Chrome / TalkBack | Live region not honored. | Error message is announced when otherwise focusing / navigating by field. |
Safari / VoiceOver iPadOS | Announces “Invalid data” (not the error message) onblur, interrupting next field accName announcement | Error message is announced when otherwise focusing / navigating by field. |
7. No live region with aria-describedby
<label for="Field07">
7. No live region with <code>aria-describedby</code>
</label>
<input id="Field07" type="text" aria-describedby="Msg07" aria-invalid="false">
<span id="Msg07">
<span>Field 7 error with no live region.</span>
</span>
Browser / Screen Reader | When triggered | When navigated |
---|---|---|
Firefox / NVDA | No announcement. | Error message is announced when otherwise focusing / navigating by field. |
Chrome / JAWS | Announces error message onblur, before next field accName is announced. | Error message is announced when otherwise focusing / navigating by field. |
Edge / Narrator | No announcement. | Error message is announced when otherwise focusing / navigating by field. |
Safari / VoiceOver macOS | No announcement. | Error message is announced when otherwise focusing / navigating by field. |
Chrome / TalkBack | No announcement. | Error message is announced when otherwise focusing / navigating by field. |
Safari / VoiceOver iPadOS | Announces “Invalid data” (not the error message) onblur, interrupting next field accName announcement | Error message is announced when otherwise focusing / navigating by field. |
Conclusions
These are some broad generalizations from the results above.
- Live regions:
- For the scope of this test where live regions are present,
assertive
andpolite
have no impact; - When a live region (or a de facto live region) is treated as
assertive
, the name of the subsequent field that just received focus is clipped or lost; - When a live region (or a de facto live region) is treated as
polite
, the name of the subsequent field that just received focus is followed by the error message.
- For the scope of this test where live regions are present,
aria-describedby
:- For the scope of this test, Chrome / JAWS treats
aria-describedby
as anassertive
live region; - For the scope of this test, Chrome / JAWS treats announces the live region and also the
aria-describedby
text, resulting in a double error message announcement; - Consistently exposed when navigating by fields (E or Caps Lock+Ctrl+Option+⌘+J versus Tab ).
- For the scope of this test, Chrome / JAWS treats
aria-errormessage
:- The message associated via
aria-errormessage
paired with a live region is generally not exposed onblur; - The message associated via
aria-errormessage
is generally not exposed when navigating through fields; - The virtual cursor is the only certain way to encounter a message associated using
aria-errormessage
.
- The message associated via
aria-invalid
:- Sometimes conveys a field is in error, but not with the message.
- Miscellaneous:
- VoiceOver on macOS continues to be completely divergent in behavior from VoiceOver on iDevices.
Video
I made a single video showing how the fields are exposed using JAWS with Chrome. I opted to do this since it seems to be the screen reader least commonly installed on tester and developer machines in my experience.
Wrap-up
This test is very narrow. It only uses a handful of browser and screen pairings, no Braille displays, and has a limited number of permutations of ARIA properties. However, you can see how quickly this can get complex to test.
Probably do not generalize these results, at least not if you have good data on your users — their configurations, preferences, expectations, skill levels, and so on. Broadly, however, be wary of error messages that announce when leaving a field if they clip the next field name (assertive
). If it does not clip the next field name (polite
), use a clear error message that associates it with the field in error.
Passably Related
As this post was going to, er, press, I got word that Google and Microsoft have spun up a pair of surveys to get feedback on live regions. One survey is meant for developers and the other is meant for screen reader users. Apparently Microogle are prototyping something called ariaNotify and the results of these surveys may inform their decisions. So if one (or both) of those surveys is appropriate to you, consider filling it out. Before April 14.
Again, before April 14.
5 Comments
I admit I’d be awfully curious how browser form validation compares to the methods above, (i.e. no live regions, ARIA-describedby, or aria-errormessage, just HTML5 form validation parameters like required, type, pattern.
While I 100% agree that today we can’t rely on browser form validation to be accessible, far from it, but if ultimately if we are to improve accessibility across the board we must stop making form validation so complex for developers. Ultimately browser vendors must up their game.
I guess it’s just a matter of continuing to file bugs and otherwise nag at them. ;)
In response to .The second link of the post (first paragraph) points out why I think we need to keep avoiding default browser field validation, with videos demonstrating problems with screen readers and updates showing how the messages do not scale well. Mostly browsers are ignoring the bugs that are already filed.
Hi Adrian,
Really nice article. I am building a validation library focused on Accessibility and following a pattern:
– Validation starts after form submittion.
– If there are any error in the fields (empty required fields, mistypes of information or wrong format), a banner telling the user that there are issues to be solved will appear and inputs with a class chosen by the developer will be apply, plus an error message will appear.
– Inputs are handling the error messages via “aria-describedby” attributes.
– User can correct the errors and the form field will validate on input or change event.
– If the field is ok, error message will disappear. “Aria-describedby” attribute, if it was present in the field previous validation, it will be respected with the previous values, on the contrary it will be erased.
– On resubmitting the form, error banner will dissappear and a loading icon with a legend (visible or not) will appear so the user know that a sending process started.My question is : based on the pattern I am using, should I change aria-describedby to aria-errormessage for the error messages?
Best regards,
Ezequiel
In response to .Ezequiel, given the support I identify in 3. No live region with
aria-errormessage
and in the Conclusions section, changing your instances ofaria-describedby
toaria-errormessage
seems like it will make it harder for users in your scenario to get to the message itself.
Thanks a lot Adrian!
Leave a Comment or Response