# 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 October 31, 2026

## 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>`.

```html
<!-- 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:

```html
<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:

```html
<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:

```html
<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>
```

```text
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.

```html
<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:

```html
<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:

```html
<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.

## Key terms

- [ARIA role](https://www.ask-codey.com/glossary/aria-role)
- [aria-label](https://www.ask-codey.com/glossary/aria-label)
- [aria-labelledby](https://www.ask-codey.com/glossary/aria-labelledby)
- [aria-describedby](https://www.ask-codey.com/glossary/aria-describedby)
- [aria-hidden](https://www.ask-codey.com/glossary/aria-hidden)

## Cheat sheets

- [ARIA: When HTML Isn't Enough](https://www.ask-codey.com/cheat-sheets/html-accessibility-aria-cheat-sheet)

Source: https://www.ask-codey.com/lessons/html-accessibility-aria
