# When the Accessible Name Comes From the Wrong Element

Source: https://a11yshieldhq.com/blog/2026-11-14-accessible-name-wrong-element-labelledby.html

November 14, 2026

*A client storefront failed its audit on a single button. The button had an aria-label, the label was spelled correctly, and the screen reader still announced it as plain button. The label pointed at an element ID that no longer existed.*

## The priority chain decides, and it does not ask twice

The browser computes an accessible name from a fixed priority chain, and it stops at the first entry that produces a non-empty string. aria-labelledby wins over everything. Then aria-label. Then an associated label element. Then placeholder, then title. Whatever sits below the winner is ignored completely, which is how a correct aria-label loses to a broken aria-labelledby sitting one line above it.

This is the part developers get backwards. They treat the attributes as a stack of hints that combine, and they are not. The W3C Accessible Name and Description Computation is a strict order, and the first non-empty result is the name. A dangling aria-labelledby, one that points at an ID that was renamed in a redesign or never existed, produces an empty name, and the computation does not fall through to the perfectly good aria-label underneath. The name is empty. The control fails WCAG 4.1.2, Name, Role, Value, at Level A.

## The three ways this breaks on a real storefront

First, the dangling ID. A redesign renames a heading, a component library changes its ID scheme, and every aria-labelledby that referenced the old ID goes quiet at once. The page looks identical. The audit lights up. Repeated components are the worst carriers: a product card rendered twelve times with a hard-coded ID means twelve buttons whose names all point at the first card, or at nothing if the first card is the one that changed.

Second, the borrowed name. aria-labelledby points at visible text elsewhere on the page, which is exactly what it is for, until the text it points at is not the name of the control. A close button labelledby the dialog title announces as the dialog title. A quantity stepper labelledby the product name announces as the product name. The name is present and wrong, which is harder to catch than a missing name because the scanner sees a non-empty string and moves on.

Third, the override. An aria-label on an input that already has a visible label replaces the visible label instead of supplementing it. A voice control user looks at a button that says Add to cart and says exactly that, and nothing happens, because the accessible name is something else entirely. WCAG 2.5.3, Label in Name, exists for this case: when a control has visible text, the accessible name has to contain that text, ideally at the start. The simplest fix is usually to delete the aria-label and let the visible text be the name. The scale of the problem is not small. The WebAIM Million 2025 report found buttons with empty accessible names on roughly 27.7 percent of home pages tested, and the 2024 report found form inputs without an accessible name on 49.6 percent of them.

## What to do instead, in order

Visible text first. If the control can carry its own text, let it. A button that says Add to cart needs no ARIA at all, and it is the only naming method a sighted user, a screen reader user, and a voice control user all agree on. Reach for aria-labelledby when visible text elsewhere already names the element, a dialog title or a section heading, and point it at exactly that text and nothing else.

Reach for aria-label only for icon-only controls, the close cross, the trash can, the search magnifier, where there is genuinely no visible text. Name what the control does, not what it looks like: Close dialog, not X. And hide the icon itself with aria-hidden true, or the screen reader announces the icon tooltip plus the name, which is how you get trash icon, Delete photo, button. The same failure shows up in the [icon-only buttons with no name at all](2026-11-13-icon-button-no-name.md), where the fix starts with the same question: what does this control do.

Then test the name, not the attribute. Open the page in a screen reader, tab to the control, and listen to what is announced. Or open the browser accessibility inspector and read the computed name directly. A checker finding that says button-name is a symptom; the computed name is the diagnosis. The [aria-hidden trap](2026-11-13-aria-hidden-focusable-trap.md) is the same lesson from the other side: what the browser computes is the truth, and the markup is only a guess at it.

## The one-line audit

Search the codebase for aria-labelledby and read every ID it references. If the ID does not exist, is duplicated, or belongs to text that is not the control name, the name is broken. Do the same for aria-label on any element that already has visible text. That pass takes ten minutes on most storefronts and it finds the failures the scanner waves through, because a present-but-wrong name is invisible to automation and obvious to a person listening.

## Frequently asked questions

### Does aria-labelledby override aria-label?

Yes. aria-labelledby has the highest priority in the accessible name computation, so when both are present the browser uses the labelledby text and ignores the aria-label entirely. If the labelledby ID does not exist or is empty, the name comes out empty and the browser does not fall back to the aria-label.

### Can aria-labelledby point at more than one element?

Yes, it takes a space-separated list of IDs, and the text of each referenced element is concatenated in order to form the name. That is useful for combining a verb and a noun, like a button that needs both its action and its target. Every ID in the list has to exist and contain text, or that part of the name drops out.

### Why does my button announce the wrong name when the markup looks right?

The usual cause is a duplicated ID. When a component is rendered more than once with the same hard-coded ID, every instance resolves to the first element with that ID on the page. Check whether the component is repeated, and give each instance a unique ID or switch to aria-label.

### Is an empty aria-label the same as no aria-label?

Almost. An empty aria-label produces an empty name at its step in the chain, and the computation then falls through to the next source, such as visible text or title. A dangling aria-labelledby does not fall through the same way, which is why a broken labelledby is worse than a missing label.
