Cloud Four Blog

Technical notes, War stories and anecdotes

Seriously, Don’t Use Icon Fonts

Icons are everywhere. These “little miracle workers” (as John Hicks described them) help us reinforce meaning in the interfaces we design and build. Their popularity in web design has never been greater; the conciseness and versatility of pictograms in particular make them a lovely fit for displays large and small.

But icons on the web have had their fair share of challenges. They were time-consuming to prepare for every intended display size and color. When high-resolution displays hit the market, icons looked particularly low-res and blocky compared to the text they often accompanied.

So it’s really no wonder that icon fonts became such a hit. Icons displayed via @font-face were resolution-independent and customizable in all the ways we expected text to be. Sure, delivering icons as a typeface was definitely a hack, but it was also useful, versatile, and maybe even a little fun.

But now we need to stop. It’s time to let icon fonts pass on to Hack Heaven, where they can frolic with table-based layouts, Bullet-Proof Rounded Corners and Scalable Inman Flash Replacements. Here’s why…

Screen Readers Actually Read That Stuff

Most assistive devices will read aloud text inserted via CSS, and many of the Unicode characters icon fonts depend on are no exception. Best-case scenario, your “favorite” icon gets read aloud as “black favorite star.” Worse-case scenario, it’s read as “unpronounceable” or skipped entirely.

They’re a Nightmare if You’re Dyslexic

Screenshot of icon issue after replacing fonts on GitHub

Many dyslexic people find it helpful to swap out a website’s typeface for something like OpenDyslexic. But icon fonts get replaced as well, which makes for a frustratingly broken experience.

They Encroach on Emoji Turf

Most of the time, we rely on automated tools to choose which Unicode characters are assigned to which icon. But Unicode’s also where our beloved emoji live. If you aren’t careful, they can overlap in confusing (albeit hilarious) ways. My favorite example: Etsy’s “four stars and a horse” bug. More recently, our own Jason Grigsby encountered random fist-bumps on ESPN’s site.

They Fail Poorly and Often

Microsoft's example missing glyph characters

When your icon font fails, the browser treats it like any other font and replaces it with a fallback. Best-case scenario, you’ve chosen your fallback characters carefully and something weird-looking but communicative still loads. Worse-case scenario (and far more often), the user sees something completely incongruous, usually the dreaded “missing character” glyph.

Custom fonts shouldn’t be mission-critical assets. They fail all the time. One look at Bootstrap’s icon-related issues and it’s no wonder why they’re removing them entirely from the next version.

Worse still, many users will never see those fonts. Opera Mini, which frequently rivals iOS Safari in global usage statistics with hundreds of millions of users worldwide, does not support @font-face at all.

They Never Looked Right

Detail of Stackicons' octocat in IE11

The way browsers hint fonts to optimize legibility was never a good fit for our custom iconography, and support for tweaking that behavior is all over the place.

Multicolor icons are even worse. The technique of overlaying multiple glyphs to achieve the effect is impressively resourceful, but the results often look like their printing registration is misaligned.

You’re Probably Doing It Wrong

“But Tyler,” I hear you say. “You’ve completely ignored Filament Group’s Bulletproof Icon Fonts, complete with feature tests and sensible, content-driven fallback solutions.”

And you’re right. Those techniques are great! If you’re using an icon font, you should definitely follow their recommendations to the letter.

But you probably won’t.

What you’ll probably do is adopt whatever your framework of choice has bundled, or drop in some massive free icon font you can use right away. You won’t modify how they work out of the box because that’s really hard to prioritize, especially when they look great on your monitor with virtually no effort at all.

Or maybe you will do the work, designing and curating a custom icon font, choosing your Unicode characters carefully, documenting and evangelizing the importance of implementing your icons in an accessible way with appropriate fallbacks. Then one day, Dave forgets to add a fallback image to that iconographic button he added (which looks great, by the way), which Roberta reuses for her related Pull Request, and before you know it, your app has devolved into a fragile, hack-littered wasteland once more.

These examples are not hypothetical (though names have been changed to protect the innocent). I’ve seen them happen to multiple organizations, all of them starting with the best possible intentions.

There’s Already a Better Way

SVG is awesome for icons! It’s a vector image format with optional support for CSS, JavaScript, reusability, accessibility and a bunch more. It was made for this sort of thing.

But I hear a lot of excuses for why teams avoid using it, even for brand-new projects. Here are a few…

“SVGs can’t be combined into sprites.”

They totally can. There are even really great tools like svg-sprite and IcoMoon that can help automate that process.

“SVGs are larger in file size.”

