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 ids) 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:

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>
1. Assertive live region with aria-errormessage
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>
2. Polite live region with aria-errormessage
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>
3. No live region with aria-errormessage
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>
4. No live region, uses aria-describedby with aria-errormessage
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>
5. Assertive live region with aria-describedby
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>
6. Polite live region with aria-describedby
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>
7. No live region with aria-describedby
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.

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.

This shows tabbing through the page and then pressing E to put the virtual cursor on fields, all the while pointing to which piece of text on the screen is being spoken. Visually it shows how the announcement can jump around between announcing the new field and announcing the error on the previous field.

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

Reply

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. ;)

Birkir Gunnarsson; . Permalink
In response to Birkir Gunnarsson. Reply

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.

Reply

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 Lavaca. Reply

Ezequiel, given the support I identify in 3. No live region with aria-errormessage and in the Conclusions section, changing your instances of aria-describedby to aria-errormessage seems like it will make it harder for users in your scenario to get to the message itself.

Reply

Thanks a lot Adrian!

Leave a Comment or Response

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>