Redundantly Redundant a11y Accessibility
Let’s review 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, huh? For those of you who know some of the ins and outs of web accessibility, you may have run into a similar looking markup pattern at one point or another. And, if you’re like me (sorry) you may be screaming (aloud or internally. it varies).
“What’s wrong with this?”, you who have been told they need to make their form fields accessible, wonder aloud. Your cat hears you, but they provide no commentary. Firstly, because cats have very little knowledge about web accessibility. Secondly, cats don’t talk. And thirdly, and most importantly, even if a cat did know about web accessibility and could talk, they wouldn’t offer any advice to you. Because they are a cat. They like to watch things suffer.
So, 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 on Windows 10 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 repetative announcements in there. Maybe this is 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 redundantly verbose form field. Removing the aria-*
attributes would just mean that the native HTML itself would be the sole culprit of our aural quagmire.
Wait…. but isn’t semantic HTML supposed to be accessible by default?
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>
A 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 explicitly specify it.
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:
aria-label
oraria-labelledby
attributes<label>
elementtitle
attribute (if neither of the above two methods are used)placeholder
attribute (if none of the above three methods are used - also ew. this is a fallback for bad coding practices. don’t be bad.)
If a text field is provided a name via a <label>
element, or one of the aria-label
attributes, then title
and 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 <label>
, aria-label
, title
and placeholder
were all used, and all provided relatively similar content. That’s silly.
What this markup should have looked like is:
<label for="name">
Name <span>*</span>
</label>
<input id="name" autocomplete="name" ...>
The <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 already identified the item as a sandwich. People generally understand a sandwich is not footwear.
Continuing on the pathway of using common sense, we may now realize 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
, required
, aria-required=true
and aria-describedby
attributes.
Using aria-disabled
First, the 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
Next, regarding aria-required=true
and 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-label
aria-labelledby
aria-describedby
So, choose required
or 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 aria-required
or required
attribute, this visual identifier could be consistently hidden by adding an aria-hidden=true
to the containing <span>
.
Describing an invalid entry
Finally, the 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, this would have been an issue (lol. oh goodness that was a ridiculous sentence to type. a useful title
attribute… sigh).
Wrapping up
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.