aria-label Examples: When to Use It and 20 Fixes

Use aria-label only when an element has no visible text that names it, such as an icon-only button: <button aria-label="Close dialog">. If visible text already names it, rely on that or point aria-labelledby at it. aria-describedby adds extra help that is read after the name. Native HTML labels always come first.

Which naming tool to reach for
SituationUse
Visible text inside the elementNothing extra, the text is the name
Form field with a visible caption<label for>
Name already shown elsewhere on screenaria-labelledby="id"
No visible text at all (icon only)aria-label="..."
Extra hint or error after the namearia-describedby="id"

Updated

Every button, link, field and landmark needs an accessible name, the short phrase assistive technology speaks when focus lands there. This page shows how that name is worked out, twenty broken patterns next to their repaired versions, and a checker that reads a pasted snippet and reports the name it would get.

aria-label, aria-labelledby, aria-describedby or visible text

Work down this list and stop at the first option that fits. Visible text is first because it serves sighted, screen reader and voice control users with one string.

The four ways to give an element its name or description
OptionWhat it doesUse it whenWatch out for
Visible text or a <label>Native name from content or the label elementAlmost always, for buttons, links and fieldsPlaceholder is not a label
aria-labelledbyBuilds the name from other elements by idA heading or caption already on screen names the thingReferenced ids must exist and be unique
aria-labelSets the name as a string, overriding contentThere is no visible text, as with icon buttonsIgnored on generic div and span; often not translated
aria-describedbyAdds a description read after the nameHints, format rules, error messagesNot a name; may be skipped by users who turn descriptions off

20 before and after snippets

Each snippet shows the broken pattern, the repair, and roughly what a screen reader says after the repair. Announcements vary by product and settings.

Icon-only close button

Without a name the button is typically announced as just button. Hide the drawing so it is not read as an unlabelled image. After the fix it is typically announced as: Close, button.

<!-- Before -->
<button><svg viewBox="0 0 24 24"><path d="M6 6l12 12M18 6L6 18"/></svg></button>

<!-- After -->
<button type="button" aria-label="Close">
  <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 6l12 12M18 6L6 18"/></svg>
</button>

Icon link to a social profile

An empty link is often read as its URL, letter by letter on some setups. After the fix it is typically announced as: Our shop on Instagram, link.

<!-- Before -->
<a href="https://example.com/ourshop"><i class="icon-insta"></i></a>

<!-- After -->
<a href="https://example.com/ourshop" aria-label="Our shop on Instagram">
  <i class="icon-insta" aria-hidden="true"></i>
</a>

Icon plus hidden text instead of aria-label

Real text in a visually hidden span survives translation tools, which tend to skip attribute values. After the fix it is typically announced as: Cart, 3 items, link.

<!-- Before -->
<a href="/cart"><svg aria-hidden="true">...</svg></a>

<!-- After -->
<a href="/cart">
  <svg aria-hidden="true" focusable="false">...</svg>
  <span class="visually-hidden">Cart, 3 items</span>
</a>

Two navigation landmarks

With two unnamed nav elements, the landmark list shows navigation twice and nobody can tell them apart. After the fix it is typically announced as: Main, navigation.

<!-- Before -->
<nav>...main menu...</nav>
<nav>...footer links...</nav>

<!-- After -->
<nav aria-label="Main">...</nav>
<nav aria-label="Footer">...</nav>

Search field with only a placeholder

Placeholder text is a weak fallback name and disappears once someone types. After the fix it is typically announced as: Search products, search edit text.

<!-- Before -->
<input type="search" placeholder="Search">

<!-- After -->
<form role="search">
  <label for="q" class="visually-hidden">Search products</label>
  <input id="q" type="search" placeholder="e.g. walking boots">
  <button>Search</button>
</form>

Form field with a visible label

A real label element also makes the caption clickable, which enlarges the target. After the fix it is typically announced as: Email, edit text.

<!-- Before -->
<span>Email</span>
<input type="email" aria-label="Email">

<!-- After -->
<label for="email">Email</label>
<input id="email" type="email" autocomplete="email">

Hint and error with aria-describedby

Descriptions are joined in the order the ids are listed. After the fix it is typically announced as: Password, secure edit text, invalid entry, At least 12 characters, Too short....

<!-- Before -->
<label for="pw">Password</label>
<input id="pw" type="password">
<p>At least 12 characters</p>

<!-- After -->
<label for="pw">Password</label>
<input id="pw" type="password" aria-describedby="pw-hint pw-err" aria-invalid="true">
<p id="pw-hint">At least 12 characters</p>
<p id="pw-err">Too short, add 4 more characters</p>