Usually when I hear this, the team’s comparing a single binary icon font to multiple, uncompressed SVG files. The gap narrows dramatically when you optimize your SVGs, combine reusable ones into sprites, and deliver those with active Gzip compression or embedded in-page.

Occasionally I’ve heard the gap is still too wide when hundreds of icons are included. This begs the question: Why are you including hundreds of icons on every page?

“The icon markup is too verbose by comparison.”

Let’s compare:

<!-- Typical @font-face icon: -->
<span class="icon icon-search" aria-hidden="true"></span>
<!-- Typical SVG icon: -->
<svg class="icon">
  <use xlink:href="path/to/icons.svg#search"/>

The SVG markup is barely more verbose, and way more descriptive and semantic than some empty, ARIA-hidden <span> element.

“Browser support for SVG is worse.”

As of this writing, global support for SVG is up to 96%… 4% higher than the same stat for @font-face. Plus, SVGs are way more accessible and their fallbacks are much more straightforward.

“The framework we chose already has an icon font.”

If your framework told you to jump off a bridge, would you?

Don’t Be “Table Guy”

I was in school when the Web Standards movement hit critical mass. While the majority of my instructors saw the merits of semantic markup and embraced it wholeheartedly, one passionately held out. “Table Guy” argued that no layout tool could usurp <table>, that it was inherently better-suited for crafting grid-based designs. He boasted of how quickly and easily he could achieve the “Holy Grail” layout with his trusty table cells. He cited the wealth of cross-browser inconsistencies that continued to plague CSS.

Table Guy and I kept in touch. Today, he freely admits he was wrong about CSS. He feels embarrassed to have been so married to a technique that was so clearly misappropriated in hindsight.

If you won’t stop using icon fonts for people with screen readers, people with dyslexia, people with browsers that don’t support @font-face, people who randomly didn’t load the icon font once for some reason, or designers who just want their icons to look right on-screen…

Then do it for yourself. Don’t be Table Guy.

Dare to Repeat Yourself (At First)

It was mid-afternoon on a Wednesday when my team started finding strange bugs in older versions of Internet Explorer. At first these appeared to be unrelated… until we noticed seemingly random chunks of style appeared to be missing entirely. What was going on?

After some digging, we found the issue: Our project had exceeded old IE’s infamous CSS selector limit. Weeks prior, I’d lost an argument to resist including a sizable framework in the project. Mentally, I was already patting myself on the back. “I told you so,” I practiced saying in my own head.

Then I looked at the compiled CSS, and realized it was actually my fault.


I’d designed a custom interface element that was pretty complex. Because we were using Sass, I used some fancy mixins and loops to avoid repetition between a handful of breakpoint-specific modifiers. It was easy to read, maintain and modify.

It was so easy, in fact, that I failed to notice that the compiled CSS made up about 25% of the total project’s styles! Even more embarrassingly, I discovered that I could replicate the exact same functionality without most of the loops. I ended up reducing the selector count for that component from 1,207 to just 42 (seriously).

While it was great to find and fix the problem, it shook me up a little. Sass didn’t write crap code; I did. I was so focused on automating my repetitive solution that I hadn’t stopped to ask myself if it was even the right solution.

We recently started using PostCSS for a few of our projects. Every PostCSS feature is a plugin, which we include as needed. So far, we’ve yet to include plugins for nesting, mixins or loops.

Every time we’ve thought to include those features, we’ve instead found a simpler way to do the same thing. Nesting gives way to descendent class names, mixins become utilities, loops are questioned entirely. The initial pain of having to repeat ourselves motivates us to approach the problem in a different way. Repetitive selectors that survive this process are intentional, because a human being actually wrote them.

I know that’s probably silly. It’s definitely not DRY. But there’s a fine line between “smarter stylesheets” and “dumber designer.” Embracing painful repetition by nerfing my preprocessor (especially in combination with analysis tools like Parker) helps me draw that line.

Responsive Images 101, Part 8: CSS Images

Most of the time when people refer to responsive images, they are referring to inline images, not CSS images.

This is because before <picture> and srcset there were no good solutions for inline responsive images. When it comes to CSS images, we could always use media queries. So why worry?

But now it is time to revisit responsive CSS images and look at the solutions anew based on what we’ve learned about inline images.

image-set() for resolution switching

Just like when we’re working with inline images, one of the first questions we’ll need to ask ourselves is whether we’re dealing with the resolution switching or art direction use case.

For resolution switching, we should strive to provide the browser with options and let the browser pick the best possible image. The browser is in a better position to know what image will work best based on user preference, network conditions, etc.

