Required attribute requirements

Introduced in HTML5, the required attribute conveys that a particular form control or grouping of controls must have a valid value on form submission. For some controls, a valid value may be a boolean (radios and checkboxes), a non-empty string (standard text fields or selects), or a specifically formatted entry (email, number, date, etc.).

Now it’s true that fully accessible client-side validation isn’t available across all browsers. This has led some to believe that the required attribute has poor accessibility support, and that we should be using aria-required instead. However, if you’re looking to indicate that a form control is required, the required attribute will do that quite well.

Breakdown of support

Testing with modern browsers and screen readers, across different devices and operating systems, the required attribute produces a “required” announcement for all tests, with the exception of using TalkBack with Firefox and Chrome on Android.

(Unfortunately, even using aria-required="true" on a form control did not help TalkBack announce it as “required.”)

Windows 10 with JAWS 18 to 2019, and NVDA 2018.4.1

Firefox (65) with JAWS and NVDA will announce a required field as “required” and “invalid” by default. If you add aria-invalid="false" to the element, Firefox will still announce “invalid” when the field is empty, until a valid entry has been made.

IE11 with JAWS and NVDA will announce a required field as “required” by default, but neither pairing will announce it as “invalid.” Using aria-invalid to communicate state will rectify the lack of a native “invalid” announcement.

Edge with JAWS announces “required” by default, but will not announce a required field as being “invalid.” Using aria-invalid to communicate state will rectify the lack of a native “invalid” announcement.

Chrome (72) with NVDA and JAWS, and Edge with NVDA and Narrator will announce a required field as “required” and “invalid” by default. Entering content into a field will negate the native “invalid” announcement, but sometimes prematurely. (For example, entering a single letter into an email field would still not pass proper validation.) If adding aria-invalid="false" to a field by default, “invalid” will not be announced.

macOS 10.14.2 with VoiceOver

Safari (12.0.2) will announce a required field as “required” by default. Correctly announcing a field as “invalid” will depend on the type. (For example, entering a single letter into an email field will not trigger an invalid state, but entering a letter into a number field will.) Using aria-invalid can help correctly convey state based on one’s validation script.

Chrome (71) and VoiceOver will announce a required field as “required” and “invalid” by default. Entering content into a field will negate the invalid announcement, but sometimes the field may still be invalid (e.g. entering a single letter into an email field). If adding aria-invalid="false" to an field by default, “invalid” will not be announced.

iOS 12.1.2 with VoiceOver

Safari will announce a required field as “required” by default. Using aria-invalid will allow for fields to be announced as “invalid” when necessary.

Android 8.1 with TalkBack 7.2

Neither Firefox (63.0.2) nor Chrome (70) will announce fields as “required” or if they contain invalid entries. Use of ARIA attributes does not help produce the expected announcements.

Review the tests yourself

If you want to play around with the markup that was used for testing, you can check out the following CodePens:

Properly announcing invalid state

While using aria-required isn’t necessary to help indicate the importance of a form control, there are still inconsistencies with the announcements of invalid states that developers will need to address. Fortunately, the attribute aria-invalid can be used to help with just that.

To stop form controls from announcing as invalid by default, one can add aria-invalid="false" to any necessary element.

<label for="n">
<input id="n" required aria-invalid="false">

When its time to mark a form control as invalid, the attribute’s value should be set to “true”. Ideally, a control should be set to invalid only after a required control has been focused, and then blurred, without a proper string being entered.

If writing a script to check for input validity while a control is still focused, flagging a control as invalid should only happen after a few seconds have passed without a keypress event being fired. People may find it frustrating, and potentially even confusing, if they receive feedback that their entry is invalid while they’re typing.

aria-invalid quirks:

Note, that even when setting a form control to aria-invalid="false", Firefox will ignore this attribute and screen readers will still announce “invalid” when focus first entering an empty required form control.

Providing context to invalid controls

To communicate why a control is being announced as invalid, form controls should have associated error messages to help people correct invalid entries. While we can’t, and often we don’t, rely on browser’s client-side validation to be accessible, there are ways to provide accessible inline error messages.

First, suppress the browser’s default validation messages by adding the novalidate attribute to the wrapping form element. Now you’ll be in complete control to implement a custom client-side validation script, and helpful messaging.

