Icon Accessibility: A Complete Best-Practices Guide (WCAG)
Icons are visual shortcuts — but for someone using a screen reader, a low-vision user, or anyone navigating by keyboard, a carelessly implemented icon is a dead end. Accessibility (a11y) isn’t a finishing touch; it’s a requirement, and for icons it comes down to a handful of patterns you can apply every time.
Every icon in our directory ships as plain, optimized SVG, so the markup below applies directly to whatever you download.
The one decision that drives everything: decorative or semantic?
Before you add a single ARIA attribute, ask: does this icon carry meaning that isn’t already in the text next to it?
- Decorative — the icon repeats or decorates adjacent text (a house icon next to the word “Home”). Screen readers should ignore it.
- Semantic — the icon is the label (an icon-only button, a status indicator with no visible text). Screen readers must announce it.
Getting this split right fixes the majority of icon a11y bugs.
Decorative icons: hide them
If the meaning is already in the text, hide the icon from assistive tech so it isn’t announced twice:
<a href="/settings">
<svg aria-hidden="true" focusable="false" width="20" height="20" ...>…</svg>
Settings
</a> aria-hidden="true"removes it from the accessibility tree.focusable="false"prevents a stray tab stop in some legacy browsers.
Semantic icons: label them
An icon-only control needs an accessible name. Two reliable approaches:
<!-- 1. Label the control -->
<button aria-label="Delete item">
<svg aria-hidden="true" focusable="false" ...>…</svg>
</button>
<!-- 2. Label the SVG itself -->
<svg role="img" aria-label="Delete" width="20" height="20" ...>
<title>Delete</title>
…
</svg> Prefer labeling the button (approach 1) for interactive controls — it keeps the accessible name on the focusable element. Use role="img" + aria-label/<title> (approach 2) for a standalone informational icon that isn’t a control.
Rule of thumb: an icon-only button without an
aria-labelis the single most common icon a11y failure. If a control has no visible text, it needs a name.
Don’t rely on icons alone to convey meaning
WCAG’s “use of color” principle extends to icons: don’t make an icon the only way information is communicated, especially for status. A red circle and a green circle look identical to a colorblind user and to a screen reader. Pair status icons with text, shape, or both — a checkmark and “Paid”, an alert triangle and “Error”. Distinct shapes (the shapes category is useful here) and text labels make status legible to everyone.
Color contrast
Icons must meet contrast minimums just like text:
- At least 3:1 against the background for meaningful UI icons and graphical objects (WCAG 1.4.11).
- 4.5:1 is safer for small or critical informational icons.
- Purely decorative icons are exempt — but if it conveys meaning, it has to be perceivable.
Because most library icons use currentColor, contrast is governed by the text color you apply — test the actual rendered color against its background, including hover and dark-mode states.
Hit area: make icon buttons tappable
An icon button that’s only 20×20px is a hard target for users with motor impairments or anyone on a phone. WCAG 2.2 (Target Size) calls for at least 24×24 CSS px, and the long-standing practical recommendation is 44×44. Keep the glyph small but pad the control:
.icon-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 44px;
min-height: 44px; /* generous tap target around a 20px glyph */
} Motion and animation
Animated icons (loaders, draw-on effects, the kind you’ll find in the Material Line set) are great — with two guardrails:
- No more than three flashes per second (WCAG 2.3.1) to avoid seizure triggers.
- Respect reduced-motion. Honor the user’s OS setting:
@media (prefers-reduced-motion: reduce) {
.icon-animate { animation: none; }
} Keyboard and focus
Anything interactive must be reachable and operable by keyboard, with a visible focus indicator:
- Use real
<button>/<a>elements, not a<div>with a click handler. - Ensure a visible
:focus-visibleoutline — neveroutline: nonewithout a replacement. - Decorative SVGs should not be focusable (
focusable="false"on the SVG; don’t addtabindex).
A quick pre-ship checklist
- Every icon is classified decorative (
aria-hidden) or semantic (labeled). - Icon-only buttons have an
aria-label(or visually-hidden text). - No status communicated by icon/color alone — text or shape backs it up.
- Meaningful icons clear 3:1 contrast (4.5:1 for small/critical), in every theme.
- Interactive icons have a ≥44px target and a visible focus ring.
- Animations respect
prefers-reduced-motionand don’t flash >3×/sec. - Tested with an actual screen reader (VoiceOver, NVDA) and keyboard-only.
How to test in five minutes
- Tab through the page — can you reach every icon control and see where focus is?
- Turn on a screen reader and listen: decorative icons stay silent; icon buttons announce a clear name.
- Run an automated checker (axe, Lighthouse) to catch missing names and contrast issues.
- Zoom to 200% and toggle dark mode — icons stay crisp (they’re SVG) and still meet contrast.
The bottom line
Accessible icons come down to a clear decorative-vs-semantic decision, honest labeling, sufficient contrast and target size, and respect for motion preferences — all of which take seconds once they’re habit. Start from clean, optimized SVGs (browse the libraries) and apply these patterns, and your icons will be clear and navigable for everyone. For the format reasoning behind using SVG in the first place, see SVG vs PNG vs icon fonts.