To provide the browser with options, we should use the image-set() syntax.

image-set() syntax repeated below

You may notice some similarity between image-set() and srcset. In fact, srcset was modeled after image-set().

background-image: image-set( "foo.png" 1x, "foo-2x.png" 2x);

Like srcset, image-set’s value contains a comma-separated list of image URIs along with a display density descriptor. If a display density descriptor isn’t provided, it is assumed to be 1x.

However, image-set() does not support width descriptors yet. The plan is to refine image-set() to provide feature parity with srcset.

While most of the examples you will see for image-set() show it applied to background-image, it can be applied to any CSS property that accepts images.

image-set(): The forgotten responsive images standard

image-set() was the first responsive images specific syntax, and as mentioned, it was the foundation for srcset.

However, because we had solutions for CSS responsive images using media queries, image-set() was ignored by nearly everyone. The Responsive Images Community Group didn’t spend much time discussing it. Browsers didn’t prioritize implementing it.

Once we were nearing completion of the <picture> and srcset standards, we looked around and realized that we had neglected image-set(). Work is underway to increase the functionality of image-set() to bring it in line with srcset.

But as of publication, despite being the first responsive images standard, browser support for image-set() is lacking. It is available with a webkit prefix in Chrome, Opera, and Safari. Neither Firefox nor Microsoft have implemented it yet.

So why include it in this Responsive Images 101 series?

Because image-set() is the correct solution for resolution switching. When image-set() is widely supported, we should use it for all of the same reasons we use srcset instead of <picture> with the media attribute when we’re dealing with resolution switching.

Until image-set() is widely supported, you’ll probably end up using the CSS art direction solution for resolution switching.

Art direction

What is the CSS solution for art direction? Media queries.

It’s that simple. In fact, I’m going to assume you know media queries so no syntax sample here.

But as long as I’ve got your ear, make sure your media queries for images don’t overlap or you’ll end up with duplicate downloads. If you have any doubts, check out Tim Kadlec’s Media Query & Asset Downloading Results.

Resolution media queries

If you want to support high density displays in art direction, you’ll probably want to use the new resolution media queries.

Resolution media query syntax. Repeated below.

The resolution media query allows you to apply specific CSS rules to devices that meet the display density that you define.

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
/* High density stuff here */

(Much thanks to CSS Tricks for this syntax sample.)

The first thing you’ll notice in the syntax above is that we’re including a -webkit prefixed media query. This is for devices that support the old device-pixel-ratio syntax. The only devices that got in the wild with this syntax used the -webkit prefix which is why it is the only one listed.

The syntax going forward is the resolution media query. In our example, we’re using min-resolution, but as you probably guessed, there is a complimentary max-resolution feature that can be used instead.

The resolution media query can check one of three things:

  • dpi — dots per inch
  • dpcm — dots per centimeter
  • dppx — dots per px unit

The first two are fairly straight-forward, but I found dppx confusing. The Mozilla Developer Network documentation defines ddpx thusly:

This unit represents the number of dots per px unit. Due to the 1:96 fixed ratio of CSS in to CSS px, 1dppx is equivalent to 96dpi, that corresponds to the default resolution of images displayed in CSS as defined by image-resolution.

Confused? I certainly was when I first read it.

Here’s the way I’ve begun to think about it, the idea of 1x, 2x, 3x, etc. is based on an imprecision. The value of 1x on some devices is different than others because some devices are 72dpi or 96dpi or whatever.

But from a CSS perspective, these differences don’t matter. The CSS Working Group has decided that there will always be a 1:96 fixed ratio of CSS inches to CSS pixels.

So while 1x might leave things up to interpretation because of 72dpi vs. 96dpi screens, 1dppx will always be what you and I think of as “1x”.

You may be asking yourself, why was 1x sufficient for srcset and image-set, but for min-resolution, it was necessary to use dppx?

I don’t know. All I know is that you can think of 1dppx as 1x, 2dppx as 2x, and so on. At this point, I’ve just accepted the inconsistency and decided to move on with my life. I recommend you do the same. ;-)

Now comes the hard part

Believe it or not, responsive images syntax is the easy part. In Part 9, we’ll discuss the vexing challenge of picking your image breakpoints.

Responsive Images 101 Series
  1. Definitions
  2. Img Required
  3. Srcset Display Density
  4. Srcset Width Descriptors
  5. Sizes
  6. Picture Element
  7. Type
  8. CSS Responsive Images
  9. Image breakpoints
  10. Conclusion

Responsive Images 101, Part 7: Type

