Error messages on forms

Question

We’re updating our error messaging styling. Currently the error messages float and it’s not clear which field they belong to for sighted users. For screen readers, it is currently working well.

Error messages pop up as soon as the user tabs out of a field. We want to move the error message ahead of the field. What's the implication of this styling switch on screen readers — and as the validation is triggered on exit of the field, would screen readers be able to read the error, and get them to restate the error?

Answer

For your situation, I’d follow a combination of the approaches described at https://hiddedevries.nl/en/blog/2017-04-04-how-to-make-error-messages-accessible and http://wet-boew.github.io/v4.0-ci/demos/formvalid/formvalid-en.html.

I’m suggesting the following features:

  1. Error summary at the top of the form listing all found errors.

  2. Error message added to the label for each form field in error.

  3. A polite live region (visually hidden and positioned somewhere after the form submit button) into which each error message is inserted when found.

    Note that any previous error messages in the live region should be removed before adding a new one, so that only the most recently found error is announced.You'll also want some trigger to fully empty the live region so that the screen reader user doesn’t happen upon it later — maybe any onblur event within the form could empty the live region, e.g. onblur -> empty live region, add any relevant error message?

  4. I’d recommend using the HTML required.

  5. I’d also recommend using the ARIA aria-invalid attribute. But I’d suggest treating invalid user input and no user input on required fields differently: invalid user input is expressly an error that can be checked as soon as the user leaves the field, but a required field left empty is only invalid upon form submission. Remember to remove aria-invalid or to set aria-invalid=false the moment the error is corrected. You can set aria-invalid=true on form submit for required fields left blank.

And here’s how I see it working:

    1. Onblur, validate the field, and if there’s an error (not just a blank field):

    2. Set aria-invalid=true on the field

    3. inject an appropriate message into an error summary at the top of the form

    4. inject an appropriate message into the field’s label

    5. clear the live region and inject an appropriate error message.

  1. If an error was corrected:

    1. Remove the message from the error summary

    2. Remove the message from the field’s label.

  2. On form submit, additionally validate the form for missing required fields:

    1. Set aria-invalid=true on any required fields left blank

    2. inject an appropriate message into an error summary at the top of the form

    3. inject an appropriate message into the field’s label

    4. move keyboard focus to the heading in the error summary.

In more detail:

Step 1. User enters invalid data to form field #1 and tabs to required form field #2, at which point the dynamic validation determines the error on form field #1 and sets aria-invalid=true on the form field.

The error summary at the top gets displayed and updated with a new error message. The same message is added to the field’s label, and inserted into the visually hidden live region, causing screen readers to announce it.

So both sighted and non-sighted users are informed of the error. Keyboard focus is on form field #2, but this gives the user the option to go back and fix it now or later, and does not steal focus from them.

Step 2. User now leaves the required form field #2 blank and tabs to form field #3. The aria-invalid=true is not set on the form field. The user also hasn’t tried to submit the form, so there’s no explicit error yet, and so no error message is inserted into the form field’s label, or into the summary error list at this point. But onblur the live region was emptied.

Step 3. The user enters invalid data into form field #3 and tabs to the submit button. The field is set with aria-invalid=true, and a new error message is inserted into the form field’s label, into the summary error list, and into the live region after first emptying the live region.

Step 4. The user returns to form field #1, corrects the error, and tabs out to form field #2. The validation confirms form field #1 is okay now, so aria-invalid=false is set (or the attribute removed), the error messages removed from the form field’s label, and from the error summary, and the live region is emptied.

Step 5. The user tabs leaves required form field #2 blank, and tabs to field #3. There’s no change, and no new messages are added to the live region.

Step 6. The user tabs to the form submit button and tries to submit the form. Validation confirms that required form field #2 is empty, and so in error. As such, aria-invalid=true is set on form field #2, and an appropriate message added to the error summary and field label. Form field #3 is still in error, but the relevant messages still exist in the field’s label and the error summary, so nothing to do. Focus is set to the heading in the error summary.

If you implement a live region as above, then there is no issue with the inline error messages being before the form field since they’ll be announced as they appear. However, the inline error message should be injected into the relevant field’s label so that when focus is set to the form field, the error is read as part of the label.

Last updated