Cloud Four Blog

Technical notes, War stories and anecdotes

Simpler SVG Icons

As a follow-up to my post discouraging the use of icon fonts, I recently wrote about the SVG icon process we’ve been using at Cloud Four. While most of the feedback I received was positive, some felt the steps I described seemed complicated, even intimidating.

I can’t entirely disagree. Our designers find the process easy to use (save SVG to folder, optionally update YAML file, use in HTML), but it does require quite a bit of elbow grease up-front.

And while I’d argue that our process is only modestly more complex than comparable icon font setups (examples here, here, here and here), I completely understand how it might feel overwhelming to anyone new to the SVG format (which is most of us, if we’re honest).

So I thought it might be helpful (and fun!) to devote a post to some of the simpler ways you can start using SVG icons today. Whether these options serve as your “toe in the water” or your production-ready technique of choice, I think you’ll find all of them more approachable.

Use Images

SVG is an image format, which means you can use it just like an image:

<img src="my-icon.svg" alt="Niftiest Icon Ever">

Crazy, right? Or if your icons are purely decorative:

.my-icon {
  background-image: url("my-icon.svg");
  /* width, height, etc. */
}

Neither of these techniques inherit the parent element’s text color, a feature we’ve all taken for granted since 2011. But unless your site’s color palette is enormous, they’ll probably work just fine.

Copy and Paste SVG Source

We use this technique a lot when we’re prototyping. Most of the time, you can copy and paste the source of an SVG right into your HTML document:

See the Pen SVG icon pasted into HTML by Tyler Sticka (@tylersticka) on CodePen.

The markup’s a little messy and there’s no caching to speak of, but it works just fine. SVG elements can even inherit the parent text color!

(Tip: If you find yourself saving files over and over just to copy their contents, try clicking the “Show Code” button in Illustrator’s Export dialog instead. There’s also a “Copy Layer as SVG” plugin for Sketch.)

Use a GUI

Detail of IcoMoon interface

If you really want to compile your icons into a single resource but don’t want to mess with Gulp or Grunt, several web apps exist that will guide you through the process. Our favorites are:

  • IcoMoon: Can generate an SVG sprite or an icon font (or both) using any combination of stock icons or your own.
  • Grumpicon: Generates a CSS file with embedded SVG, as well as PNG fallbacks.

Both apps are free and easy to use, generating helpful code samples along with the assets themselves.

Use Pre-Made Icons

In addition to the stock icons available via the aforementioned IcoMoon, there are a growing number of SVG icon packs available. Some of the best we’ve seen so far:

  • Material icons: Google’s material design icons are open source, and available individually or as as a sprite.
  • Evil Icons: Free and open source icon pack with an intuitive markup syntax.
  • Glyph: Large icon pack available via Creative Commons.
  • Iconic: Not free, but SVG version is incredibly full-featured.
  • Resources like The Noun Project, SVG Icons and iconmonstr offer lots of great stock icons in SVG format.

If you have a favorite icon pack that doesn’t offer SVG as an option, ask for it! For example, here’s an open issue in the FontAwesome repo requesting the format.

And Many More!

SVG as a format is only as complicated as you need it to be. None of the aforementioned techniques are inherently less valid than the fancy-schmancy process we use at Cloud Four. You can start simple and stay there, or you can choose to expand on your process later on.

So give it a try! It’s probably less scary than you think.

Case Study: Our SVG Icon Process

When I wrote about why you shouldn’t use icon fonts in your next web project, I had no idea it would spark so much heated debate and intense discussion. One recurring question stood out to me: How might one actually implement an SVG icon system?

It’s a surprisingly difficult question to answer. Should you use <img>, <svg>, <object> or pure CSS? Will you combine your icons into a single sprite or keep them separate? If you do combine them, do you use an old-school technique or embrace SVG’s built-in features? Can JavaScript factor in somehow?

