Type something to search...
What are icon fonts and should you still use them?

What are icon fonts and should you still use them?

Icon fonts are font files in which the glyphs are pictograms instead of letters, so you can display an icon by rendering a character in that font, usually through a CSS class like icon-search. They became popular in the early 2010s because a single small file could deliver hundreds of scalable, colourable icons at a time when SVG support was patchy. For most new projects you should no longer use them: SVG icons are more accessible, render more crisply, can use multiple colours, don't break when fonts fail to load, and are well supported in every modern browser. Icon fonts still have a place in a few situations, such as legacy systems and some variable-font icon sets, but SVG should be your default.

Plenty of live sites still depend on Font Awesome's webfont version, old Bootstrap Glyphicons or a custom IcoMoon build, and many teams are unsure whether a migration is worth the effort. In this article you'll learn how icon fonts actually work, the specific problems they cause, the main ways to use SVG icons instead, how to make icons accessible whichever technique you use, and how to plan a migration.

How Icon Fonts Work

An icon font is an ordinary font file, usually WOFF2, loaded with @font-face. Its glyphs are icons, and each icon is mapped to a character code. There are two common mapping approaches.

Private Use Area Code Points

Most classic icon fonts assign each icon to a code point in Unicode's Private Use Area, a range of characters with no standard meaning. The CSS inserts that character with a pseudo-element:

@font-face {
  font-family: "SiteIcons";
  src: url("/fonts/site-icons.woff2") format("woff2");
  font-display: block;
}

.icon {
  font-family: "SiteIcons";
  font-style: normal;
  font-weight: normal;
  line-height: 1;
}

.icon-search::before {
  content: "\e900";
}

.icon-cart::before {
  content: "\e901";
}
<button type="button">
  <span class="icon icon-search" aria-hidden="true"></span>
  Search
</button>

The character \e900 means nothing on its own. It only becomes a magnifying glass when the icon font is applied.

Ligatures

Some icon fonts, such as Google's Material Symbols, use ligatures instead. You type the icon's name as text, and the font replaces the whole word with a single glyph:

<span class="material-symbols-outlined" aria-hidden="true">shopping_cart</span>

Ligature-based icons are more readable in source code, and if the font fails, the user sees a word instead of a meaningless box. The downside is that the word occupies a lot of space before the font loads, which can cause layout shift.

Why Icon Fonts Fell Out of Favour

Icon fonts solved real problems in their day, but they come with drawbacks that are hard to engineer around.

Accessibility Problems

Screen readers can announce Private Use Area characters as odd symbols, unpronounceable sounds or nothing at all, depending on the reader. If you forget aria-hidden="true" on a decorative icon, some users hear noise. If you rely on the icon alone to label a button, users of assistive technology may hear nothing useful.

There's another problem that affects many people who don't use screen readers. Users with dyslexia or low vision sometimes override site fonts with a browser extension or custom stylesheet. Because icon fonts are fonts, those overrides replace them too, and every icon turns into an empty box or a random character. SVG icons aren't affected.

Failure Modes

  • Font blocked or slow: If the icon font fails to load, perhaps because of a content blocker, a corporate firewall or a slow connection, icons appear as empty squares or letters. Using font-display: block hides them for up to about three seconds while waiting, which can leave buttons blank.
  • Wrong font applied: A specificity mistake that sets font-family on a parent can turn icons into text.
  • Cross-origin restrictions: Fonts loaded from another origin need correct CORS headers, and misconfiguration can break every icon on the page.

Rendering and Layout

Browsers treat icons in fonts as text, so they're anti-aliased and hinted like text. At small sizes, edges can look blurry or misaligned, and the same icon can render slightly differently across operating systems. Icons also inherit text properties such as line height and baseline position, which makes pixel-perfect vertical alignment fiddly.

One Colour Only

A standard icon font glyph takes a single colour from color. Multi-colour icons need stacked glyphs or a colour font format, both of which are awkward compared with SVG, where each shape can have its own fill.

Payload

An icon font contains every icon in the set, whether you use five or five hundred. Unless you build a custom subset, users download icons they'll never see.

The SVG Alternatives

SVG is a vector image format that browsers render natively, and it's been supported everywhere for well over a decade. There are several ways to use it for icons.

Inline SVG

Paste the SVG markup directly into the HTML. This gives you full CSS control, no extra request and no flash of missing icons.

