- Icon Monks
- Guides
- Accessible icons
How to make icons accessible
Decide what each icon is for. An icon next to a visible text label is decorative, so hide it from screen readers with aria-hidden="true". An icon that stands alone, like a close or search button, carries meaning, so give it a text label with aria-label. Then make sure it has enough contrast and a big enough tap target.
Decorative or meaningful?
| Situation | What to do | React example |
|---|---|---|
| Icon next to text ("Settings") | Hide it | <Gear aria-hidden="true" /> |
| Icon-only button | Label the button | <button aria-label="Close"><X aria-hidden="true" /></button> |
| Icon that conveys status on its own | Label the icon | <Warning role="img" aria-label="Error" /> |
| Purely visual flourish | Hide it | aria-hidden="true" |
React, Vue and inline SVG
// React or Vue: any SVG attribute passes through
<button aria-label="Search">
<MagnifyingGlass aria-hidden="true" />
</button>
<!-- inline SVG with its own name -->
<svg role="img" aria-label="Verified" viewBox="0 0 256 256">…</svg>Icon Monks components do not hide themselves by default, so add aria-hidden="true" to decorative icons. The toSvg() helper in @icon-monks/core also takes a title option that adds a <title> element.
Web component and icon font
<!-- hidden from screen readers unless you add a label -->
<icon-monks name="heart"></icon-monks>
<icon-monks name="heart" label="Favorite"></icon-monks>
<!-- icon font: hide the glyph, label the control -->
<button aria-label="Delete"><i class="im im-trash" aria-hidden="true"></i></button>The <icon-monks> element sets aria-hidden for you and switches to role="img" with an aria-label when you pass label. Icon font glyphs are private-use characters that screen readers may read as noise, so always hide them.
Flutter, SwiftUI and Jetpack Compose
// Flutter
Icon(IconMonks.heart, semanticLabel: 'Favorite')
// SwiftUI
IconMonkImage(.heart).accessibilityLabel("Favorite")
// Jetpack Compose (null for decorative icons)
Icon(IconMonks.Regular.Heart, contentDescription = "Favorite")
Contrast and size
WCAG 2.2 asks for a 3:1 contrast ratio between an icon that carries meaning and its background, and a target of at least 24 by 24 CSS pixels for anything you click or tap. Use the bold weight at 16 px and below so strokes stay visible, and never rely on color alone: pair a red icon with a shape change, such as warning instead of a plain circle.
Frequently asked questions
Should icons have alt text?
Only when they carry meaning on their own. A decorative icon next to text should be hidden with aria-hidden="true", because repeating the label makes screen readers say it twice.
Is aria-label or a title element better for SVG icons?
Use aria-label on the button or link around the icon. It is read consistently by all major screen readers, while support for the SVG title element varies.
Do icon fonts hurt accessibility?
Not if you hide the glyph with aria-hidden="true" and give the control a text label. Problems come from unlabelled icon-only buttons, which affect SVG icons just as much.
More guides
Last updated with version 0.4.3. Release notes