An icon button looks obvious to anyone who can see the icon. A screen reader user hears "button" and has to guess what it does.
Web HIG tip #5 (Quick #69, HIG-A11Y-004): Every control needs an accessible name. Icon-only buttons are where this breaks most often.
Why it matters
Icon-only controls are everywhere: delete, edit, close, menu, share. Without a name:
- Screen readers announce just "button", so a row of five icons sounds like five identical buttons
- Voice control users can't say "click Delete" when the button has no name to match
- The same trash icon can mean "delete this invoice" or "clear the whole list", and the icon alone doesn't tell you which
- Automated accessibility checkers flag buttons and links with no name, so this also shows up in audits
The rule of thumb
Ask: if the icon disappeared, would this control still say what it does?
If not, give it a name.
<!-- Don't: no text, no aria-label, no <title> in the svg, so a screen reader just says "button" -->
<button><svg>…</svg></button>
<!-- Do: name the button, hide the decorative icon -->
<button aria-label="Delete invoice">
<svg aria-hidden="true">…</svg>
</button>
If the button already has visible text, like <button><svg aria-hidden="true">…</svg> Delete</button>, the text is the name. Don't add an aria-label to the icon or the button as well. If you ever do label a button that has visible text, the label must contain that text, so voice users can say what they see.
Do this instead
- Name the action and its target, like "Delete invoice", not just "Delete" or "Trash"
- Put the name on the
<button>or link itself (anaria-labelor visually hidden text), and mark the iconaria-hidden="true" - Use a real
<button>, not a clickable<div>or<span>. A name on a<div>doesn't make it a button (see tip #3) - Add a visible tooltip or text label when the icon isn't widely understood. The accessible name helps assistive tech, not everyone
Quick check for your app
Tab through one screen with a screen reader on. Every control should be announced with a name that tells you what it does, and no icon button should come through as just "button". In browser dev tools, the Accessibility panel shows each control's computed name, so any empty one is a fix.
The Web HIG is a behavioral contract for how the web should behave, not a component library. Design systems define how it looks. The Web HIG defines how it behaves.
- Docs: https://frozonfreak.github.io/webhig/
- Quick Reference: https://github.com/frozonfreak/webhig/blob/main/HIG-QUICK.md
- Tip discussion: https://github.com/frozonfreak/webhig/discussions/13
- Repo: https://github.com/frozonfreak/webhig
Top comments (2)
A useful design-system rule is making reusable icon components decorative by default, while the button owns the accessible name. It keeps semantics out of the SVG asset.
Thanks, that's a great way to put it. Make the icon component decorative by default (aria-hidden="true"), and the button or link that uses it owns the name. That keeps the name next to the action it describes, so the same trash icon can be "Delete invoice" in one place and "Remove filter" in another. The one exception is a standalone icon that carries meaning on its own, like a status icon with no text next to it. For those, an explicit opt-in such as a
labelprop can give it a name.