Though this variety of options might feel overwhelming, it’s ultimately a good thing. SVG’s versatility empowers us to craft the most appropriate solution for our audience and use-case.

So as much as I’d like to, I can’t say exactly how you should implement SVG icons in your next project. There’s no substitution for research and trying stuff out when it comes to finding the best fit for your project.

Instead, I’m going to show how we tend to assemble and implement SVG icons, and why we do it that way.

What We Do

The icon process we’ve adopted here at Cloud Four is a byproduct of the types of projects we take on, which tend to be large responsive redesigns or brand-new responsive interfaces. Our most common deliverables are in-browser mockups and pattern libraries. We often work with existing in-house teams, designing or extending icon systems based on their brand guidelines.

The front-end problems we solve tend to be those that are too complex or idiosyncratic to tackle with a framework or a simple content reflow. Our most common use of icons is to reinforce the meaning or relative importance of interface controls (a plus next to the word “Add,” a checkmark next to the word “Okay,” etc.).

Our Requirements

With this context in mind, we can assemble a list of requirements:

  • Accessibility: Because our icons represent or reinforce content, they should exist in markup.
  • Design: Our icons will most often coexist with text. They should inherit the text color and flow with the text by default.
  • Performance: Icons should be consolidated into a single, external sprite to avoid multiple requests and maximize caching.
  • Workflow: Whatever icon prep we can automate should be baked into our existing development tools (Node.js and Gulp).
  • Browsers: Our projects tend to be optimized for IE9+, Android 3+ and the usual array of less finicky modern browsers.

With requirements in hand, it’s time to build an SVG icon system!

1. Exporting Icons

Though our team digs Sketch for UI explorations, we still feel like Illustrator is a bit more intuitive for the design of icons and other illustrative elements.

We maintain a master icons.ai file in a shared spot (usually Dropbox), with each icon in the library residing in its own named artboard. We can see every icon in the context of its siblings, make any final tweaks for consistency, and simplify or combine any overlapping or unnecessary paths.

Screenshots of Illustrator artboards with icons

During this process, we purposely avoid preparing different rotations of the same icon. Traditionally, icon sets have exported separate assets for “left arrow,” “right arrow,” etc., but with SVG this repetition is redundant and unnecessary. Later on, we’ll walk through how to create simple rotational variations of the same icon.

Once everything’s looking good and feeling clean, we use Illustrator CC’s recently-improved exporting to generate SVGs from our artboards. After removing anything Illustrator over-enthusiastically prepended to our filenames, we’re ready to smoosh all of our icons into a single sprite.

2. Creating Our Sprite

As mentioned earlier, our team likes using Gulp for our local environment. If you’ve never used Gulp before, here’s a great article covering the basics. We’re going to write a Gulp task called icons this will compile a folder of separate, unoptimized SVG icons into a single, optimized sprite we can reference from our HTML.

Of the handful of plugins we’ve tried for accomplishing this sort of thing, our favorite is currently gulp-svg-sprite. It boasts a wealth of output modes and configuration options, making it the perfect choice for control freaks like yours truly.

For our icons task, we’ll be using the plugin’s symbol “mode.” This will transform each of our SVG files into a <symbol> element, which we’ll be able to reference by ID later.

Here’s what our SVG task might look like:

var gulp = require('gulp');
var svgSprite = require('gulp-svg-sprite');
 
var svgSpriteConfig = {
  mode: {
    symbol: {
      dest: '',
      sprite: 'icons.svg'
    }
  }
};
 
gulp.task('icons', function () {
  return gulp.src('./src/icons/**/*.svg')
    .pipe(svgSprite(svgSpriteConfig))
    .pipe(gulp.dest('dist'));
});

This task will:

  1. Find every SVG file in the src/icons directory.
  2. Pass those files to the gulp-svg-sprite plugin, which combines them into a single icons.svg file using the symbol output mode.
  3. Output the result to the dist directory.

