Fixing ARIA Labels and Accessible Names
Every button, link, form field and landmark on a page has an accessible name: the words a screen reader announces and that voice control users say to activate it. ARIA attributes such as aria-label and aria-labelledby let developers set or override that name.
Used well they rescue icon-only buttons; used badly they hide visible text, point at IDs that do not exist, or leave hidden elements that can still take keyboard focus. This guide shows how browsers work out a name, which ARIA findings matter most, and the exact markup to fix each one.
How a browser decides what a control is called
Browsers build an accessibility tree alongside the page, and each interactive element in it gets a role (button, link, textbox) and a name. WCAG 4.1.2, Name, Role, Value, at level A, requires that every user interface component has a name that assistive technology can read. The rules the browser follows are set out in the W3C's Accessible Name and Description Computation specification. In simplified form, the first of these that exists wins:
aria-labelledby: the text of the element or elements whose IDs it lists.aria-label: the string in the attribute.- Native labelling: a connected
<label>for a form field,altfor an image,<caption>for a table,<legend>for a fieldset. - Content: for buttons, links and some other roles, the text inside the element, including the alt of any image in it.
title: a last resort, and a weak one.
Two consequences surprise people. First, ARIA overrides what is on screen: a button showing "Buy now"
with aria-label="Add to cart" is announced as "Add to cart". Second, ARIA changes only what
assistive technology is told. It does not add keyboard behaviour, focus or visible text. That is why
the first rule of ARIA, as the W3C puts it, is to use a native HTML element with the semantics you need
wherever one exists.
| Attribute | What it does | Use it for |
|---|---|---|
aria-label | Sets the name to a string you type. | Controls with no visible text at all, such as an icon button. |
aria-labelledby | Sets the name from other elements on the page, by ID. | When visible text elsewhere already names the element, such as a dialog title or a section heading. |
aria-describedby | Adds a description, read after the name and role. | Hints, format instructions and error messages. |
aria-hidden="true" | Removes the element and its children from the accessibility tree. | Decorative icons and duplicated content. Never on anything focusable. |
The ARIA findings you are most likely to see
Button or link has no accessible name
The most common and most serious. A hamburger menu, a close cross, a social icon or a slider arrow built from an SVG or icon font, with no text inside. A screen reader says only "button", and a voice control user has nothing to say to press it.
<!-- Fails: nothing to announce -->
<button class="menu-toggle"><svg viewBox="0 0 24 24">...</svg></button>
<!-- Fixed -->
<button class="menu-toggle" aria-expanded="false" aria-controls="main-menu" aria-label="Menu">
<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24">...</svg>
</button>
Accessible name does not contain the visible label
WCAG 2.5.3, Label in Name, at level A, requires that when a control has visible text, its accessible
name includes that text, ideally at the start. A voice control user looks at a button labelled "Send"
and says "click Send". If the code says aria-label="Submit enquiry form", the command fails.
The simplest fix is usually to delete the aria-label and let the visible text be the name.
aria-labelledby points at an ID that does not exist
The attribute takes one or more IDs separated by spaces. If an ID is misspelt, was changed in a redesign, or is duplicated on the page, the name comes out empty or wrong. Components rendered more than once, such as a product card or an accordion, are a frequent cause, because hard-coded IDs repeat.
aria-hidden element is focusable
If something is hidden from the accessibility tree but can still receive keyboard focus, a screen
reader user tabs to it and hears silence or a confusing fragment. It often happens with off-screen menus,
closed modals and carousel slides that are hidden with aria-hidden="true" but still contain
links.
aria-label used where it is not supported
ARIA names are prohibited on generic elements. An aria-label on a plain <div>,
<span> or <p> with no role is ignored by many screen readers, so
the text never reaches the user. If the text matters, make it visible; if the element needs a name, it
probably needs a proper role or element first.
The fix, step by step
Step 1
Read the computed name in developer tools
Run the Accessibility Audit and open the first flagged element in your browser. In Chrome or Edge, inspect it and open the Accessibility pane in the Elements panel, which shows the computed role and name and which source the name came from. Firefox has a similar Accessibility Inspector. Write down the current name for each flagged element. Seeing the name the browser actually computes is faster and more reliable than reading the markup and guessing.
Step 2
Name every icon-only control
For
every button or link with no visible text, add a short name that says what it does, such as aria-label="Close",
aria-label="Next slide" or aria-label="Search". Alternatively, put the words
in a visually hidden span inside the element, which also survives automatic page translation more reliably.
Hide the icon itself with aria-hidden="true" so it is not announced separately. Keep names
short, and do not include the role, because the screen reader already says button or link.
Step 3
Replace aria-label that fights visible text
Search your templates for aria-label on elements that already show text:
buttons, links, tabs and menu items. Where the label repeats the visible text, delete it. Where it says
something different, either delete it and improve the visible text, or make sure the aria-label starts
with the exact visible words, such as Read more about boiler servicing on a link showing
Read more. This satisfies Label in Name and keeps voice control working.
Step 4
Repair broken aria-labelledby references
For
every aria-labelledby and aria-describedby on the page, check that each ID
it lists exists exactly once, with identical spelling and case. In the browser console, document.querySelectorAll('[aria-labelledby]')
lets you loop through them quickly. In components that render many times, generate unique IDs per instance,
for example with the useId hook in React or by adding the product or post ID. Then recheck
the computed name in developer tools.
Step 5
Remove aria-hidden from anything focusable
Find elements with aria-hidden="true" that contain links, buttons, inputs
or anything with a tabindex. If the content is truly hidden, such as a closed menu or modal,
hide it with the hidden attribute, display: none or the inert
attribute, all of which remove it from both the tab order and the accessibility tree. If the content
is visible and usable, remove aria-hidden so screen reader users can reach what everyone else can.
Step 6
Take ARIA off elements that do not support it
Remove aria-label from plain divs, spans and paragraphs that have
no role, and decide what the text was for. If it was extra information, make it visible or connect it
with aria-describedby to a real control. If the element is clickable, replace it with a
real <button> or <a href>, which brings keyboard support and a
role for free. Also delete redundant roles, such as role="button" on a button element.
Step 7
Confirm the names with a screen reader
Open the page with NVDA and press Tab through every control, or use the VoiceOver rotor on a Mac to list form controls, links and landmarks. Each item should be announced with a clear name, the right role and any state, such as expanded or pressed. Listen for anything announced as just button or link, names that repeat, and hidden content that is still read. Then run the Accessibility Audit again and confirm the findings have cleared.
Correct patterns you can copy
A section named by its visible heading
<section aria-labelledby="delivery-heading">
<h2 id="delivery-heading">Delivery and returns</h2>
...
</section>
A <section> only becomes a region landmark when it has a name, and reusing the
heading keeps the name and the visible text in step.
Two navigation areas told apart
<nav aria-label="Main">...</nav>
<nav aria-label="Footer">...</nav>
Screen readers already say "navigation", so the label is just "Main" or "Footer", not "Main navigation".
A visually hidden name that translates
<a href="/basket" class="basket-link">
<svg aria-hidden="true" focusable="false">...</svg>
<span class="visually-hidden">Basket</span>
<span class="count">2</span>
</a>
.visually-hidden {
position: absolute; width: 1px; height: 1px;
margin: -1px; padding: 0; overflow: hidden;
clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
Some automatic translation tools translate page text but not attribute values, so a visually hidden span is often a safer choice than aria-label on multilingual sites.
A dialog named by its title and described by its text
<div role="dialog" aria-modal="true" aria-labelledby="dlg-title" aria-describedby="dlg-desc">
<h2 id="dlg-title">Remove item?</h2>
<p id="dlg-desc">The blue kettle will be removed from your basket.</p>
<button type="button">Remove</button>
<button type="button">Keep it</button>
</div>
The native <dialog> element opened with showModal() is now well supported
and handles focus containment for you; the labelling works the same way.
ARIA mistakes that make things worse
| Mistake | What the user experiences | Fix |
|---|---|---|
<div role="button" aria-label="Save"> with only a click handler | Announced as a button but cannot be reached or pressed with the keyboard. | Use <button type="button">. |
| aria-label stuffed with keywords | Long, repetitive announcements that bury the real action. | A short name saying what the control does. |
aria-label on an input that has a visible label | The visible label is ignored; voice commands may fail. | Connect the <label> and remove aria-label. |
Role words in the name, such as aria-label="Close button" | Hears "Close button, button". | aria-label="Close". |
aria-hidden="true" on the body or main wrapper while a modal is open | Works only if the modal sits outside that wrapper; otherwise the whole page, modal included, goes silent. | Use
a native dialog or the inert attribute on background content. |
| Names added to every decorative icon | Noise: "star, star, star, star, star, 4 stars". | Hide decorative icons and name the group once. |
Where ARIA problems come from
WordPress. Menu toggles, search icons and social icon blocks in themes and page builders are the usual sources. Block themes generally output labelled navigation toggles; older themes and builder widgets may not. Many builders offer an "ARIA label" or "accessible name" field on icon and button widgets. Where they do not, a child theme template override is the durable fix.
Shopify. Themes provide most icon buttons, such as the cart, account and search icons in the header and the slider controls in product galleries. Their labels often come from the theme's translation strings in the locale files, so a missing label may be an empty translation rather than missing markup. Check apps that inject chat buttons, review stars and pop-ups too.
React, Vue and other frameworks. Component libraries usually label their own controls,
but custom icon buttons are often written without a name. Make the name a required prop on your IconButton
component, generate IDs with the framework's ID helper, and use the eslint-plugin-jsx-a11y
rules, or the equivalent for your framework, to catch missing names and invalid ARIA while coding. For
wider framework advice see the React
and Next.js accessibility guide.
Keeping accessible names correct
- Prefer visible text. A button that says "Download brochure" needs no ARIA at all and helps every visitor.
- Build one icon button component that cannot be used without a name, and use it everywhere.
- Review aria-label values whenever visible text changes, so the two do not drift apart.
- Run automated checks in development and the Accessibility Audit after each release.
- Include a quick screen reader pass in testing for any new interactive component, such as a menu, tab set or modal.
ARIA naming overlaps with two other guides: form fields are covered in fixing form labels, and links whose visible text is vague are covered in fixing vague link text. Controls you cannot reach at all belong in fixing keyboard navigation.
1. What is the difference between aria-label and aria-labelledby?
Both set an element's accessible name, but from different places. aria-label takes a string written directly in the attribute, so it is invisible on screen and must be kept up to date by hand. aria-labelledby takes the IDs of other elements and uses their visible text, so the name and what sighted users see stay in step automatically. When suitable visible text already exists on the page, aria-labelledby is usually the better choice. If both are present, aria-labelledby wins.
2. When should I use aria-describedby instead?
Use aria-describedby for extra information that supports the name rather than replacing it: a password rule, a date format, a character limit or an error message. Screen readers read the description after the name and role, often after a short pause, and some users turn descriptions off. So the essential purpose of the control belongs in the name, and the helpful detail belongs in the description. Like aria-labelledby, it takes one or more IDs of elements on the page.
3. Does aria-label help SEO?
It should not be relied on for SEO. aria-label is for assistive technology and is not visible text, so it is not a way to add keywords to a page. Search engines may read attribute values, but stuffing them with search terms makes the page worse to use with a screen reader and adds no meaningful value. If words matter for search, they belong in visible headings, text, links and alt text, where they help every visitor as well as search engines and AI tools.
4. Why does my checker say the button has no name when I added a title attribute?
The title attribute is the last fallback in the accessible name calculation and is not treated as a reliable label by many tools or assistive technologies. It is also only shown as a mouse hover tooltip, so keyboard and touch users never see it. Some checkers therefore still report the button. Give the button a proper name with visible text, a visually hidden span or aria-label, and remove the title unless it adds a genuinely useful extra hint for mouse users.
5. Is it wrong to use ARIA at all?
No. ARIA is essential for custom components that have no native HTML equivalent, such as tab sets, tree views and comboboxes, and for naming things like icon buttons and landmarks. The problem is ARIA used as a shortcut instead of the right HTML element, or added without testing. The W3C advice is to use native elements first, and when ARIA is needed, to follow the patterns in the ARIA Authoring Practices Guide and test them with real screen readers.
6. Can aria-labelledby point to hidden text?
Yes. aria-labelledby and aria-describedby include the text of the elements they reference even when those elements are hidden with the hidden attribute or display none. That lets you build a name from text that is not shown on screen. Use this carefully: hidden names cannot be checked by sighted testers, and they drift out of date more easily. Visible text or a visually hidden span inside the control is usually simpler to maintain and to review.
7. Should every section and div have an aria-label?
No. Adding names to ordinary content creates noise and can be ignored anyway, because names are not supported on generic elements. Name interactive controls, landmarks where there is more than one of the same type, such as two navigation areas, and sections you want to appear as landmarks. Headings already give screen reader users a way to move through content, so a well structured heading outline does more than a page full of aria-label attributes.
The Accessibility Audit tests your page against WCAG 2.2 level AA and lists each finding, such as buttons with no name and invalid or conflicting ARIA attributes, with the element that failed and the success criterion behind it.
Open Accessibility Fixes โ