ARIA: When HTML Isn't Enough
ARIA attributes change what a screen reader announces, never how an element behaves, so you use them only where HTML has no element or attribute for the job, such as naming an icon-only button.
Freelancer · HTML · Web Development · Updated
After this lesson you can
- Choose a native HTML element instead of ARIA when one does the job
- Name and describe an element with aria-label, aria-labelledby and aria-describedby
- Use aria-hidden, aria-expanded and aria-current to tell a screen reader what the HTML alone can't
Key takeaways
- The first rule of ARIA is to use a native HTML element when one does the job, because it brings its role, its name, its keyboard use and its state for free.
- ARIA changes only what's announced. role="button" on a div doesn't make it focusable or make Enter press it.
- aria-label gives a name in words, aria-labelledby borrows a name from other text on the page, and aria-describedby adds a description after the name.
- aria-hidden="true" hides something from screen readers only, so use it on decoration and never on anything that can take focus.
- aria-expanded says whether something that opens and closes is open, and aria-current="page" marks the link to the page you're on.
Open the HTML of almost any big website, or any component library a job will hand you, and you'll find attributes that start with aria-. Some of them make the page better for screen-reader users, and some make it worse. This lesson is about telling the two apart, so you can add ARIA where it helps, and take it out where it hurts.
ARIA is a name tag, not a superpower
Put on a name tag that says "Chef" and people will call you chef. It doesn't teach you to cook.
ARIA, short for Accessible Rich Internet Applications (said "AIR-ee-uh"), is a set of attributes that change what the accessibility tree says about an element: its role, its name and its state, the three things from What a Screen Reader Hears. That's all it changes. It doesn't change how the element looks or what it does.
This is why the people who wrote ARIA put a rule at the top of it, known as the first rule of ARIA: if there's a native HTML element or attribute that already does what you need, use it instead of adding ARIA. A "native" element is one that's part of HTML itself, such as <button>, <a> or <details>.
<!-- Native: a role, a name, Tab, Enter and Space, all built in -->
<button type="button">Show trailer</button>
<!-- ARIA on a div: announced as "Show trailer, button", but Tab skips it and Enter does nothing -->
<div role="button">Show trailer</div>
The role attribute, an ARIA role, tells the screen reader what an element is. On the <div>, it makes a promise the <div> can't keep: a screen-reader user hears "button", presses Enter, and nothing happens. To keep the promise, you'd add tabindex="0" and a script that listens for Enter and Space, and you'd have rebuilt a worse <button> by hand. The W3C's own guide to ARIA puts it plainly: no ARIA is better than bad ARIA.
Naming: aria-label and aria-labelledby
Sometimes the name a screen reader needs isn't on the screen. The Starlight Cinema's newsletter form ends with a button that shows only a paper-plane icon. Sighted people understand it; a screen reader says "button" and nothing else. HTML has no attribute for an invisible name, so this is a job for ARIA:
<button type="submit" aria-label="Sign up">
<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true">
<path d="M2 21 23 12 2 3v7l15 2-15 2z" fill="currentColor"/>
</svg>
</button>
aria-label gives an element its accessible name, in words you write. Now it's announced "Sign up, button".
aria-labelledby (read "aria labelled by") does the same job by pointing at other text on the page, by its id. Use it when the name is already visible somewhere, so the two can't drift apart:
<section aria-labelledby="trailer-heading">
<h2 id="trailer-heading">Watch the Trailer: Comet Riders</h2>
<!-- the video -->
</section>
Now the section is a landmark named "Watch the Trailer: Comet Riders", which a screen-reader user can jump to. When an element has both, aria-labelledby wins over aria-label, and both win over the element's own text.
Don't use them where HTML already has a way: a form field gets its name from a <label>, and a picture from its alt.
Describing: aria-describedby
A name says what something is. Sometimes people also need a hint: the extra sentence under a form field that says what to type. You met this in Hints and Error Messages Everyone Can Use. aria-describedby (read "aria described by") points at that hint by its id, and a screen reader reads it after the name:
<label for="promo">Promo code</label>
<input type="text" id="promo" name="promo" aria-describedby="promo-hint">
<p id="promo-hint">Eight letters and numbers, from the back of your ticket stub.</p>
Promo code, edit text, Eight letters and numbers, from the back of your ticket stub.
The label stays the name; the hint is the description. Both stay visible on the screen for everyone.
Hiding: aria-hidden
aria-hidden="true" takes an element, and everything inside it, out of the accessibility tree. It stays on the screen; screen readers skip it. That's right for decoration a sighted person ignores too, such as the icon inside the "Sign up" button above: without aria-hidden, some screen readers would try to read the drawing.
<p><span aria-hidden="true">★★★★☆</span> Rated 4 out of 5 by our members</p>
Without aria-hidden, a screen reader might read "black star, black star, black star, black star, white star". The words after it already say the rating.
Two warnings. aria-hidden doesn't hide anything from the eyes, so it's not a way to hide content you don't want. And never put it on something that can take focus, such as a link or a button: a keyboard user can still Tab to it, and a screen reader then says nothing at all.
States: aria-expanded and aria-current
Some states HTML can't show by itself.
aria-expanded says whether something that opens and closes is open right now. <details> does this on its own, so it needs no ARIA. But a menu button that a script opens and closes does:
<button type="button" aria-expanded="false">Menu</button>
Its script changes aria-expanded to "true" when the menu opens, and back to "false" when it closes, and a screen reader says "Menu, button, collapsed" or "expanded". (The JavaScript in Depth course shows how a script does that, if you want to go further.)
aria-current marks the one item in a set that's the current one. In a menu, aria-current="page" marks the link to the page you're on:
<nav>
<a href="index.html" aria-current="page">Showtimes</a>
<a href="tickets.html">Tickets</a>
</nav>
A sighted visitor sees the current page in bold or underlined; a screen reader says "Showtimes, link, current page".
Try it. Open a site you use, right-click its main menu and choose Inspect. Look for aria-current or aria-expanded in its HTML, then open and close the menu and watch the value change.
Check yourself
2 questions to try on the lesson page.
So what
ARIA is a precise tool for the few things HTML can't say, such as a name for an icon or whether a menu is open; reach for a native element first, and ARIA only when there isn't one.