Next, for any required form controls that could need inline error messaging, provide each with an aria-describedby attribute, that points to an empty element in the document (ideally directly after the form control in the DOM). If the control becomes invalid, this element would then be populated with a concise error message. When the value of the form control is no longer invalid, the associated element should become empty, along with the control having its aria-invalid attribute removed, or set to “false”.

<label for="n">
<input id="n" required aria-describedby="n_msg" aria-invalid="true">
<span id="n_msg">Please enter your name.</span>

Feature or a bug?

Interestingly, when populating an element that is the target of aria-describedby, Chrome (specifically on Windows) will treat it as a live region. JAWS and NVDA will immediately announce the content that is populated into the element.

Android Firefox description gap

If using Firefox (64.0.2) on Android, you’ll find TalkBack doesn’t announce aria-describedby content when a form control is focused. This is not an issue with Chrome on Android. Bug filed for Firefox on Android.

Wrapping up

When using the required attribute on form controls, you’re going to want to keep the following in mind:

  • Excluding Android browsers paired with TalkBack, all other tested screen reader and browser pairings announce a form control as “required” when using the required attribute.
  • Use the novalidate attribute on a form to disable browsers’ client-side validation, and instead implement custom validation scripts and accessible error messaging.
  • To ensure most screen readers won’t default to announcing required form controls as invalid, use aria-invalid="false".
  • Update the aria-invalid attribute to “true” if the current value (or lack thereof) of a required control does not pass validation.
  • Use aria-describedby on required form controls to point to an element that will contain any necessary inline error messaging.
  • Be patient before marking a form control as invalid and displaying an inline error message. Ideally wait until the control has lost focus, or there has been an adequate delay in key presses.

And finally, perform your own testing! Modern browsers and screen readers are updated quite frequently. That means that it’s quite possible that support for features could be added without you even noticing. Or on the flip side, bugs or unique heuristics could be introduced, which you may also need to be accounted for.

Categories: Technical

About Scott O'Hara

Scott joined TPGi in 2017 (until 2021), bringing with him nearly two decades of experience working as a designer and front-developer for product companies and UX consulting agencies.


Patrick H. Lauke says:

A little side-note I’d add: if the validation happens server-side, and you’re then bouncing users back to the form with invalid fields marked with aria-invalid="true", it’s worth also removing that attribute dynamically once the user has changed anything relating to that field, as otherwise even if they correct their error, AT will still announce it as invalid, even though at that point it may well be valid (but we won’t know until it’s sent back to the server for validation). So, something like (using inline event handler here for compactness)

Scott O'Hara says:

Thank you for the note/addition Patrick!

Based on Patrick’s suggestion, and from a progressive enhancement standpoint, could those values be set on load by JavaScript? I’m imagining a situation where the aria-invalid attribute is generated server-side but if JavaScript was disabled or not loaded, like on a slow connection, it would lead to confusion if the attribute isn’t removed/value isn’t changed.

Is that worrying too much about an edge case here, or is there an approach that would be as accessible as possible by default without requiring JavaScript?

Scott O'Hara says:

Hey Garrett,

I’d definitely recommend that any ARIA attributes used to modify the announcements only be set to elements when JavaScript is enabled, exactly for the reason you mentioned. Without using aria-invalid, the fields would just fall back to the default announcements that each browser would expose.

Patrick Lauke says:

set them server-side to data-aria-invalid="true" or something, then on load change the attributes to just aria-invalid="true" … yeah, that could be done.

James Long says:

Hey Scott!

Wonderful checklist at the end – I’ll be checking that a few times. Out of interest, do you know how aria-describedby compares to aria-errormessage? Is it worth using one over the other?


Scott O'Hara says:

Hey James, thanks for reading.

Last I checked, which would be late 2017/early 2018, support for aria-errormessage wasn’t really there to make it a viable option. I’m unaware of support changing since last I checked, but seems like it might be due to give it another look.

James Long says:

Alas, I think the support still isn’t there. I managed to find a couple of tickets open for aria-errormessage support.

Seems like aria-describedby is still the better option. Thanks for replying 🙂

Oscar says:

The Firefox team fixed the bug with aria-describedby in Firefox 67 🙂