Now if we run gulp icons, we should find a shiny new icons.svg file in our dist directory, ready to be referenced from our markup.

3. Including Icons in Our Markup

Now that we have our SVG sprite, we can reference it from our markup using <svg> and the <use> element:

<svg>
  <use xlink:href="icons.svg#back"/>
</svg>

This markup tells the browser “use the symbol with ID back from the file icons.svg.” This means our external file is nice and cacheable, and we can reference the same icon asset multiple times from the same file! Hooray!

Except, it looks like garbage:

Unstyled SVG icon in document

We haven’t told the browser how we want our icons to be sized, filled or aligned based on their surroundings. To do that, we need some CSS.

4. Styling Icons

We don’t want to style every svg because SVG can be used for a lot more than icons. Instead, we’re going to create a class. Our team likes using SUIT CSS naming conventions, so we’ll name our class Icon:

.Icon {
  /* Flow with text content */
  display: inline-block;
  /* Inherit the parent text color */
  fill: currentColor;
  /* Use the parent font-size for width and height */
  height: 1em;
  width: 1em;
  /* Vertically align icon with adjacent text */
  vertical-align: middle;
  /* Align more nicely with capital letters */
  position: relative;
  top: -0.0625em;
}

(Props to Chris Coyier and Jonathan Snook!)

Here’s the result after adding class="Icon" to our SVG element:

Styled SVG icon in document

Success! Our icons are successfully inheriting their size and color, and aligning nicely with adjacent type.

This accomplishes most of what we set out to do, but we haven’t taken advantage of what makes SVG special just yet. Let’s fix that.

5. Adding DRY Variations

Back when we were exporting icons, we only exported a single arrow asset (back.svg), the contents of which looked something like this:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path d="M22,10H6.83l3.59-3.59A2,2,0,0,0,7.59,3.59l-7,7a2,2,0,0,0,0,2.83l7,7a2,2,0,0,0,2.83-2.83L6.83,14H22A2,2,0,0,0,22,10Z"/>
</svg>

Let’s pop open our favorite code editor, and create a new forward.svg file to compliment it:

<svg xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink" 
  viewBox="0 0 24 24">
  <use xlink:href="#back" transform="rotate(180 12 12)"/>
</svg>

Here’s what’s going on:

  1. Our <svg> element is identical to back.svg, except we’ve added an xmlns:xlink attribute. This helps avoid errors during optimization by letting the plugin know that this SVG will reference other elements.
  2. Instead of including the forward icon’s path data, we reference our existing #back icon from a <use> element (similar to how we reference icons from our markup).
  3. The transform attribute rotates the icon 180 degrees from the center of our viewBox.

If we recompile our sprite, we should now be able to reference both icons from our markup:

Original arrow icon and derivative icon in document

Any changes made to back.svg will cascade to forward.svg (or any future variations). Plus, we save a small amount of file-size in the compiled sprite. Win/win!

6. Enforcing Mandatory Colors

Sometimes there are icons that really shouldn’t inherit everything about the parent. A common concern we hear from design teams is that the meaning of certain icons (in particular those representing “error” or “warning”) can be diluted over time if they are applied inconsistently.

In these cases, it’s helpful to remember that SVG elements are subject to the same style cascade as everything else. By specifying mandatory colors via attributes on the elements themselves (fill, style, etc.), we can overrule some or all color inheritance.

As an example, this error.svg file has fill attributes on the elements themselves:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path fill="#ff4136" d="M13.74,3l9,15.7A2.21,2.21,0,0,1,20.9,22H3.1a2.21,2.21,0,0,1-1.8-3.34l9-15.7A2,2,0,0,1,13.74,3Z"/>
  <path fill="#fff" d="M10.59,17.82a1.41,1.41,0,1,1,1.4,1.4A1.42,1.42,0,0,1,10.59,17.82Zm2.77-9.63a32.3,32.3,0,0,1-.61,4.5l-0.34,2.11H11.6l-0.34-2.11a32.77,32.77,0,0,1-.61-4.5A1.24,1.24,0,0,1,12,6.78,1.24,1.24,0,0,1,13.36,8.18Z"/>
