WAI-ARIA Compliant, Accessibile Form Validation: How To Do It Simple and Well

I’ve been building accessible websites for a number of years now and one thing I’ve learned is that when it comes to accessibility, there’s always more than one way to achieve perfection and form inputs are a prime example of this! However, after much trial and error in the past, if I could build a form input anyway I want without framework, project or design constraints, here’s my optimal way to markup an input with accessibility in mind:

The features we need to make it accessible:

  • Label with “for” attribute, id on input to match
  • Use aria-invalid=”false” (changed to true when invalid)
  • If field needs a hint, use aria-describedby=”field-hint”
  • When in error, use aria-describedby=”error-message”
  • If field is required, a “required” attribute

A code example:

A field without errors

<label for=”my-input”>My field input</label>
<input type=”text” id=”my-input” aria-invalid="false" aria-describedby=”my-input-hint” required>
<div style="display:none;" id=”my-input-error” aria-hidden="true">Text for input hint description, can be any HTML</div>
<div id=”my-input-hint”>Text for input hint description, can be any HTML</div>

A field with errors

<label for=”my-input”>My field input</label>
<input type=”text” id=”my-input” aria-invalid="true" aria-describedby=”my-input-errors my-input-hint” required>
<div style="display:block;" id=”my-input-error” aria-hidden="false">Text for input hint description, can be any HTML</div>
<div id=”my-input-hint”>Text for input hint description, can be any HTML</div>

The details explained

Label contains “for” attribute, with matching id on the input
This associates it with the unique matching input ID. This will then be read out by screen readers when the user tabs to or clicks on the input.

aria-invalid=”false” (changed to true when invalid).
The screen reader will announce that the field is in error when you have aria-invalid=”true”.

If field has a hint, associate it with the input using the aria-describedby attribute that matches it’s ID. You can have multiple field IDs being associated with a single aria-describedby, by separating each one with a space. For example: aria-describedby=”field1 field2". Note: They will be read out in the order they are listed and when the field is in error, the error message should be read out before the hint, so list it first.

The id associated to the error field should only be added to the aria-describedby when the field is in error.

A “required” attribute
This will ensure that the field is read out as required by screen readers and assistive software.

style=”display:none” and “display:”block”
This is to visually show and hide your error message and can be a class performing the same action. NOTE: It’s best to use the CSS display property to hide content and not position or any other CSS rule as the screen reader may still read the text out.

Some extra DOs and DON’Ts…


  • Use aria-invalid=”false” (changed to true when invalid). Associate errors and hints for form inputs using the above aria-describedby way and NOT aria-labelledby, etc, even though it’s possible to do so. Although the latter will work, aria-describedby is the standard way of doing things and will be a recognisable pattern for users of assistive software.
  • Keep your label text visible and next to the input, not inside it. This was a popular design pattern, but when a user clicks or focuses inside the input, the context is now lost, which can be difficult for those with cognitive disabilities.


  • Worry about hiding text from screen readers, and vice versa. For example if you’ve got an error message, no need to create a hidden version that reads out something verbose at the start like “this is an error message…”. With the correct markup, this should all be taken care of without having to do extra work and therefore worry about updating multiple text regions in the future. Users of assistive software are generally “power users” of the web and know how to navigate fields in error, etc, so long as they are marked up correctly.