Accessibility and performance trade-offs
Decorative versus meaningful icons, the labelling patterns that actually work, and what each delivery method costs in bytes and render time.
Labelling icons correctly
<!-- decorative: the adjacent text already says it -->
<i class="fa-solid fa-trash-can" aria-hidden="true"></i> Delete
<!-- icon-only control: label the control, hide the icon -->
<button type="button" aria-label="Delete item 4">
<i class="fa-solid fa-trash-can" aria-hidden="true"></i>
</button>
<!-- meaningful standalone icon: give the SVG an accessible name -->
<span role="img" aria-label="Warning: unsaved changes">
<i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i>
</span>
<!-- text alternative that stays available to screen readers only -->
<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>
<button class="icon-btn">
<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>
<span class="sr-only">Search</span>
</button>| Situation | Correct pattern |
|---|---|
| Icon beside text | aria-hidden="true" on the icon |
| Icon-only button or link | Label on the control: aria-label or visually hidden text |
| Icon conveying information alone | role="img" plus aria-label, or a hidden text equivalent |
| Purely decorative background icon | aria-hidden="true", and no label anywhere |
| Icon font with ligatures | Expect the ligature text to be read aloud — always combine with aria-hidden |
The SVG + JavaScript renderer adds aria-hidden="true" automatically when an icon carries no accessibility data of its own; the CSS webfont approach leaves that entirely to you. Also set focusable="false" on inline SVG so Internet Explorer and older Edge builds cannot tab into it.
Performance trade-offs
| Delivery | What the browser fetches | Cost / benefit |
|---|---|---|
| CDN CSS plus webfont | One stylesheet and one or more font files | Icons are plain text and stay cached across sites; the font may be large and the first paint can show empty boxes |
| Hosted kit script | A JavaScript bundle containing your subset | Smaller than the full font for a handful of icons; adds a blocking script and a DOM rewrite pass |
| npm CSS plus webfont | The same files from your own origin | One less DNS lookup and full version control; you now own caching and the update cycle |
| svg-core with named imports | Only the imported icons, inlined in your bundle | Smallest for a curated set; each icon adds roughly one to two kilobytes of raw source before compression |
| Inline SVG sprite | One cached sprite file | Fastest runtime, no font and no script; requires your own tooling |
<!-- defer the renderer so it cannot block the first paint -->
<script src="/vendor/fontawesome-kit.js" defer></script>
<!-- reserve the icon box so nothing shifts when the webfont swaps in -->
<style>
.fa-solid, .fa-regular { width: 1.25em; height: 1em; line-height: 1; }
</style>
<!-- or drop the webfont entirely: import three icons and nothing else -->
<!-- import { faBolt, faLock, faCheck } from '@fortawesome/free-solid-svg-icons'; -->- A webfont is a single request for the whole style: cheap when you use dozens of icons, wasteful when you use three.
- Load the kit or the SVG core script with
defer, or late in the body, so it does not delay the first contentful paint. - Avoid animating a large number of icon fonts at once — every frame re-rasterises glyphs; SVG paths animate far more cheaply.
- If icons are the only thing that changes on language switch or theme toggle, prefer CSS colour changes over re-rendering icon markup.
Licensing and alternatives
| Item | Licence | Practical effect |
|---|---|---|
| Free icons | CC BY 4.0 | Attribution is expected; check your project's policy on that |
| Free fonts | SIL OFL 1.1 | Free to embed and redistribute, including commercially |
| Free code | MIT | Use and modify freely |
| Pro icons and kits | Paid per-seat subscription | More styles and icons, but the licence ends with the subscription |
<!-- one component owns size, colour and accessibility -->
<a class="icon-link" href="/docs">
<svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" width="16" height="16">
<path d="M6 2 h9 l5 5 v15 H6 z M9 12 h6 M9 16 h6" fill="none"
stroke="currentColor" stroke-width="2" />
</svg>
Documentation
</a>
<style>
.icon { vertical-align: -0.125em; flex: none; }
.icon-link { display: inline-flex; align-items: center; gap: 0.4em; }
</style>- Inline SVG or a hand-built sprite is the most control with no runtime dependency — the right answer for a small, fixed icon set.
- Modern alternatives with permissive licences and tree-shakeable packages include Lucide, Heroicons, Bootstrap Icons and Material Symbols.
- Whatever the source, keep one icon component that owns the size, the colour and the accessibility attributes, so no page can get it wrong.
⚠️
An icon font renders nothing until the font file arrives, and screen readers have nothing to announce if you forgot the label. If an icon carries meaning, the label is not optional — and if it does not, hiding it with
aria-hidden is equally mandatory.FAQ
Is an icon font or inline SVG better for accessibility?
Inline SVG is easier to get right: it can carry
role, title and aria-label directly, and it cannot fail to load as text. Icon fonts depend on a font file resolving and on you adding the hidden label yourself.Does using Font Awesome hurt my Core Web Vitals?
It can, if the stylesheet or kit script blocks rendering or if the icon font triggers a late swap. Loading with
defer, self-hosting, and reserving space with fixed-width utilities keeps layout shift and blocking time under control.Related
Using, sizing and animating icons Animation, embedding and accessibility
Last refreshed 2026-09-18.