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>
SituationCorrect pattern
Icon beside textaria-hidden="true" on the icon
Icon-only button or linkLabel on the control: aria-label or visually hidden text
Icon conveying information alonerole="img" plus aria-label, or a hidden text equivalent
Purely decorative background iconaria-hidden="true", and no label anywhere
Icon font with ligaturesExpect 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

DeliveryWhat the browser fetchesCost / benefit
CDN CSS plus webfontOne stylesheet and one or more font filesIcons are plain text and stay cached across sites; the font may be large and the first paint can show empty boxes
Hosted kit scriptA JavaScript bundle containing your subsetSmaller than the full font for a handful of icons; adds a blocking script and a DOM rewrite pass
npm CSS plus webfontThe same files from your own originOne less DNS lookup and full version control; you now own caching and the update cycle
svg-core with named importsOnly the imported icons, inlined in your bundleSmallest for a curated set; each icon adds roughly one to two kilobytes of raw source before compression
Inline SVG spriteOne cached sprite fileFastest 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

ItemLicencePractical effect
Free iconsCC BY 4.0Attribution is expected; check your project's policy on that
Free fontsSIL OFL 1.1Free to embed and redistribute, including commercially
Free codeMITUse and modify freely
Pro icons and kitsPaid per-seat subscriptionMore 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.

Using, sizing and animating icons Animation, embedding and accessibility

Last refreshed 2026-09-18.