Group of radio buttons

The legend names the group, so each option makes sense on its own. After the fix it is typically announced as: Delivery speed, group, Standard, radio button, 1 of 2.

<!-- Before -->
<p>Delivery</p>
<input type="radio" id="std" name="d"><label for="std">Standard</label>

<!-- After -->
<fieldset>
  <legend>Delivery speed</legend>
  <input type="radio" id="std" name="d"><label for="std">Standard</label>
  <input type="radio" id="exp" name="d"><label for="exp">Express</label>
</fieldset>

Modal dialog named by its heading

Pointing at the visible heading keeps the spoken and shown titles identical. Open it with showModal() for a true modal. After the fix it is typically announced as: Delete project?, dialog, This removes all 14 pages..

<!-- Before -->
<div class="modal">
  <h2>Delete project?</h2>...
</div>

<!-- After -->
<dialog aria-labelledby="dlg-t" aria-describedby="dlg-d">
  <h2 id="dlg-t">Delete project?</h2>
  <p id="dlg-d">This removes all 14 pages.</p>
  <button>Cancel</button> <button>Delete</button>
</dialog>

Toggle button with aria-pressed

Keep the name fixed and let the state change. Do not also swap the label to Unbold. After the fix it is typically announced as: Bold, toggle button, pressed.

<!-- Before -->
<button class="on">Bold</button>

<!-- After -->
<button type="button" aria-pressed="true">Bold</button>

Icon toggle: mute

Changing both the label and a pressed state at once reads as a double negative. After the fix it is typically announced as: Mute, toggle button, not pressed.

<!-- Before -->
<button aria-label="Unmute"><svg>...</svg></button>

<!-- After -->
<button type="button" aria-label="Mute" aria-pressed="false">
  <svg aria-hidden="true" focusable="false">...</svg>
</button>

Menu button with aria-expanded

Script must flip aria-expanded and the hidden attribute together. After the fix it is typically announced as: Products, button, collapsed.

<!-- Before -->
<button onclick="toggle()">Products</button>
<ul class="menu">...</ul>

<!-- After -->
<button type="button" aria-expanded="false" aria-controls="prod-menu">Products</button>
<ul id="prod-menu" hidden>...</ul>

Hamburger menu button

A clickable div is not focusable or announced as a control at all. After the fix it is typically announced as: Menu, button, collapsed.

<!-- Before -->
<div class="burger" onclick="open()">&#9776;</div>

<!-- After -->
<button type="button" aria-label="Menu" aria-expanded="false" aria-controls="site-nav">
  <span aria-hidden="true">&#9776;</span>
</button>

Informative and decorative images

Write what the picture tells the reader. Empty alt marks decoration so it is skipped. After the fix it is typically announced as: Sales rose from 40 to 65 orders a week in May, image.

<!-- Before -->
<img src="chart.png" aria-label="chart">
<img src="swirl.png">

<!-- After -->
<img src="chart.png" alt="Sales rose from 40 to 65 orders a week in May">
<img src="swirl.png" alt="">

Standalone informative SVG

role=img makes the SVG one object instead of a tree of shapes. After the fix it is typically announced as: Northgate Bakery, image.

<!-- Before -->
<svg viewBox="0 0 24 24">...</svg>

<!-- After -->
<svg role="img" aria-labelledby="logo-t" viewBox="0 0 24 24">
  <title id="logo-t">Northgate Bakery</title>
  ...
</svg>

Data table with a caption

A visible caption names the table for everyone. Use aria-labelledby only when the title sits outside it. After the fix it is typically announced as: Opening hours, table, 2 columns, 8 rows.

<!-- Before -->
<table aria-label="table">...</table>

<!-- After -->
<table>
  <caption>Opening hours</caption>
  <tr><th scope="col">Day</th><th scope="col">Hours</th></tr>
  ...
</table>

Status message in a live region

The region must exist before its text changes, or many readers miss the update. After the fix it is typically announced as: Saved (spoken without moving focus).

<!-- Before -->
<div class="toast">Saved</div>

<!-- After -->
<div role="status">Saved</div>
<!-- keep this element on the page, then change its text -->

Named region for a carousel

aria-roledescription changes the spoken role. Use it sparingly and never on generic elements. After the fix it is typically announced as: Customer photos, carousel.

<!-- Before -->
<div class="carousel">...</div>

<!-- After -->
<section aria-roledescription="carousel" aria-label="Customer photos">
  ...
</section>

Repeated Read more links