</svg>

Even with our .Icon class applied, these colors will not be overruled:

Error icon that will not inherit its fill color from document

7. Improving Accessibility

Arguably the best reason to adopt SVG is to take advantage of its accessibility features. Thanks to Léonie Watson’s Tips for Creating Accessible SVG, we know to add the following elements and attributes to our icons:

<svg
  xmlns="http://www.w3.org/2000/svg" 
  viewBox="0 0 24 24"
  aria-labelledby="title desc">
  <title id="title">Back</title>
  <desc id="desc">A leftward arrow</desc>
  <path d="M22,10H6.83l3.59-3.59A2,2,0,0,0,7.59,3.59l-7,7a2,2,0,0,0,0,2.83l7,7a2,2,0,0,0,2.83-2.83L6.83,14H22A2,2,0,0,0,22,10Z"/>
</svg>

This insures that our icons have human readable fallbacks for blind and partially sighted people in a variety of user agents.

But there’s a problem with this approach. IDs must be unique, and we’ll be combining multiple files into a single SVG document. Our accessibility efforts will be thwarted if two <title> or <desc> elements attempt to use the same ID within the same document.

We could just be really diligent about choosing unique IDs, but that’s kind of a pain. If only we could manage these titles and descriptions in a central location, relying on our Gulp task to assign unique identifiers…

Luckily, we can! All we need to do is provide all our titles and descriptions in a separate YAML file:

back:
  title: Back
  description: A leftward arrow

error:
  title: Error
  description: A red sign with a white exclamation mark

forward:
  title: Forward
  description: A rightward arrow

search:
  title: Search
  description: A magnifying glass

Then update the Gulp task with the location of that file:

var svgSpriteConfig = {
  mode: { /* ... */ },
  shape: {
    // Titles and descriptions
    meta: './src/icons/icons.yaml'
  }
};

When we run gulp icons again, gulp-svg-sprite will add <title> and <desc> elements with unique, namespaced IDs and update the aria-labelledby attribute accordingly.

(It’s important to note that while we’ve specified <title> and <desc> elements within our sprite, you should still take care to use accessibility attributes in the page itself when the icon’s meaning is not re-enforced by its surrounding content.)

8. Supporting More Browsers

Time to address the elephant in the room…

Our icon sprite is a separate file, which is great for caching. But referencing symbols in an external file doesn’t work in Internet Explorer (though it does in Edge).

Icons not displaying in IE10

To address that, we’re going to use a polyfill called svgxuse. The script works by detecting failed external references, loading the referenced file via AJAX, injecting the sprite into the page itself, and updating the xlink:href attributes to point to the in-page resource. We like svgxuse because it minimizes the duplicated path data while retaining the ability for icons to reference each other.

The polyfill will work as-is, but we should make a couple of changes one small change to our task config to avoid any collisions with in-page content:

var config = {
  mode: { /* ... */ },
  shape: {
    // Titles and descriptions
    meta: SRC + '/icons/icons.yaml',
    // Add suffix to IDs
    id: {
      generator: '%s-icon'
    }
  }
};

Now we won’t have to worry about the SVG sprite being visible in Internet Explorer, and the IDs for our icons are far less susceptible to collisions once they coexist in the same document. Once we update our icon references to include the -icon suffix, we should have our target browsers covered:

Icons displaying in IE10 with svgxuse

Update: An earlier version of this post included specific configuration options for hiding the injected SVG sprite, but svgxuse handles that automatically now. Open source is awesome!

Putting It All Together

