Are icon fonts a bad practice?


There‘s a lot of outdated information about icon fonts on the internet. When SVG vs icon fonts was last a conversation in the frontend blogosphere (circa 2016) SVG won the debate.

Since then, font technology and CSS have moved on:

  • The CSS content property can now specify alt text.
  • Variable fonts are now a thing.
  • Universal browser support for COLR font technology means that icons can contain multiple colors.
  • woff2 has been supported in all browsers for many years and is the only format you need for the web.
  • We have full browser support for font-display. It’s advisable to use font-display: block for icon fonts (this is the default behaviour).

Despite numerous anti-icon font articles, they remain popular. Of all the fonts in the world, Font Awesome is the second most used font on the web. Collectively, icon fonts account for around 18% of web font usage, according to the 2024 Web Almanac.

Multicolor icons

In the past, multicolored icons were not possible in a font. COLRv0, a font technology supported in all browsers since 2019, changed that. Icons can contain an unlimited range of solid colors.

account_circlefavoritevisibilitythumb_uplanguageassessmentrecord_voice_overtodayshopping_cart

You can change the colors using the CSS font-palette property.

account_circlefavoritevisibilitythumb_uplanguageassessmentrecord_voice_overtodayshopping_cart

COLRv1 and OpenType-SVG allow for gradients and more advanced design effects.

Variable icon fonts?

With a variable icon font you can match the weight of the icon to the weight of the text. A CTA button with bold text could have a chunky version of the icon. When used elsewhere in a more subtle context, a thinner version of the same glyph could be used. You could achieve this result with SVG by changing the stroke-width with CSS, but changing the font-weight of a variable font feels slightly more natural.

Take SF Symbols from Apple as an example:

SF Symbols provides symbols in a wide range of weights and scales to help you create adaptable designs. Each of the nine symbol weights — from ultralight to black — corresponds to a weight of the San Francisco system font, helping you achieve precise weight matching between symbols and adjacent text, while supporting flexibility for different sizes and contexts.

The precise inner workings of SF Symbols are a mystery (is it a variable font under the hood?), but its an interesting example of a symbol set with multiple weights (from ultralight to black) and both monochrome and multicolor variations.

Material Symbols, available through Google Fonts, is an example of a variable icon font. It comes with an axis for weight, grade and optical sizing.

The axes of a variable font can be animated, which can be useful for things like hover effects.

While I like the concept of variable icon fonts, actually creating one takes a fair amount of know-how. There are simple tools to take a bunch of SVG files and convert them into a regular icon font. Creating a variable font, by contrast, requires complex and expensive software like Glyphs or FontLab.

Performance

woff2 compression is impressive.

However, it’s frustratingly common for websites to use a font containing hundreds of icons when only a few dozen are actually used. If you are using a third party icon font rather than creating your own, tools like pyftsubset, glyphhanger and subfonts or an online GUI like Font Squirrel or Fontello can help remove unused glyphs (a process called subsetting). The paid version of Font Awesome includes its own subsetting functionality. Updating a font every time you use an additional icon does seem more laborious than maintaining a folder of SVG files.

Even if the font file contains only icons you actually use, chances are you’re not using all of the icons on every page. That’s still wasteful — if a page uses a single icon, the entire font needs to be downloaded (an SVG sprite sheet suffers from the same issue).

You could partly get around this problem by splitting your icons into multiple files. Keep your most frequently used icons in one woff2 file, (which you could consider preloading). Icons that are only used in a particular section of the site could be kept in a seperate woff2 file. By specifying unicode-range as part of @font-face, the file is only requested if icons it contains are actually used on the current page. This approach takes effort and maintenance.

What happens if the font fails to load?

Some browsers, such as Firefox, and some browser extensions, allow a user to override the fonts you’ve specified in your CSS with their own preferred typeface. This used to break icon fonts. Firefox found a way around this issue and some extensions are also smart enough to not override icon fonts.

Users can also block all web fonts for improved performance and security or to save on bandwidth, but again, there are ways for users to do this without blocking icon fonts. I have no data on how many people actually do this and assume it’s fairly insignificant but it does speak to the brittleness of fonts compared to SVG.

A more pressing issue than users purposefully blocking or overriding your font is a flaky internet connection simply causing the download to fail. With an inline SVG, the icon is literally part of the document so will always be included. Fonts, by contrast, are a seperate request and inevitably less reliable.

Text and emoji are standardised by Unicode. Unicode defines Private Use Areas for custom glyphs that have no standard meaning. It’s important to use the Private Use Areas for custom icons, otherwise users might see confusing results if the font request fails. Some operating systems, such as iOS, make their own use of the Private Use Area for some icons. This can lead to unexpected results that are seemingly unavoidable.

In Chrome and Safari, missing icons will be rendered as rectangles:

In Firefox they’re displayed as a box with the relevant Unicode code inside:

Accessibility

There seems to be a general feeling that icon fonts are bad for accessibility. SVG are not accessible by default, and its always been possible to use icon fonts in an accessible way. The most popular way to utilise icon fonts makes use of the CSS content property. Relatively recently, browsers added support for alt text when using pseudo-elements. Icons in the private use area of Unicode get ignored by screenreaders by default.

For icons that aren’t in the private use area, you can prevent superfluous content from being spoken by VoiceOver by using an empty string. In the following example, rather than announcing “black star favourite”, a screenreader will pronounce “favourite”.

.star::before {
          content:  "★" / "";
      }
<button><span class="star"></span>Favourite</button>

For icon-only buttons, you can use an aria-label attribute on the button, or give the icon itself some useful alt text:

.star::before {
          content:  "★" / "Favourite";
      }
<button class="star"></button>

If you’re using an icon in different contexts to mean slightly different things, you can easily change the alt text by using a data attribute:

.star::before {
    content:  "★" / attr(data-icon-alt);
}
<span data-icon-alt="Love it" class="star"></span>

Making an icon font accessible is no more difficult than making an SVG accessible.

Positioning icons

Chris Coyier has written of the frustration of positioning icon fonts:

It depends on line-height, vertical-align, letter-spacing, word-spacing, how the font glyph is designed (does it naturally have space around it? does it have kerning information?). Then the pseudo elements display type affects if those properties have an effect or not.

Soon you’ll also be able to throw text-box-trim & text-box-edge into the mix.

I’d add that controlling the size of an icon with font-size rather than width and height is non-ideal.

SVG isn’t always simple either. Trying to understand how viewBox works has caused me no shortage of annoyance over the years.

Conclusion: you should probably use SVG

There are so many different ways to make use of SVG that a direct comparison is difficult (I cover using SVG for icons in a separate article). The continuing popularity of Font Awesome is testament to the fact that the developer experience of icon fonts isn’t that bad. So long as you subset your font to remove unused characters, icon fonts aren’t a horrificly bad practice, but they’re not the best approach. Creating a custom icon font with a tool like icomoon is simple, but to the majority of web designers and developers, the technical innards of a font file are an opaque mystery. SVG has its own complexities, but is less of a black box.

A special case: Emoji

Emoji are a kind of icon but, unlike the arbitrary custom symbols you might use in your UI, emoji are standardised as part of Unicode. There’s a case for using a COLR font for custom emoji. If the font fails to load, the user still sees the default emoji of the operating system. Below is an example using an open source custom emoji font from Mozilla.

<span class="my-emoji-font">💥😲🔥💎💩😵👽👼🫠</span>

💥😲🔥💎💩😵👽👼🫠

Emoji fonts can get excessively large because there are over three thousand emoji. If you only need a limited set, an emoji font is the best option. If a color font is too large, you could consider splitting it into multiple files.