Unique link text helps everyone scanning a list of links. Starting with the visible words keeps Label in Name intact. After the fix it is typically announced as: Read more about winter opening hours, link.

<!-- Before -->
<a href="/post/1">Read more</a>
<a href="/post/2">Read more</a>

<!-- After -->
<a href="/post/1" aria-describedby="t1">Read more</a>
<!-- or better: -->
<a href="/post/1">Read more<span class="visually-hidden"> about winter opening hours</span></a>

Label that does not match the visible text

Someone saying click Send to voice control must hit this button, so the visible word belongs in the name. After the fix it is typically announced as: Send, button.

<!-- Before -->
<button aria-label="Submit form">Send</button>

<!-- After -->
<button>Send</button>
<!-- if more context is needed: -->
<button aria-label="Send message">Send</button>

Common mistakes

Mistakes seen in audits and their fixes
MistakeWhy it hurtsFix
aria-label on a div or span with no roleProhibited, so most readers say nothingUse a button, link or landmark, or give it a suitable role
aria-label that differs from the visible wordsVoice control commands miss itBegin the name with the visible text
Role word inside the name, as in Search buttonRole is spoken twiceName it Search
aria-hidden="true" on a focusable elementFocus lands on something silentRemove it, or make the element unfocusable too
aria-labelledby pointing at a missing idName falls back or ends up emptyCheck ids after every template change
Same label on every Delete button in a listUsers cannot tell which row it affectsDelete invoice 1042, or aria-labelledby with the row title
Using title as the only nameUnreliable for touch and keyboard usersVisible text or aria-label

Testing checklist

Check names before you ship
CheckHow
Every control has a nameBrowser dev tools Accessibility pane shows the computed name
Names match what is shownRead the visible label, compare with the computed name
Landmarks are distinguishableOpen the landmarks list in a screen reader rotor or elements list
States updateToggle a menu or button and listen for expanded or pressed
Live messages are heardTrigger a save or error without moving focus
Real screen reader passNVDA or JAWS with Firefox or Chrome on Windows, VoiceOver with Safari on Mac and iPhone, TalkBack on Android

The first rule of ARIA

The W3C guidance on using ARIA opens with one rule: if a native HTML element already has the meaning and behaviour you need, use it instead of adding ARIA to something else. A real button with real text needs no ARIA at all.

ARIA changes only what assistive technology is told. It does not add keyboard support, focus or click handling, so a div with role="button" still needs tabindex and key handlers that a button gives you for free.

How browsers work out the name

The Accessible Name and Description Computation spec sets an order. aria-labelledby comes first, then aria-label, then the native source such as a label element, alt text or the text inside a button or link, and finally the title attribute as a last resort.

Because the first source found wins, a stray aria-label can silently hide a perfectly good visible label. When a name sounds wrong, check the higher-priority attributes before editing the text.

Names, descriptions and announcements

A screen reader typically speaks the name, then the role, then any state such as pressed or expanded, then the description. Exact wording and order differ between products, versions and verbosity settings, so the announcements on this page are approximate.

How it works

  1. Read the decision table to pick between visible text, a label element, aria-labelledby and aria-label.
  2. Find your pattern among the twenty before and after snippets and copy the repaired version.
  3. Paste your own markup into the name checker to see a simplified accessible name for its first interactive element.
  4. Confirm the result with a real screen reader using the testing checklist at the bottom.

Tips

Frequently asked questions

What is the difference between aria-label and aria-labelledby?

aria-label holds the name as a string inside the attribute. aria-labelledby points at one or more element ids and builds the name from their text, so the name always matches what sighted users see. When both are present, aria-labelledby wins.

Does aria-label replace the visible text of a button?

Yes. For most controls the aria-label value overrides the text content, so a button showing Buy that carries aria-label="Add to basket" is announced as Add to basket. That mismatch confuses voice control users and breaks WCAG 2.5.3 Label in Name.

When should I use aria-describedby?

Use it for supporting information that is not the name: a password rule, a format hint or an error message. Screen readers usually read the description after the name and role, often after a short pause, and some users turn descriptions off.

Can I put aria-label on a div?

Only if the div has a role that allows naming, such as role="region" or role="dialog". On a generic div or span, ARIA prohibits naming and most screen readers ignore the attribute.

Do images need aria-label?

No. Images take their name from the alt attribute. Use alt="" for decoration so the image is skipped. aria-label on an img works but adds nothing alt cannot do.

Related: HTML accessibility checker, Website accessibility checklist, WCAG 2.2 checklist, Color contrast checker, HTML links guide.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap