Redundantly Redundant a11y Accessibility
Let’s take a look at the following markup snippet, shall we?
<label for="name"> Name <span>*</span> </label> <input type="text" id="name" role="textbox" aria-disabled="false" required aria-required="true" aria-label="Enter your name, required" placeholder="Your name" title="Enter your Name" aria-describedby="msg" ...> <div id="msg" class="display-none"> This field is required </div>
Quite a few attributes there, wouldn’t you say? For those of you well versed in accessibility, you have likely run into a similar looking markup pattern. And, you may see all the ARIA attributes here and immediately cry foul.
Now “why might that be?”, you who has been told they need to make their form fields accessible wonders aloud. Your cat hears you, but they provide no commentary. Firstly, because cats have very little knowledge about web accessibility. Second, cats don’t talk. And third, and most importantly, because even if a cat did know about web accessibility and could talk, they wouldn’t offer any advice to you anyway. Because they are a cat.
Since the cat will never be of any help, ever, let’s fire up macOS’s built-in screen reader, VoiceOver (because let’s face it, you’re using a mac laptop right now, aren’t you?). With VoiceOver on, navigating to this field will announce the following:
“Enter your name, required, Your name, required, edit text. (pause) This field is required.”
For comparative purposes, here’s what NVDA announces when navigating to the field:
“Enter your name, required. edit. required. invalid entry… This field is required. Your name.”
There sure are a lot of repeat announcements in there. This must be why people say “the first rule of ARIA is don’t use ARIA”. But let’s be clear, even removing all the ARIA from the above snippet wouldn’t solve our overly verbose and redundant form field. Removing the
aria-* attributes would just mean that the native HTML itself would be the sole culprit of our aural quagmire.
Breaking down to the essentials
Please note: I am not talking about form validation here, or the subject of live regions and their potential use to announce error messages for invalid form controls. That is not the purpose of this particular post. For some information on required/invalid states, you can read required attribute requirements. But again, that too is an article that has a specific purpose which may still not cover all of your questions about live regions and validation.
To understand why the form field is so chatty, we need to understand how the element and its attributes are exposed to assistive technologies, and what people actually need to know about the field to be able to interact with it.
Know your role
First, as we need users to be able to enter a string of text (in this case their name), we would use the
<input> element. An
<input> defaults to a
type=text, regardless of whether the
type attribute is specified or not.
<input> <!-- or --> <input type=text>
role=textbox can be used on HTML elements to override the announcement of their native semantics, allowing that element to be announced as an “edit” or “edit text” field. However, as an
<input> implicitly conveys this role, there is no need to specify it explicitly.
A name is not a sentence
But a role alone does not provide enough information on what this particular field is looking for a user to enter. This is where labeling / naming a field comes into play.
A text field can be named via one of the following methods:
titleattribute (if neither of the above two methods are used)
placeholderattribute (if none of the above three methods are used)
If a text field is provided a name via a
<label> element, or one of the
aria-label attributes, then
placeholder attributes will shift in their responsibilities. The
title providing a “description” for the field (more on this later), and the
placeholder providing an “entry hint”.
Regarding this particular markup snippet, the
placeholder were all used, and all provided relatively similar content. This is pretty silly.
What this markup should have looked like is:
<label for="name"> Name <span>*</span> </label> <input id="name" autocomplete="name" ...>
<label> provides the concise accessible name for this text field - indicating the expected entry for the user is their “name”. (Note: being a text field that expects the name of the user filling out the form, an
autocomplete="name" would also be expected to satisfy WCAG 1.3.5 Identify Input Purpose).
The use of
aria-label="Enter your name, required" in the original snippet was unnecessary as there was already a concise
<label> that provided the name to the text field. Additionally, the
aria-label was doing more than just “naming” the field. It was adding unnecessary “instructions” such as “Enter your”, and indicating state (“required”). Being that the element is a text field, it is already understood that one would “enter” data into it. Much like if you were to give someone a sandwich, you would not say to them “here is a sandwich for you to shove into your mouth and chew.” The mouth shoving and chewing is understood, as you have indicated that the item in question is a sandwich.
This then shows us how unnecessarily redundant the
title="Enter your Name" and
placeholder="Your name" attributes are. If we nix those from our text field, we will have cut out one of our extra “Your name” announcements, and gotten rid of the unnecessary native tooltip that appears on mouse hover.
The remaining attributes
That still leaves us with
aria-disabled=false attribute does nothing in this situation. A text field can either be enabled (default state) or disabled, meaning it cannot be interacted with by users (and if natively disabled it will not submit its data upon form entry). Specifying
aria-disabled=false is the same as if the attribute was not there… there’s no use case I can think of where this attribute/value would ever need to be explicitly declared.
Declaring a required state
required, you only need one of these. Both attributes will expose the text field as being “required”. In reality, using the attributes together means that HTML
required attribute would win out over the ARIA attribute, per ARIA’s rules concerning Conflicts with Host Language Semantics.
The reason the native HTML attribute overrides the ARIA version is due to the following reason:
When a host language declares a WAI-ARIA attribute to be in direct semantic conflict with a native attribute for a given element, user agents MUST ignore the WAI-ARIA attribute and instead use the host language attribute with the same implicit semantic.
The only ARIA attributes which are exempt from being ignored by similar HTML features are:
aria-required=true to indicate the field must be completed by the user. You don’t need both. Read this post for more information on which required attribute to choose.
Note that this also means we can clean up our label a bit, specifically the
<span>*</span>. While the asterisk is commonly used to visually indicate a required field in a form, the text character itself can be announced by a screen reader in different ways, if announced at all. Since the field will already be announced as “required” due to the use of the
required attribute, this visual identifier could be consistently hidden by adding an
aria-hidden=true to the containing
Describing an invalid entry
aria-describedby in this situation is used to point to where a validation message would display. However, in this case the message is persistently in the DOM (though the container is set to
display: none until it is needed). This, however, poses an issue as
aria-describedby can still obtain a description from an element’s descendant text, even if that element is not presently rendered in the DOM.
So, to mitigate against this error message being constantly announced, the message area needs to be empty when its associated form field is not in error. OR, the
aria-describedby attribute needs to only be added to the
<input> element when the text field is in the invalid state.
Note: to differentiate this error message from a general descriptive text string, and to help ensure it does not potentially fail WCAG 1.4.1 Use of Color (by using the color red, alone, to indicate this text represents an error message), the text “Error:” has been added to the start of the message.
Note 2: because
aria-describedby takes precedent over
title attribute, the
title that was specified on the initial snippet would not end up being announced at all. If this were to have been an actually useful instance of a
title attribute (rare to find one of those), this would have been an issue.
After giving the initial markup snippet a good scrub, getting rid of all the unnecessary attributes, the resulting markup would resemble the following:
<label for="name"> Name <span aria-hidden="true">*</span> </label> <input id="name" aria-required="true" aria-describedby="msg" autocomplete="name"> <div id="msg"> <!-- the following is only added to the DOM when needed --> Error: This field is required </div>
VoiceOver would announce the above as “Name, required, edit text” (and if in error would only then add “Error: this field is required” as its description).
NVDA would announce “Name, edit, required, invalid entry has autocomplete”. Also, if found to be invalid, the error description would then be announced. Other PC screen readers would announce similarly.
What I hope you take from this is that it’s not just unnecessary ARIA that can make web content overly verbose and redundant. Rather, unnecessary and misused HTML can make for awful aural experiences as well. Being percise with our markup, and knowing how each element and attriube we specifiy will contribute to the way an element is exposed to assistive technologies is impariative in creating accessible and inclusive user experiences.