Accessibility
Icons play an important role in user interfaces, but they need to be implemented correctly to be accessible to all users, including those who rely on screen readers, keyboard navigation, or have visual impairments.
Decorative Icons #
Most icons are decorative -- they accompany text that already conveys the meaning. These icons should be hidden from screen readers using aria-hidden="true":
<!-- Decorative: text provides meaning -->
<button>
<i class="icf-solid icf-download" aria-hidden="true"></i> Download
</button>
<!-- Decorative: label text is sufficient -->
<a href="/settings">
<i class="icf-solid icf-gear" aria-hidden="true"></i> Settings
</a>
<!-- Decorative: used for visual flair -->
<h2>
<i class="icf-solid icf-star" aria-hidden="true"></i> Featured Items
</h2>
When in doubt, ask yourself: "If this icon disappeared, would the user still understand the interface?" If yes, the icon is decorative and should have aria-hidden="true".
Meaningful Icons #
Some icons convey meaning on their own, without any accompanying text. These icons must have an accessible label so screen reader users can understand their purpose.
Use role="img" and aria-label:
<!-- Icon-only button: needs aria-label -->
<button aria-label="Close">
<i class="icf-solid icf-xmark" aria-hidden="true"></i>
</button>
<!-- Icon that communicates status -->
<i class="icf-solid icf-check-circle"
role="img"
aria-label="Completed"
style="color: #10b981;"></i>
<!-- Icon-only link -->
<a href="/search" aria-label="Search">
<i class="icf-solid icf-magnifying-glass" aria-hidden="true"></i>
</a>
<!-- Notification badge with icon -->
<span role="img" aria-label="3 new notifications">
<i class="icf-solid icf-bell" aria-hidden="true"></i>
<span class="badge">3</span>
</span>
Screen Reader Text Pattern #
An alternative approach is to use visually hidden text that only screen readers can access. This is useful when you want more detailed descriptions:
<!-- CSS for visually hidden text -->
<style>
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>
<!-- Icon-only delete button with sr-only text -->
<button>
<i class="icf-solid icf-trash" aria-hidden="true"></i>
<span class="sr-only">Delete item</span>
</button>
<!-- Status indicator with detailed description -->
<div>
<i class="icf-solid icf-circle-exclamation" aria-hidden="true" style="color: #f59e0b;"></i>
<span class="sr-only">Warning: Your subscription expires in 3 days</span>
</div>
<!-- Social media links -->
<a href="https://twitter.com/iconfyra">
<i class="icf-solid icf-twitter" aria-hidden="true"></i>
<span class="sr-only">Follow us on Twitter</span>
</a>
Focus Management #
When icons are interactive (inside buttons, links, or act as controls), ensure proper focus management:
<!-- Icon button with visible focus indicator -->
<style>
.icon-btn {
padding: 8px;
border-radius: 8px;
background: none;
border: none;
cursor: pointer;
font-size: 20px;
color: var(--text-secondary);
transition: all 0.15s ease;
}
/* Clear focus ring */
.icon-btn:focus-visible {
outline: 2px solid var(--primary);
outline-offset: 2px;
}
.icon-btn:hover {
background: var(--gray-100);
color: var(--text);
}
</style>
<button class="icon-btn" aria-label="Edit profile">
<i class="icf-solid icf-pen" aria-hidden="true"></i>
</button>
Never remove focus outlines (outline: none) without providing an alternative focus indicator. Users who navigate with a keyboard rely on visible focus to know where they are on the page.
Color Contrast #
Icons used to convey information must meet WCAG contrast requirements. For non-text elements, WCAG 2.1 requires a minimum contrast ratio of 3:1 against the background.
| Icon Usage | Min Contrast | Example |
|---|---|---|
| Decorative (with text label) | No requirement | Icon next to "Settings" text |
| Meaningful (conveys info) | 3:1 | Status indicators (check, warning) |
| Interactive (in controls) | 3:1 | Icon-only buttons |
/* Good: sufficient contrast on white background */
.icon-success { color: #059669; } /* 4.6:1 on white */
.icon-danger { color: #dc2626; } /* 4.5:1 on white */
.icon-info { color: #2563eb; } /* 4.6:1 on white */
/* Bad: insufficient contrast on white background */
.icon-light { color: #d1d5db; } /* 1.6:1 on white - FAILS */
Icon-Only Controls Checklist #
When using icon-only buttons or links (no visible text), make sure to cover these requirements:
- Add
aria-labelto the interactive element (button/link), not the icon - Add
aria-hidden="true"to the<i>icon element - Ensure a minimum touch target of 44x44 pixels on mobile
- Provide a visible focus indicator
- Consider adding a tooltip for sighted users
<!-- Complete accessible icon button -->
<button
class="icon-btn"
aria-label="Delete message"
title="Delete message">
<i class="icf-solid icf-trash" aria-hidden="true"></i>
</button>
<!-- Or with sr-only text -->
<button class="icon-btn" title="Delete message">
<i class="icf-solid icf-trash" aria-hidden="true"></i>
<span class="sr-only">Delete message</span>
</button>
Animated Icons #
If you use animated icons (like icf-anim-spin), make sure they respect reduced motion preferences and communicate their purpose to assistive technology:
<!-- Loading spinner with proper announcement -->
<div role="status" aria-label="Loading content">
<i class="icf-solid icf-spinner icf-anim-spin" aria-hidden="true"></i>
<span class="sr-only">Loading...</span>
</div>
Good accessibility benefits everyone: it makes interfaces clearer, more predictable, and more robust. When icons are properly labeled and structured, they help all users navigate your application.