<button type="button" class="btn">
  <svg
    class="icon"
    aria-hidden="true"
    focusable="false"
    width="20"
    height="20"
    viewBox="0 0 24 24"
  >
    <circle
      cx="11"
      cy="11"
      r="7"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
    />
    <path
      d="M20 20l-4-4"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
    />
  </svg>
  Search
</button>
.icon {
  inline-size: 1.25em;
  block-size: 1.25em;
  flex-shrink: 0;
  vertical-align: -0.2em;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  color: var(--btn-text);
}

Using currentColor for fill or stroke means the icon picks up the text colour automatically, just as an icon font would. Sizing in em keeps icons proportional to the surrounding text.

Component frameworks make this pleasant. Libraries such as Lucide, Heroicons, Phosphor and Tabler publish their icons as individual components, so a build tool includes only the icons you import.

SVG Sprites With use

If the same icons appear many times on a page, repeating the full markup is wasteful. An SVG sprite defines each icon once as a symbol, and every instance references it with use.

<svg xmlns="http://www.w3.org/2000/svg" style="display: none">
  <symbol id="icon-cart" viewBox="0 0 24 24">
    <path
      d="M3 4h2l2.4 10.2a1 1 0 0 0 1 .8h8.9a1 1 0 0 0 1-.8L20 8H6"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    />
    <circle cx="9" cy="19" r="1.5" fill="currentColor" />
    <circle cx="17" cy="19" r="1.5" fill="currentColor" />
  </symbol>
</svg>

<a href="/cart" class="nav-link">
  <svg class="icon" aria-hidden="true" focusable="false">
    <use href="#icon-cart"></use>
  </svg>
  Basket
</a>

You can also keep the sprite in an external file, such as /icons.svg, and reference icons as /icons.svg#icon-cart. The file is cached across pages. External sprites must be served from the same origin, because use doesn't load cross-origin SVG.

SVG as an Image

For icons that never change colour, a plain img element works and keeps the HTML tidy:

<img src="/icons/logo-mark.svg" alt="" width="24" height="24" />

The downside is that CSS can't reach inside the image, so you can't change its colour with color on hover.

CSS Masks

If you want icons in CSS only, without touching the HTML, use the SVG as a mask and paint it with background-color. This is the closest drop-in replacement for an icon font's pseudo-element approach.

.icon-mask {
  display: inline-block;
  inline-size: 1em;
  block-size: 1em;
  background-color: currentColor;
  mask: var(--icon) no-repeat center / contain;
}

.icon-mask--download {
  --icon: url("/icons/download.svg");
}
<a href="/report.pdf" class="link">
  <span class="icon-mask icon-mask--download" aria-hidden="true"></span>
  Download report
</a>

The unprefixed mask property is supported in all current major browsers. The icon takes the text colour, scales with font size and doesn't depend on a font loading.

Comparing the Options

TechniqueColour via CSSMulti-colourExtra requestWorks if fonts are blockedBest for
Icon fontYes, one colourNoYesNoLegacy projects
Inline SVGYesYesNoYesComponents and frameworks
SVG spriteYesLimitedOptionalYesMany repeated icons
SVG in imgNoYesYesYesFixed-colour graphics
CSS maskYes, one colourNoYesYesCSS-only replacement

Making Icons Accessible

The technique matters less than how you label the icon. Ask whether the icon is decorative or meaningful.

  • Decorative icons: If visible text next to the icon already says what it means, hide the icon from assistive technology with aria-hidden="true". For inline SVG, add focusable="false" as well.
  • Icon-only buttons and links: Give the control an accessible name. A visually hidden text label is the most robust approach, and aria-label on the button also works.
  • Standalone meaningful icons: If an icon conveys information on its own, such as a status indicator, give the SVG role="img" and an accessible name.
<!-- Icon-only button -->
<button type="button" class="icon-button" aria-label="Close dialog">
  <svg
    aria-hidden="true"
    focusable="false"
    width="20"
    height="20"
    viewBox="0 0 24 24"
  >
    <path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2" />
  </svg>
</button>

<!-- Meaningful standalone icon -->
<svg
  role="img"
  aria-labelledby="status-title"
  width="16"
  height="16"
  viewBox="0 0 16 16"
>
  <title id="status-title">Payment failed</title>
  <circle cx="8" cy="8" r="7" fill="#b42318" />
</svg>

Icons also need enough contrast. WCAG 2.2 requires a contrast ratio of at least 3:1 for graphical objects that are needed to understand the content, under success criterion 1.4.11 Non-text Contrast.

When Icon Fonts Still Make Sense