So far we’ve been focused on how to make responsive images more performant. That’s essential, but at the end of the day, we still have the same old image on the page.

Now, it is time for the fun stuff!

Type attribute

Have you ever bemoaned the fact that your options for reliable image formats are limited to jpg, png, and gif? Ever wanted found yourself wondering if there is enough browser support for new formats like SVG or webp?

If so, you’re going to love the type attribute.

Type syntax repeated below

The type attribute can be added to <source> elements inside a <picture> element and allows you to declare different image types that the browser can choose from:

  <source type="image/svg+xml" srcset="logo.xml">
  <source type="image/webp" srcset="logo.webp"> 
  <img src="logo.png" alt="ACME Corp">

This new type attribute is modeled on the <video> element’s type attribute and works the same way.

The browser will pick the first source where the declared image type is one that it supports. If it doesn’t recognize any of the source types, it will use the <img> element’s src or srcset declarations.

The value is a MIME type for the image format being referenced in the srcset attribute. If you have multiple image URIs listed in the srcset attribute, they should all match the declared image MIME type.

Of course, you can combine type with the sizes and/or the media attributes as well. All three of these attributes are optional and can be combined to accomplish whatever you need.

The srcset attribute is required for all <source> elements. Both display density and width descriptors can be used with the type attribute.

Do you need the media attribute?

I’ve gotten in the habit of telling people that they shouldn’t use the <picture> element for most responsive images. That is both true and a bit misleading.

So now that you’re up to speed on all of the inline responsive images techniques, let’s break it down:

  • Most images on the web fit the resolution switching use case.
  • When you’ve got a resolution switching use case, you want to empower the browser to make the best choice possible. This is what srcset is designed to do.
  • When you use the <picture> element with media attributes, you’re dictating to the browser what images it should use.

Therefore, you can and should use <picture> when you want both resolution switching and to support multiple image formats. Just leave off the media attribute so that the browser can do its thing.

Progressive enhancement for image formats

So far in this series, I’ve tried to keep things professional, but lighthearted. But that ends here because…


Phew, had to get that out of my system.

For years we’ve wanted to be able to use different image formats, but had to wait for wide spread adoption of the format.

But even when we finally felt we could switch, we always knew that we were ignoring old browsers. We’d chalk it up to progress and hope it didn’t affect too many people. Or maybe we never switched to the new image formats for fear of leaving people out.

But <picture> plus the type attribute gives use a way out of this conundrum. We can use progressive enhancement for image formats right now.

Sara Soueidan described how she is starting to do this for SVG with PNG fallbacks instead of all the hacks we used to use.

But it’s not just SVG and webp. What about JPEG-2000? JPEG-XR? APNG?

If you can find browsers that support an image format and you believe it can provide some value to your users, then there is no reason not to use that format so long as you provide alternatives.

JPEG-2000? Yes please!

A wonderfully in-depth article by Zoltan Hawryluk opened my eyes to the benefits of different image formats and in particular JPEG-2000 for alpha transparency images.

In one of the examples in Zoltan’s article, he shows dice placed above a multi-color background. To pull it off requires an alpha-channel transparency.


The file sizes of the dice image are:

320×240 2K 22.6K 55.2K 112.1K
600×450 13.5K 48.5K 14.3K 26.6K
1024×768 19.2K 95.7K 325.7K 56K

Look at those savings. The dice PNG at 1024×768 is 325.7K. The same image as JPEG-2000 is only 19.2K. That’s insane!

I know what you’re thinking. That’s wonderful, but no browsers support JPEG-2000.

That’s what I thought too, but I was wrong. Both desktop and Mobile Safari already support JPEG-2000.

Now before you go converting all of your images to JPEG-2000, heed Zoltan’s warning:

As you can see, the numbers for JPEG-2000 are especially impressive. However, the file sizes of the alternate images will vary depending on the characteristics of the original image… While alternative image formats may give better results, sometimes they don’t.

So it will depend on the image and the design. But you can see how there can be significant benefits for some of your users depending on the types of images and the formats their browsers support.

Brave new world of image formats

I don’t expect anyone to go off and immediately start using JPEG-2000. There’s a lot more work to be done in this space so that we know what image formats make sense and when to use them.

Simply getting tools in place to output the various images formats can be difficult. Zoltan includes information at the bottom of his article on what tools he used to create the different formats.

Other than the command-line tools, I find the tools to be awkward and rough around the edges. There has been no incentive for companies like Adobe to add rich support for image formats like JPEG-2000 because no one could use them until now.

