In short
ARIA is a set of HTML attributes that give extra information to screen readers when normal HTML is not enough. In this lesson you will learn ARIA roles, labels and states, and when you should not use them. What is ARIA and what is the first rule of ARIA?
ARIA is a set of HTML attributes that give extra information to screen readers when normal HTML is not enough. In this lesson you will learn ARIA roles, labels and states, and when you should not use them.
What is ARIA and what is the first rule of ARIA?
ARIA stands for Accessible Rich Internet Applications. It is a set of attributes that start with aria- plus the role attribute. ARIA changes what a screen reader announces. It does not change how the element looks or behaves. A <div role="button"> is still a <div>: it cannot get keyboard focus and does not react to the Enter key.
That is why the first rule of ARIA is: if a native HTML element already does the job, use it instead of ARIA. A real <button> is always better than <div role="button">. People who build accessibility tools often say "No ARIA is better than bad ARIA", because wrong ARIA can make a page worse than no ARIA at all.
| Instead of this | Use this |
|---|---|
<div role="button"> | <button> |
<span role="link"> | <a href="..."> |
<div role="navigation"> | <nav> |
<div role="heading" aria-level="2"> | <h2> |
<div role="checkbox"> | <input type="checkbox"> |
ARIA labels: aria-label, aria-labelledby and aria-describedby
Every interactive element needs an accessible name, which is what a screen reader says when the user reaches it. Usually the name comes from visible text, like the text inside a <button> or a <label> for an input. When there is no visible text, ARIA can help.
aria-labelgives a name directly as text. Use it for icon-only buttons.aria-labelledbypoints to theidof another element whose text should be the name.aria-describedbypoints to extra help text that is read after the name, like a hint.
<!-- Icon-only button: no visible text, so we add a name -->
<button type="button" aria-label="Close menu">
<svg width="24" height="24" aria-hidden="true">
<path d="M4 4 L20 20 M20 4 L4 20" stroke="currentColor" stroke-width="2"/>
</svg>
</button>
<!-- A section named by its own heading -->
<section aria-labelledby="reviews-title">
<h2 id="reviews-title">Customer reviews</h2>
<p>4.8 out of 5 from 320 reviews.</p>
</section>
<!-- A password field with a hint -->
<label for="password">Password</label>
<input type="password" id="password" aria-describedby="password-hint">
<p id="password-hint">At least 12 characters, with one number.</p>
The close button shows only an X icon, but a screen reader says "Close menu, button". The icon itself has aria-hidden="true" so it is not read twice. The section is announced as "Customer reviews, region". When the user reaches the password field they hear "Password, edit text, at least 12 characters, with one number".
Common mistake: Adding
aria-labelto a<div>or<span>that has no role. Screen readers often ignore labels on plain, non-interactive elements. Put labels on buttons, links, form fields, landmarks and other elements with a real role.
ARIA states: aria-expanded, aria-hidden and aria-live
A state is information that can change, like "open" or "closed". ARIA states let a screen reader know what is happening right now.
aria-expanded="true"or"false"tells the user whether a menu or panel controlled by a button is open.aria-hidden="true"hides an element from screen readers but keeps it visible on screen. Use it for decorative icons and duplicated content.aria-liveturns an area into a live region. When its text changes, the screen reader reads the new text without the user moving there.aria-live="polite"waits until the user is idle.aria-live="assertive"interrupts at once, so save it for urgent errors.
<button type="button" aria-expanded="false" aria-controls="mobile-menu">
Menu
</button>
<ul id="mobile-menu" hidden>
<li><a href="/shop">Shop</a></li>
<li><a href="/about">About</a></li>
</ul>
<p>
<span aria-hidden="true">★★★★☆</span>
<span class="visually-hidden">Rated 4 out of 5</span>
</p>
<div aria-live="polite" id="cart-status"></div>
The menu button tells screen readers "Menu, collapsed". Your JavaScript must change aria-expanded to "true" and remove hidden when the menu opens, because ARIA never does this for you. The star rating hides the star symbols (a screen reader would say "black star, black star...") and gives a text version instead, which you hide visually with CSS. When JavaScript writes "Added to cart" into #cart-status, the screen reader announces it politely.
Note: A live region must already exist in the page before its text changes. If you add the element and its text at the same moment, many screen readers will say nothing. For simple status messages you can also use
role="status", which works likearia-live="polite".
Good vs bad ARIA examples
<!-- Bad: fake button, needs ARIA, tabindex and JavaScript to work -->
<div role="button" onclick="save()">Save</div>
<!-- Good: real button, works with keyboard by default -->
<button type="button" onclick="save()">Save</button>
<!-- Bad: aria-hidden on something the user can focus -->
<a href="/help" aria-hidden="true">Help</a>
<!-- Bad: label repeats the role ("Search button, button") -->
<button aria-label="Search button">Search</button>
<!-- Good: visible text is already the name -->
<button>Search</button>
Never put aria-hidden="true" on something a keyboard user can reach: they would land on an element that the screen reader says nothing about. And don't add the word "button" or "link" to labels, because the screen reader already says the role.
Practice exercise
Build a "Like" button that shows only a heart icon. Give it an accessible name, and add aria-pressed="false" so screen readers know it is a toggle. Then add an empty live region under it where a message like "Added to favourites" could appear.
Hint: aria-pressed works like aria-expanded, but for on/off buttons. Hide the heart icon itself with aria-hidden="true".
Key takeaways
- ARIA changes what screen readers announce, not how elements behave.
- First rule of ARIA: use a native HTML element if one exists.
aria-label,aria-labelledbyandaria-describedbygive names and descriptions.aria-expanded,aria-hiddenandaria-liveshare state and changes with screen reader users.- Bad ARIA is worse than no ARIA, so add it carefully and test it.