We made it! Here’s what we accomplished:

  • Our Gulp task will compile a folder of icons into a single, cacheable SVG sprite.
  • Individual icons can be referenced one or more times from anywhere in our HTML document.
  • By default, icons will base their position, size and color on their parent elements and surrounding text.
  • Icons may defy the default styles where appropriate.
  • To avoid repetition, some icons can be simple variations of others.
  • We can specify conflict-free accessibility details within icons.yaml.
  • When external references fail, the asset will be injected into the page itself.

You can see a live demo of the end result or browse the code.

Our completed SVG icon demo

Before we pat ourselves on the back too vigorously, it’s important to remember that that there is no one, true SVG icon process. Your ideal setup might involve the picture element, grunt-svgstore, SVGInjector or even an existing library. It can and should change based on the needs of your project. We modify ours all the time.

So consider this just one potential starting point for your own SVG icon system. I hope you’ll share what you come up with!

Responsive Images 101, Part 9: Image Breakpoints

I’ve dreaded writing this installment of the Responsive Images 101 series. Selecting image breakpoints is something everyone will face, and frankly, I have no good answers for you.

But sooner or later, we will all face the image breakpoints koan. We might as well start now.

What are responsive image breakpoints?

In our responsive layouts, breakpoints refer to the viewport sizes at which we make changes to the layout or functionality of a page. These typically map to media queries.

Responsive image breakpoints are similar, but slightly different. When I think about image breakpoints, I’m trying to answer two questions:

  • How many image sources do I need to provide to cover the continuum of sizes that this image will be used for?
  • Where and when should the various image sources be used?

The answers to these questions lead to different breakpoints than the criteria we use to select breakpoints for our responsive layouts. For our layouts, we follow Stephen Hay’s advanced methodology: We resize the browser until the page looks bad and then BOOOOM, we need a breakpoint.

With the exception of art direction, the main reason why we need multiple image sources has nothing to do with where the images look bad. We want to provide multiple image sources because of performance concerns, different screen densities, etc.

So we can’t simply reuse our responsive layout breakpoints for our images. Or I guess we can, but if we do so, we’re not really addressing the fundamental reasons why we wanted responsive images in the first place.

Image breakpoints for art direction is relatively easy

In situations where the image falls under the art direction use case, the art direction itself will often tell us how many image sources we need and when they should be used.

If you think back to the Nokia browser site example, we can tell when the image switches from landscape to portrait mode. When that switch occurs, we know we’re going to need a new source image.

However, this may only be part of the picture. What if one of the art directed images covers a large range of sizes. We may find that we still need to have multiple sources that don’t map to the art direction switch.

You can see an example of this in the Shopify homepage that we looked at in Part 8.

Shopify home page animated

Despite the fact that the image only has one major art direction change—from the full image to the cropped one—Shopify still provided six image sources to account for file size and display density.

<picture>
  <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">
</picture>

So knowing that an image falls under the art direction use case can give us some clues, but it doesn’t answer all of our questions about the necessary image breakpoints.

What about resolution switching breakpoints

This is where things really get tricky. At least art direction provides us with some hints about how many image sources might be needed.

So long as we’re downscaling flexible images, they will always look good. We can’t rely on them looking bad to tell us when we need to change image sources.

Let’s take a look at a resolution switching example:

Photo of Michelle Obama at three sizes

In this example, we have a photo of Michelle Obama where the image in the page is 400 x 602 pixels for the current viewport size. The largest size that the image is ever displayed at is 2000 x 3010 pixels. That large file is 250K.

We can simply shrink that 2000-pixel image, and it will look good. But it would be unnecessarily large. It would be better if we provided a smaller version like the one 800 x 1204 resolution image that is shown in the example. That image is only 73K.

We can all agree that when the image in the page is only 400 x 602 pixels in size, that providing an image that is 800×1204 and 73K is better than having people download the largest version of the image.

But why stop at 800×1204?

Michelle Obama example with a fourth size at 600px wide

