Overview
Icons are everywhere in modern interfaces — navigation, buttons, status indicators, social links. The critical accessibility question is always the same: is the icon decorative (it appears next to text that already conveys the meaning) or informative (it is the only way the meaning is communicated)? Getting this wrong in either direction causes problems. A decorative icon without aria-hidden="true" may cause screen readers to announce the SVG’s title, path data, or Unicode codepoint. An informative icon without an accessible name leaves a button or link completely unlabeled. SVGs are preferred over icon fonts because they are real DOM elements that can be properly hidden with aria-hidden, while icon fonts rely on CSS pseudo-elements and Unicode Private Use Area codepoints that screen readers may attempt to read.
WCAG Criteria:
- 1.1.1 Non-text Content — every non-text element must either have a text alternative or be marked as decorative
- 4.1.2 Name, Role, Value — interactive elements must have an accessible name that describes their purpose
Key requirements:
- Decorative icons (next to visible text) must be hidden with
aria-hidden="true"so screen readers skip them - Add
focusable="false"on SVGs to prevent a legacy IE/Edge bug where SVGs receive keyboard focus - Informative icon-only buttons must have an accessible name via
aria-labelon the button (not on the SVG) - Never put
role="img"and a<title>on a decorative SVG — this adds redundant announcements - SVGs are preferred over icon fonts because icon font glyphs in the Unicode Private Use Area may be announced as random characters
- If using icon fonts, set
aria-hidden="true"on the<i>or<span>element and provide the accessible name on the parent
Decorative Icon
Exposed Decorative SVG vs. Properly Hidden Decorative SVG
<!-- SVG has no aria-hidden — screen reader may read icon content -->
<button>
<svg xmlns="http://www.w3.org/2000/svg"
width="18" height="18"
viewBox="0 0 24 24"
fill="none" stroke="currentColor"
stroke-width="2">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2
2 0 0 1-2-2z"></path>
<polyline points="9 22 9 12 15 12 15 22"></polyline>
</svg>
Home
</button>Screen reader may announce: "graphic, Home" or SVG path data before "Home"
<!-- aria-hidden + focusable="false" hides the decorative icon -->
<button>
<svg aria-hidden="true"
focusable="false"
xmlns="http://www.w3.org/2000/svg"
width="18" height="18"
viewBox="0 0 24 24"
fill="none" stroke="currentColor"
stroke-width="2">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2
2 0 0 1-2-2z"></path>
<polyline points="9 22 9 12 15 12 15 22"></polyline>
</svg>
Home
</button>Screen reader announces: "Home, button" — icon is hidden
What’s wrong with the exposed decorative SVG?
- Without
aria-hidden="true", screen readers may try to announce the SVG — depending on the browser and screen reader combination, users may hear “graphic”, the SVG’s<title>if present, or even path data - The icon is purely decorative because the text “Home” already conveys the button’s purpose — the SVG adds no information
- Without
focusable="false", Internet Explorer and older Edge versions allow SVGs to receive keyboard focus, creating an extra (meaningless) tab stop - The result is a degraded experience: “graphic, Home, button” instead of the clean “Home, button”
What the screen reader announces:
| Version | Announcement |
|---|---|
| Inaccessible (SVG exposed) | “graphic, Home, button” or “img, Home, button” (varies by screen reader) |
| Accessible (SVG hidden) | “Home, button” |
Informative Icon Button
Unlabeled Icon Button vs. Labeled Icon Button
<!-- Icon-only button with no accessible name -->
<button>
<svg xmlns="http://www.w3.org/2000/svg"
width="16" height="16"
viewBox="0 0 24 24"
fill="none" stroke="currentColor"
stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
<!-- Screen reader says: "button" — what does it do? -->Screen reader announces: "button" — no accessible name
<!-- aria-label on the button provides the accessible name -->
<button aria-label="Close">
<svg aria-hidden="true"
focusable="false"
xmlns="http://www.w3.org/2000/svg"
width="16" height="16"
viewBox="0 0 24 24"
fill="none" stroke="currentColor"
stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
<!-- Screen reader says: "Close, button" -->Screen reader announces: "Close, button"
What’s wrong with the unlabeled icon button?
- The button contains only an SVG with no text content — its computed accessible name is empty
- Screen readers announce “button” with no indication of what the button does
- This is a WCAG 4.1.2 failure: every interactive element must have an accessible name
- Adding
aria-labelto the SVG instead of the button would be incorrect — the name should be on the interactive element, not its child
What the screen reader announces:
| Version | Announcement |
|---|---|
| Inaccessible (no name) | “button” (no indication of purpose) |
| Accessible (aria-label) | “Close, button” |
Where to put the accessible name:
| Scenario | Technique |
|---|---|
| Icon next to visible text | aria-hidden="true" on the icon — button already has a name from the text |
| Icon-only button | aria-label on the <button> — SVG still gets aria-hidden="true" |
| Icon-only link | aria-label on the <a> — SVG still gets aria-hidden="true" |
| Standalone informative image | role="img" + aria-label on the SVG (rare — used for infographics, not UI icons) |
Key Teaching Points
| Technique | Purpose | When to use |
|---|---|---|
aria-hidden="true" on SVG | Hide decorative icon from screen readers | Always — whether the icon is decorative or inside a labeled button |
focusable="false" on SVG | Prevent IE/Edge focus bug | Always, alongside aria-hidden="true" |
aria-label on button | Provide accessible name for icon-only buttons | When there is no visible text inside the button |
| Visible text in button | Provides accessible name naturally | Preferred over aria-label when space allows |
| SVGs over icon fonts | Real DOM elements, proper ARIA support | Always — icon fonts use Private Use Area codepoints that screen readers may read |
The decision tree:
- Does the icon appear next to text that conveys the same meaning? Yes — it is decorative. Add
aria-hidden="true" focusable="false"to the SVG. Done. - Is the icon the only content in an interactive element (button, link)? Yes — it is informative. Add
aria-labelto the button/link, andaria-hidden="true" focusable="false"to the SVG. - Is the icon a standalone image conveying information (e.g., a status indicator)? Yes — add
role="img"andaria-labelto the SVG itself.