There are a few situations where keeping or even choosing an icon font is reasonable:

  • Legacy systems: A large site with thousands of templates using icon font classes may not justify a full migration. Fix the accessibility basics first.
  • Variable icon fonts: Material Symbols is a variable font with axes for fill, weight, grade and optical size, letting you animate or tune icons with font-variation-settings. That's hard to replicate with static SVGs.
  • Environments without SVG control: Some CMS editors and third-party widgets only let you add a class name. A CSS mask is usually a better fit, but an icon font may be the only option.

If you do use Material Symbols, request only the icons you need. Google Fonts accepts an icon_names parameter that subsets the font to a listed set of icons, which reduces the file size considerably.

<link
  rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0&icon_names=home,search,shopping_cart&display=block"
/>
.material-symbols-outlined {
  font-variation-settings:
    "FILL" 0,
    "wght" 400,
    "GRAD" 0,
    "opsz" 24;
}

.nav-link:hover .material-symbols-outlined {
  font-variation-settings:
    "FILL" 1,
    "wght" 600,
    "GRAD" 0,
    "opsz" 24;
}

How to Migrate From an Icon Font to SVG

A migration doesn't have to happen in one go. A gradual plan keeps risk low.

  1. Audit usage: Search your templates for icon font class names to find which icons you actually use. Many projects use a small fraction of the set.
  2. Pick an equivalent SVG set: Most popular icon fonts, including Font Awesome, offer an SVG version. If you used a custom IcoMoon font, export the original SVGs from your IcoMoon project.
  3. Build an icon component or sprite: Create a single helper, such as a template partial or framework component, that takes an icon name and outputs the SVG with the right accessibility attributes.
  4. Replace icons page by page: Keep the font loaded until the last icon is replaced, then remove the @font-face rule and the font file.
  5. Check alignment and size: SVGs don't sit on the text baseline the way glyphs do, so check buttons and inline icons for vertical alignment.

A quick search shows how many icon font references are left:

grep -rhoE "fa-[a-z0-9-]+" templates/ | sort | uniq -c | sort -rn | head
  48 fa-chevron-right
  31 fa-search
  22 fa-user
  17 fa-shopping-cart
   9 fa-times

FAQ: Icon Fonts

They can be. Screen readers may announce the private-use characters as strange symbols, and users who override website fonts for readability will see broken boxes instead of icons. You can reduce the first problem by hiding decorative icons with aria-hidden, but the second can't be fixed while the icons are a font.

Often, yes, because you only ship the icons you use and inline SVG needs no extra request. A small subsetted icon font can be lightweight too, but it still has to download before icons appear, while inline SVG renders with the HTML.

Yes, if the SVG is inline, used through a sprite, or applied as a CSS mask. Set its fill or stroke to currentColor and it will follow the CSS color property, including on hover. An SVG loaded through an img element can't be recoloured with CSS.

Font Awesome offers both a web font version and an SVG version, along with framework components. For new projects, the SVG or component approach is generally the better choice for accessibility and performance.

Yes. Material Symbols is a variable icon font that uses ligatures, so typing an icon's name produces the icon. Google also provides the same icons as SVG files, which you can use if you don't need the variable axes.

Not if you give them a size. Set width and height attributes or a CSS size so the browser reserves the space before the icon renders. Inline SVG is part of the HTML, so it can't arrive late the way a web font can.


Conclusion

Icon fonts were a clever workaround for an era when SVG wasn't reliable, packaging icons as glyphs so they could scale and take on colour like text. That same trick is the source of their problems today: screen readers misread them, user font overrides break them, they disappear when fonts fail to load, and they can only ever be a single colour.

For new work, use SVG. Inline SVG and framework components suit most projects, sprites handle heavy repetition, and CSS masks offer a drop-in replacement for class-based icons. Whichever you choose, label icons properly and give them enough contrast. Keep icon fonts only where a migration isn't worth it yet, or where a variable icon font like Material Symbols gives you something SVG can't.

Share :

Related Posts

How to make typography accessible?

How to make typography accessible?

You make typography accessible by choosing clear typefaces, setting text in relative units so it scales with user preferences, giving it enough colou

Dive Deeper
What are the parts of a letterform?

What are the parts of a letterform?

A letterform is the shape of a single letter, and typographers break it down into named parts. The main ones are the stem (the main vertical

Dive Deeper
What are ascenders, descenders and the baseline?

What are ascenders, descenders and the baseline?

The baseline is the invisible line that letters sit on. Ascenders are the parts of lowercase letters that rise above the x-height, such as th

Dive Deeper