If we provided another image source that was 600×903 pixels wide, it would only be 42K. That saves us 31K (42%) from the 800×1204 image.

Well shoot. A savings of 42% is a big deal. Maybe we should keep going. 500 pixels wide? 450 pixels wide?

Photo of Michelle Obama with examples at 450 and 500 pixels wide

Each smaller image source offers the potential for substantial savings over the previous size. If we keep on this track, we eventually end up with an image source that is the exact size of the image in the page.

So here’s the question that has vexed me about image breakpoints. How do I know when an image source is too big for the size that the image is being used in the page?

The answer is that unless the image source matches exactly the size that the image is being displayed in the page, it is always going to be too big. There is always going to be an opportunity to optimize it further by providing a smaller image.

Why not provide the exact size of the image?

At this point, you may be wondering why we simply don’t provide the exact size of that the image is going to be used in the page.

First, the whole point of flexible images in responsive design is to provide images that scale as the size of the viewport changes. If we provided images that were exactly the size used in the page, we’d likely need to download new images whenever the viewport size changes or the device was rotated.

Second, it is unrealistic to provide images at any size imaginable. Yes, we can dynamically resize images, but when we resize images, the server needs to do that work which slows down delivery of that image to the browser.

For this reason, most larger sites cache images on content delivery networks (CDN). Caching every image size possible on the CDN would be incredibly expensive.

Finally, the browser doesn’t know the exact size of the image in the page when it starts downloading. That’s what got us to new responsive images standards in the first place!

Possible ways to pick image breakpoints

As I mentioned at the beginning, I have no rock solid solutions for how to pick the number of image sources that you need. Instead, I want to describe some different ways of looking at the problem that may help inform your decisions.

Winging it (aka, matching your layout breakpoints)

Someone on your team says, “Hey, how many source images do you think we need for these product photos?”

You ponder for a moment and say, “Hmm… how about three? Small, medium and large.”

Don’t be ashamed if you’ve done this. I’m pretty sure almost every person working on responsive images has done this at some point.

Perhaps your organization still thinks about mobile, tablet and desktop which makes small, medium and large make sense.

Or maybe you take a look at the range that the image will be displayed and make your best guess. Perhaps you simply look at the number of major layout breakpoints and decide to do the same for your image breakpoints.

I completely understand. And this is better than providing one huge image for all viewports.

But it sure would be nice to have more logic behind our decisions.

Testing representative images

If guessing doesn’t seem like a sound strategy, then let’s insert a little science into the art of picking image breakpoints. We can take a look at some representative images and figure out how many breakpoints they need.

The hardest part of doing this is determining representative images, or figuring out if you have any at all.

For some sites, all the photographs may have a particular style dictated by the brand. If that is the case, finding representative images is easy. Pick a few images and then resize them and save them at sizes ranging between the largest and the smallest images until you feel like you’ve got decent coverage.

Of course, if your site has a diversity of image styles, finding representative images can be nearly impossible.

Memory usage influencing the distribution of image breakpoints

Earlier this summer, Tim Kadlec gave a presentation on Mobile Image Processing. In that presentation, he took a look at the memory usage of flexible images in responsive designs.

What Tim showed is that as an image gets bigger, the impact of resizing an image gets larger.

Memory usage of two different images

In the example above, reducing a 600×600 pixel image by 50 pixels in each direction results in 230,000 wasted bytes versus the 70,000 wasted bytes caused by reducing a 200×200 image by 50 pixels in the exact same way.

Knowing this tells us a bit about how we should pick our breakpoints. Instead of spacing out breakpoints evenly, we should have more breakpoints as the image gets larger.

graph showing more breakpoints at large sizes

Unfortunately, while this tells us that we should have more breakpoints at larger sizes, it doesn’t tell us where those breakpoints should be.

Setting image breakpoints based on a performance budget

What if we applied the idea of a performance budget to responsive images? What would that look like?