We have a lot of experimenting to do. I can’t wait!

What about CSS?

Everything we’ve talked about so far has been for inline responsive images. Because we already had media queries in CSS, inline responsive images were the biggest challenge and most of the focus has been on them.

But there are some new standards for responsive images in CSS and a few tricks you should know. Continue on for Part 8: CSS Responsive Images.

Responsive Images 101 Series
  1. Definitions
  2. Img Required
  3. Srcset Display Density
  4. Srcset Width Descriptors
  5. Sizes
  6. Picture Element
  7. Type
  8. CSS Responsive Images
  9. Image breakpoints
  10. Conclusion

Responsive Images 101, Part 6: Picture Element

In Parts 3, 4 and 5, we focused on solutions for resolution switching. Now it is time to look at how to solve for art direction.

The picture element—the media attribute in particular—is designed to make art direction easy.

Picture syntax, replicated below

The <picture> element contains a series of <source> child elements followed by the required <img> element. The source elements work similarly to the child sources of the video element.

  <source media="(min-width: 900px)" srcset="cat-vertical.jpg">
  <source media="(min-width: 750px)" srcset="cat-horizontal.jpg">
  <img src="cat.jpg" alt="cat">

Each source has a required srcset attribute along with optional attributes including media, sizes and type. Both sizes and srcset on a <source> element work exactly the same as they do on an <img> element.

We're going to focus on the media attribute for now.

Media attribute

The value of the media attribute is a media query. Unlike the media condition that the sizes attribute uses, this is the full media query that you've come to know and love.

As the browser looks through the list of source elements, the first source whose media query matches is the one that is used. If no media queries match, then the <img> element is used.

Media attribute is a directive, not a suggestion

Unlike srcset and sizes, when you use the media attribute, you are dictating to the browser which source should be used.

The browser has no discretion to pick a different source. It must use the first <source> element whose media attribute matches the current browser conditions.

This is why the <picture> element with the media attribute is perfect for art direction. In the art direction use case, designers need to ensure that the image used at a particular viewport size is exactly the one they intend otherwise their design may break.

Let's take a look at this in action.

Picture element in the wild

Shopify uses the <picture> element for art direction. Shopify's home page highlights one of their customers, Corrine Anestopoulos, the Founder of Biko Jewellery.

Animation showing changes as Shopify home page viewport shrinks

On narrow screens, the photo of Ms. Anestopoulos is cropped. Because the image is no longer simply being scaled down, this is considered art direction.

The markup that Shopify uses combines the <picture> element with srcset display density descriptors. I've simplified the markup to remove long image paths and included it below:

  <source srcset="homepage-person@desktop.png, homepage-person@desktop-2x.png 2x"       
          media="(min-width: 990px)">
  <source srcset="homepage-person@tablet.png, homepage-person@tablet-2x.png 2x" 
          media="(min-width: 750px)">
  <img srcset="homepage-person@mobile.png, homepage-person@mobile-2x.png 2x" 
       alt="Shopify Merchant, Corrine Anestopoulos">

Looking at the code in more detail, what we see is the Shopify has three different image breakpoints. The image is a fixed width at each breakpoint—it jumps from size to size instead of flexing between breakpoints.

Because the image is fixed width, srcset display density descriptors make sense. So for each breakpoint, Shopify has defined a 1x and 2x source file. It breaks down like this:

  • <source … media="(min-width: 990px)"> — The largest image size, which Shopify calls desktop, is the first source. The media attribute tells the browser that this source should only be used if the viewport is larger than or equal to 990 pixels wide.

  • <source … media="(min-width: 750px)"> — The second source, the "tablet" image, will be used for viewports larger than or equal to 750 pixels. Because the first source takes effect at 990 pixels and the browser selects the first source that matches, the effective range of the second source is from 750 to 989 pixels.

  • <img> — If there are no matches for the two sources, then the viewport must be smaller than 750 pixels wide. When that is the case, the srcset on the <img> element will be used. This "mobile" image is the cropped image used for small screens.

If the images were flexible instead of fixed width, Shopify could have used <picture> with srcset width descriptors instead of display density descriptors.

One final trick

If you have art direction, you need the picture element. But the writers of the picture specification had one final gift to give us web authors and it's a big one.

Continue with Part 7: Type to learn about an exciting new world of image formats.

Responsive Images 101 Series
  1. Definitions
  2. Img Required
  3. Srcset Display Density
  4. Srcset Width Descriptors
  5. Sizes
  6. Picture Element
  7. Type
  8. CSS Responsive Images
  9. Image breakpoints
  10. Conclusion