How to make icon buttons accessible with aria-label
Icon buttons are everywhere: close dialogs, open menus, submit searches, toggle dark mode. They look clean and familiar to sighted users. But for someone using a screen reader, a button with nothing but an SVG icon is completely silent. No label, no context, no way to know what it does.
It’s one of the most common WCAG 2.1 failures on the web, and one of the easiest to fix.
Why icon-only buttons fail accessibility
When a screen reader encounters a button, it announces the button’s accessible name: the text that describes what the button does. That name can come from a few places, and the browser resolves them in a defined priority. An aria-labelledby reference wins first, then aria-label, then the button’s own text content, and only as a last resort a <title> element inside the SVG.
An icon button with none of these gives the screen reader nothing to work with. In practice the reader might announce “button” and stop there, or fall back to something derived from the SVG’s internal structure: a generic “icon”, or in some cases the contents of a <title> you never meant to expose.
This violates Success Criterion 4.1.2 (Name, Role, Value) at Level A, the lowest bar for conformance. There’s no gray area here. A button without an accessible name fails, full stop.
The fix: aria-label
The fastest, most portable solution is aria-label. You add it directly to the <button> element and give it a short, action-oriented description of what the button does.
<!-- Before: invisible to screen readers -->
<button>
<svg aria-hidden="true" focusable="false">...</svg>
</button>
<!-- After: accessible icon button -->
<button aria-label="Close dialog">
<svg aria-hidden="true" focusable="false">...</svg>
</button>
Two details matter here. First, aria-label goes on the <button>, not the SVG. The button is the interactive element, so that’s where the accessible name belongs. Second, aria-hidden="true" on the SVG keeps the screen reader from also reading whatever lives inside it (decorative paths, a stray <title>). The focusable="false" is a legacy fix for IE11 and old Edge, which made inline SVGs focusable by default; it does nothing in modern browsers, but it’s harmless to leave in.
One thing to get right: aria-label overrides any visible text content on the button, so it becomes the only name the screen reader sees. Keep it action-oriented. “Close dialog”, not “X” or “Close icon”.
When aria-label isn’t the right choice
aria-label works well for self-contained icon buttons, but it has a catch: the label lives only in the attribute, invisible in the rendered page, so it drifts out of sync the moment someone updates visible copy and forgets the hidden string. When a button sits next to text that already says what it does, aria-labelledby is cleaner. It points at that existing text by ID, so there’s a single source of truth.
<span id="menu-label">Main menu</span>
<button aria-labelledby="menu-label">
<svg aria-hidden="true" focusable="false">...</svg>
</button>
Use aria-labelledby when the label text is already rendered in the DOM. Use aria-label when the button’s meaning is self-evident from context and there’s no visible text to reference.
The visually-hidden text approach
A third approach that many design systems use is visually-hidden text — real text content that is positioned off-screen so it’s invisible but still read by assistive technology.
<button>
<svg aria-hidden="true" focusable="false">...</svg>
<span class="sr-only">Open navigation menu</span>
</button>
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
The advantage is that the label is real DOM content, not an attribute, so it survives even where ARIA support is patchy and it flows through translation pipelines that often skip attribute values. The catch is the CSS: get the clipping rules wrong and the text either reappears on screen or collapses to nothing for the screen reader. Copy a known-good .sr-only from a tested utility rather than rolling your own.
What about tooltip-based labels?
A common assumption is that a tooltip on hover counts as a label. It doesn’t. A tooltip rendered as a styled <div> on mouseover is just decoration as far as the accessibility tree is concerned; it never becomes the button’s accessible name unless you connect it explicitly with aria-labelledby (to name the button) or aria-describedby (to describe it). And hover-only tooltips never reach keyboard or screen reader users in the first place.
Tooltips are fine for extra context. They’re not a substitute for an accessible name.
Testing accessible icon buttons
Once you’ve added labels, verify them:
- Screen reader test: Tab to the button with VoiceOver (Mac), NVDA (Windows), or TalkBack (Android). You should hear the label read aloud clearly.
- Accessibility tree: Open browser DevTools, go to the Accessibility panel, and click the button element. The “Accessible Name” field should show your label string — not blank, not “button”.
- Automated scan: Tools like axe or Lighthouse will flag buttons with empty accessible names as violations. Run them as a baseline.
The catch with all three is the manual loop: open DevTools, switch panels, click an element, read the field, repeat. On a page with dozens of icon buttons that gets tedious fast, and tedious is where buttons slip through.
PxGuard’s Accessibility tools surface ARIA attributes, accessible names, and role information directly in an overlay on the page — so you can see at a glance which buttons have labels and which don’t, without leaving the browser tab.
This pairs well with the 5-minute WCAG check workflow if you want a faster review pass, and with the color contrast checker for catching the other common Level A failures at the same time.
A checklist before you ship
- Every icon-only
<button>has an accessible name (viaaria-label,aria-labelledby, or visually-hidden text). - SVG icons inside buttons have
aria-hidden="true"so the screen reader doesn’t also read the SVG internals. - Labels describe the action (“Close dialog”), not the shape (“X button”).
- You’ve checked at least one real screen reader, not just an automated scanner.
- No button relies on a tooltip as its only label.
Icon buttons are a tiny slice of the page with an outsized effect on whether it’s usable without a mouse. Five minutes of labeling pays off for every keyboard and screen reader user who lands on your site.
See it on your own pages
PxGuard is a free Chrome extension. Inspect spacing, typography, and accessibility in seconds.
Install Free