We’d start by defining a budget for the amount of wasted bytes that the browser would be allowed to download above what is needed to fit the size of the image in the page.

So say that we decided that we had a performance budget of 20K for each responsive image. That would mean that we would need to make sure that the various sources that we’ve defined for the image are never more than 20K apart.

When we do this, we find that the number of image breakpoints change wildly based on the visual diversity of the image and the compression being used.

Let’s take a look at three sample images.

Time Square — 8 Image Breakpoints

Times Square

This image has a lot of visual diversity. The variations in colors and textures means that JPEG’s lossy compression cannot do as much without damaging the image quality.

Because of this, there are eight image breakpoints—set at 20k intervals—between the smallest size of the image (320×213) and the largest size of the image (990×660).

Breakpoint # Width Height File Size
1 320 213 25K
2 453 302 44K
3 579 386 65K
4 687 458 85K
5 786 524 104K
6 885 590 124K
7 975 650 142K
8 990 660 151K

Morning in Kettering — 3 Image Breakpoints

Morning in Kettering

Unlike the Times Square image, this image has a lot of areas with very similar colors and little variation. Because of this, JPEG can compress the image better.

On an image that can be compressed better, our 20K budget goes farther. For this image, we only need three image breakpoints to cover the full range of sizes that the image will be used at.

Breakpoint # Width Height File Size
1 320 213 9.0K
2 731 487 29K
3 990 660 40K

Microsoft Logo — 1 Image Breakpoint

Microsoft Logo

This is a simple PNG8 file. At its largest size (990×660), it is only 13K. Because of this, it fits into our 20K budget without any modifications.

Breakpoint # Width Height File Size
1 990 660 13K

Take a look at the other images on a sample page we created. See how the number of breakpoints vary even through all the images start with the same resolution end points.

Now, I’m not suggesting that you manually decide on image breakpoints for every single image. But I can envision a future where you might be able to declare to your server that you have a performance budget of 20K for responsive images and then have the server calculate the number of image sources on a per image basis.

I’ve written in more detail about performance budgets for responsive images in the past. If you end up implementing this approach, please let me know.

Setting image breakpoints based on most frequent requests

At a recent Responsive Images Community Group (RICG) meeting, Yoav Weiss and Ilya Grigorik discussed a different way of picking image breakpoints based on the most frequently requested image sizes.

For both Yoav, who works at Akamai, and Ilya, who works at Google, one of the problems they see with multiple image sources is storing all of those sources on edge servers where storage is usually more limited and costs are higher.

Not only do companies like Akamai and Google want to reduce the number of images stored at the edge, but the whole purpose of their content delivery networks is to reduce the amount of time it takes for people to render a web page.

Therefore, if they can cache the most commonly requested image sizes at the edge, they will deliver the fastest experience for the majority of their users.

For these organizations, they can tie their image processing and breakpoints logic to their analytics and change the size of the images over time if they find that new image sizes are getting requested more frequently.

When combined with the new HTTP Client Hints feature that Ilya has championed, servers could get incredibly smart about how to store images in their CDNs and do so in a way that requires little to no decision-making by designers and developers.

Humans shouldn’t be doing this

I believe that in a few years time, no one will be talking about how to pick responsive image breakpoints because no one will be doing it manually.

Sure, we may still make decisions for images that fall into the art direction use case, but even then, we’re probably not going to make finite decisions about every image source. We’ll handle the places that require our intervention and let our image processing services handle the rest.

There is a ton of benefit to either picking image sources based on a performance budget or based on the frequency with which different sizes of the image are requested. But either of these solutions are untenable as part of a manual workflow.

In the future, our typical workflow will be that we upload the highest quality image source into our content management system or image processing system and never have to think about it again.

Part 10 of a 9-part series

This started as a 9-part series, but there is always more to discuss when it comes to responsive images. Read for the conclusion of this series where I’ll provide some essential resources and talk about what the future holds for 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

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.

Whoops.

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