Icon-only buttons with no accessible name
The header's search, basket and menu buttons are drawn with SVG icons and contain no words, so each is announced only as “button”.
The problem
Screen reader and voice control users can't tell what the header's buttons do.
A stationery shop's header has the shop's name on the left and three round buttons on the right: a magnifying glass for search, a bag with a small "2" badge for the basket, and three lines for the menu. Sighted mouse users recognise the icons and click them. A screen reader user who presses Tab hears "button", then "2, button", then "button". Nothing tells them which one opens the menu.
Each button contains only an inline SVG drawn with a use reference to a sprite. An SVG with no title and no aria-label contributes no text, so the button has no accessible name. The only text inside is the badge's "2". WCAG 4.1.2 needs every control to have a name that can be determined by software, and 1.1.1 needs the icon's meaning to be available as text.
Screen reader users hear a role with no purpose and have to press each button to find out what it does. In a list of form controls, the three entries are indistinguishable. Voice control users say a button's name to press it; with no name they fall back to slow numbered overlays. People who don't recognise an icon, such as three lines for a menu, also gain from words.
axe's button-name reliably flags a button with no name. It does not judge the name, so it passes aria-label="icon", aria-label="magnifier" or a file name copied from the sprite. A quick AI fix often names the picture instead of the action, adds aria-label to the SVG but leaves the button empty, or names the basket "Basket" while the visible badge adds a stray "2". Whether the name makes sense is something only a person can check.
Try it
Press Tab to move through the header buttons.
The panel shows what a screen reader announces. Can you tell which button opens the menu?
Screen reader says: (Tab to a button)
A recreated demo. The broken version is intentionally inaccessible.
The fix
Give each icon button a name in words: hidden text, aria-label, or a visible label.
- 1Every icon button gets a name in words, whether hidden text, aria-label or a visible label.
- 2Hide the icon itself with aria-hidden="true".
- 3Name the action or destination, not the picture, and read a count only once.
<header class="site-header"> <a href="/" class="logo">Fern & Fold</a> <button type="button" class="icon-button"> <svg class="icon"><use href="#icon-search"></use></svg> </button> <button type="button" class="icon-button"> <svg class="icon"><use href="#icon-bag"></use></svg> <span class="badge">2</span> </button> <button type="button" class="icon-button"> <svg class="icon"><use href="#icon-menu"></use></svg> </button></header>The SVGs give the buttons no text, so the search and menu buttons have no name and the basket is named "2". All three are announced as buttons and nothing more.
The words are real text in the page, so the buttons are announced as "Search, button", "Basket, 2 items, button" and "Menu, button", and browser translation translates them. The badge is hidden because the hidden text already includes the count; without that, the count would be read twice.
All three give each button a name and meet 4.1.2 and 1.1.1. Hidden text keeps the design unchanged and is translated with the page, so it suits a header with no room for words. aria-label keeps the design too and is the simplest in a component, but some browser translation tools leave it in the original language. A visible label costs space, but it is the only option that also helps people who don't recognise the icon. Choose it where the layout allows, and use one of the other two where it doesn't.
Name the action, not the picture. "Search", "Close" and "Menu" describe what happens. "Magnifier", "X" and "Hamburger" describe the drawing, and a file name such as "icon-bag.svg" describes nothing. A toggle such as a favourite heart keeps one name and exposes its state with aria-pressed, rather than swapping the name.
Hide the icon, every time. Without aria-hidden="true", some screen readers announce an unnamed SVG as "image" or "group", and icon fonts can read out a private-use character. An SVG with its own title inside a named button is read twice in some combinations.
Tooltips are not names. A title attribute or a tooltip that appears on hover may be used as a fallback name, but not consistently, and it never appears for touch or keyboard users in every browser. Name the button directly, and keep any tooltip text the same as the name.
Components. Make the name a required prop of the shared icon-button component, so an unnamed button can't be built. If the prop is optional, it will be left out.
Counts and changing state. When the basket count changes, update the name as well as the badge. If people need to hear the change, announce it in a status region; changing a button's name alone is not announced.
Verify the fix
4 checks, no mouse.
Test with: Keyboard, Screen reader, Zoom, Forced colors
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the shared icon-button component changes.
Limits
This is a starting pattern, not a guarantee for every application. The sprite ids and class names in the snippets are placeholders. Icon buttons added later by content editors or third-party widgets need the same check. Test the complete journey with your target assistive technology.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.