Working with Images and Videos in Responsive Web Design - Mastering Responsive Web Design with HTML5 and CSS3 (2015)

Mastering Responsive Web Design with HTML5 and CSS3 (2015)

Chapter 6. Working with Images and Videos in Responsive Web Design

I've always called images the "ugly child" of RWD. Why? Until the last minute, I always tried to avoid having to deal with them. Do I use image sprites? If so, do I export my transparent PNG as an 8 bit or 24 bit, or 32 bit? Some legacy IEs don't support PNG with alpha channel, so I have to export a GIF sprite. I can use SVG instead, but IE8 and below don't support SVG. I can use icon fonts, but what happens if the icon font doesn't load? I'm going to have to look up some analytics then. There's a new iDevice with a new type of high-density screen? Now I have to export two (or more) images every single time. Great! But I can't serve a high-quality image that's more than double the size of the regular size image to small-screen devices! Yeah, it may look good but it'll take forever to download, they just might as well leave the site even before the first H1 loads.

You get the picture. That's barely scratching the surface of working with media in RWD.

Some of these thoughts are still very alive today, but I've learned through the years that with a bit of common sense and staying on top of the technologies that address all these issues, having a straightforward system to deal with images (and video) can go a long way.

As in the other chapters, we are going to keep things simple but meaningful. There is no silver bullet when it comes to images, specifically in RWD, we can stay here all day long and that's certainly something we don't want with this book. I want you to go build awesome responsive sites as soon as possible. But I do encourage you to spend some time researching a bit more about images for RWD; it is truly a memorable topic among the web design and developer communities.

In this chapter, we're going to address the following topics:

· Tips for exporting images and considerably reducing their final file size while maintaining the image quality.

· How and when to use the srcset and sizes attributes, and the <picture> element.

· Using Retina.js.

· Making videos responsive.

· Using FitVids.js.

· Using vector formats: Icon Fonts and SVGs.

· Using the right CSS image replacement technique.

Now, here's the image we're going to use in our examples:

Working with Images and Videos in Responsive Web Design

These awesome individuals are two Kung Fu grandmasters from the Shaolin Temple in China. Their names are Shi DeRu and Shi DeYang.

Note

Shi DeRu and Shi DeYang by Shi Deru (Shawn Xiangyang Liu), who is the sole owner and copyright-holder of the authenticity of the picture taken at the Shaolin Temple's front gate. It is licensed under CC BY-SA 3.0 via Wikipedia. It can be found athttp://en.wikipedia.org/wiki/Shaolin_Kung_Fu#/media/File:Shi_DeRu_and_Shi_DeYang.jpg.

Since we are also mastering RWD with HTML5 and CSS3, I thought this photo fit right in with our mission.

The properties of the original image of the Kung Fu grandmasters I'm going to describe will help set a baseline to understand the before/after effect when optimizing images for RWD.

Here are the original image's properties:

· It is a 24-bit JPG image.

· The file size is 556KB, but it is compressed thanks to the magic of the JPG algorithm (about 12 MB decompressed).

· The dimensions are 2496 x 1664 pixels, which is about 4.15 megapixels. To put it in perspective, this image has more resolution than my 55" LED TV in my living room.

By the end of book, I assure you two things. One, you'll be absolutely ready to build responsive sites and apps. Two, when it's time to start a new project, you're going to get up from your seat and strike the same pose these grandmasters are doing.

Image editing is out of scope of this book and the following steps will require image manipulation of some sort. At this point, you can use your favorite image editor of choice. I personally use Adobe Fireworks (indeed I do), but the vast majority uses Photoshop.

If you don't use any of those, you can always use GNU Image Manipulation Software (GIMP) or Paint.NET—both are free. You can download them from here:

· GIMP: http://www.gimp.org/

· Paint.NET: http://www.getpaint.net/

You can also use an online image editing tool. However, I have to admit though that I've never used any of them, so I can't recommend one. At this point what I can say is try some of them out and choose the one that best fits your needs.

Let's get started.

Tips for reducing the file size in images for RWD

In design, the rule of thumb when creating copies of an image is to go from large to small—never the other way around. In other words, the larger the image, the better its subsequent copies will be.

Resizing

Just by resizing the image from 2496 x 1664 pixels to 1024 x 683 pixels, the file size is now 331 KB. This is nearly a 40 percent reduction in file size from 556 KB. A huge improvement, but we're not there yet.

Blurring the background

Blurring the background is actually quite effective in itself, but it also has another benefit from the art direction point of view: It helps draw attention to the important part(s) of the image.

After blurring the background, the file now weighs 185 KB. That's about a 67 percent reduction in file size from 556 KB. We're starting to get somewhere.

Here's the new image with the blurred background:

Blurring the background

A huge win for optimization!

Darkening or lightening the less important areas

Darkening or lightening the less important areas is very subjective and many may not necessarily agree with it. Under special circumstances, this process—just like the background blurring technique—can help reduce the file size and bring the important parts of the image out.

What we're basically trying to accomplish by darkening or lightening an image is to reduce the amount of colors by creating solid color areas, or at least as solid as possible. In other words, we're reducing the contrast. Use this trick with discretion.

In the case of our Kung Fu grandmasters, after darkening the less important parts of the image in the background, the image now weighs 178 KB. Admittedly, that's not much different from the former process (only 7 KB difference), but any kilobyte we can extractfrom the image without affecting the quality is always a good thing, and 178 KB is about a 68 percent reduction in file size.

This is how the image looks after darkening the background a little:

Darkening or lightening the less important areas

Every kilobyte counts.

Optimizing an image

This is the last step in the process. This step can actually be divided in two smaller steps.

Using Adobe Fireworks (optional)

Save a JPG that balances quality versus file size well. There are no determined values that can always be applied to every single image. It all happens on the fly. When doing this step, you don't want to save the image with too much low quality since the image is going to go through another optimization step.

I'm actually going to use a software that Adobe stopped developing back in May 2013: Fireworks.

Fireworks has been known to have a superior image optimization engine than Photoshop, I've run tests myself and Fireworks compression versus. quality always showed the best results. Fireworks is as relevant to today's web design processes and workflows as any other image editing software out there today. So don't be afraid to use it.

After exporting the image from Fireworks at 80 percent quality, the image of the Kung Fu grandmasters weighs now only 71 KB. That's about an 87 percent reduction in file size from the original 556 KB.

Compressing the image

Run the image through another image optimization tool, either a standalone application such as ImageOptim for Mac or Radical Image Optimization Tool (RIOT) for Windows, or through an online service such as https://tinypng.com/ or http://www.jpegmini.com/.

We're going to use the https://tinypng.com/ online image compression service. After running the image exported from Fireworks through https://tinypng.com/, the file size is now about 52 KB, that's about a 91 percent reduction in file size from the original 556 KB. This is a massive win for image optimization.

Tip

If you didn't run the image through Fireworks first, don't worry. Even though your image may be a bit larger, it will still be incredibly optimized and that's the objective here.

Here's the before (left) and after (right) comparison between the 556 KB image and the final 52 KB image:

Compressing the image

Third-party image resizing services

We have to acknowledge that if the manual process of optimizing images can be quite tedious and time consuming in the scenario where many images need to be resized and optimized, doing it manually may not be the best idea.

There are a few third-party and server-side services out there that automate this process for us. We're going to leave the tutorials of how to implement these services for another book. However, we're going to list some of the most popular services so you can have a reference in case you want to dive deeper.

Here are a few examples of third-party image resizing services:

· Sencha.io Src from Sencha.com (http://www.sencha.com/learn/how-to-use-src-sencha-io/)

· ReSRC by Dom Fee and Ed Thurgood (http://www.resrc.it/)

· WURFL Image Tailor (http://web.wurfl.io/#wit)

Here are a few examples of server-side (.htaccess and/or .php) image resizing services:

· Adaptive Images by Matt Wilcox (http://adaptive-images.com/)

· RESS.io (http://ress.io/)

The <picture> element and the srcset and sizes attributes

Let me start by saying that there is no 100 percent optimal solution to the image issues in RWD. This is because of the current lack of support for the recommended properties, or because there's a double download of assets. Granted, Dave Newton's article inhttp://ww1.smashingmagzine.com/, How To Avoid Duplicate Downloads In Responsive Images, tries to address this issue (http://www.smashingmagazine.com/2013/05/10/how-to-avoid-duplicate-downloads-in-responsive-images/).

However, that solution is very verbose. If you have to work with many images, this solution may not be the best option and allowing a double download starts making more sense. Every project is different, so trying to make the most informed decisions possible is incredibly important.

As soon as browser vendors decide to fully support any of the solutions mentioned here, there won't be a need to worry about double downloads or polyfills of any kind.

The <picture> element and the srcset and sizes attributes are maintained by the Responsive Images Community Group (RICG) and are now part of the HTML specification. In other words, we can use them without any type of polyfill and have the confidence that modern browsers will support them. Well, to some degree at least.

The only reason we would need to use a polyfill is to support those browsers (legacy and modern) that haven't yet implemented support for them.

Tip

Both the <picture> element and the srcset attribute have a fallback feature for those browsers that don't support them. You can opt to use a polyfill, but you are not required to do so. If you think using a polyfill enhances the user experience, by all means, go for it. Read this article about it from the creator of the Picturefill polyfill, Scott Jehl (http://www.filamentgroup.com/lab/to-picturefill.html).

There are many polyfills out there, here's a short list of the ones we can use today:

· Picturefill by Scott Jehl (recommended by the RICG: http://scottjehl.github.io/picturefill/)

· PicturePolyfill by Andrea Verlicchi (http://verlok.github.io/picturePolyfill/)

· respimage by Alexander Farkas (https://github.com/aFarkas/respimage)

Some people in the web design and web development communities feel strongly about considering that a new HTML element (<picture>) isn't the solution to the issues we are experiencing with images in RWD. They feel that the solution should come from within an already existing tag, the <img> tag.

Tip

The sizes attribute can also be used with the <picture> element, but we're going to focus on using the sizes attribute with the <img> tag.

Good for us, that the solutions come in both flavors. It doesn't matter which method you use to serve your images in a responsible way, what matters is that you should be using one of these methods. If you already are, that's awesome. If not, don't sweat it. The following explanations will help clear up any questions you have about this matter.

When to use <picture> and when to use srcset

When to use <picture> and when to use srcset? This is a very legit question that I myself couldn't wrap my head around when first heard these terms. So I decided to ask Brad Frost at a workshop he conducted in Dayton, OH.

The recommended approach boils down to this concept: art direction. In responsive images, art direction basically means that you have different images that are cropped a certain way so that less important parts of the image are left out in order to focus on the important ones.

This is different from just resizing the same image. Granted, you can use whatever method you want, but to keep things simple, you can use the <picture> element when you want to serve art directed images and the srcset attribute when you just want to serve resized versions of the same image.

Before we dive into the markup, let's see a visual example of an art directed image versus a resized image using the photo of the Kung Fu grandmasters:

When to use <picture> and when to use srcset

Let's see what happened here. The original image has a lot of space around the Kung Fu grandmasters: we can see the trees and the buildings in the back. The resized versions maintain all aspects and proportions 1:1 of the original image.

However, the art directed images have a lot of differences. The first art directed image is cropped to show both grandmasters in a close up; the second art directed image has been cropped even more to accentuate the focus on Shi DeRu only (the grandmaster on the left). We could've cropped the image to focus on Shi DeYang (the grandmaster on the right), but this was the "art direction" I wanted to give the image. This is a subjective decision but based on a solid intent.

Now, let's see the Picturefill polyfill/script in action.

Implementing the Picturefill polyfill

The first thing we need to do is download the JavaScript file, which can be downloaded from https://github.com/scottjehl/picturefill/blob/2.3.0/dist/picturefill.min.js

Then, all we need to do is include it in the <head> section of our document:

<!DOCTYPE html>

<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->

<!--[if IE 9]> <html class="no-js ie9" lang="en"> <![endif]-->

<!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]-->

<head>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, initial-scale=1">

<meta http-equiv="X-UA-Compatible" content="IE=edge">

<script src="js/picturefill.min.js"></script>

<title>Picturefill polyfill</title>

</head>

Using the <picture> element

When using the <picture> element, you (the author) tell the browser which image to use at which breakpoint. The good thing about this is that we can define exactly when a certain image should be displayed by using media queries. The media queries work exactly the same as the media queries used in CSS, and they even look exactly the same.

This is what a basic <picture> snippet looks like:

<picture>

<source srcset="images/grandmasters-small.jpg" media="(max-width: 40em)">

<source srcset="images/grandmasters-medium.jpg" media="(max-width: 64em)">

<source srcset="images/grandmasters-default.jpg">

<img src="images/grandmasters-default.jpg" alt="Fallback image">

</picture>

Now, even with a polyfill, IE9 has issues with the <picture> element. As weird as it sounds, we need to inject a <video> tag within conditional comments for IE9 to work correctly.

This is what the markup looks like after amending it for IE9:

<picture>

<!--[if IE 9]><video style="display: none;"><![endif]-->

<source srcset="images/grandmasters-small-ad.jpg" media="(max-width: 40em)">

<source srcset="images/grandmasters-medium-ad.jpg" media="(max-width: 64em)">

<source srcset="images/grandmasters-default.jpg">

<!--[if IE 9]></video><![endif]-->

<img src="images/grandmasters-default.jpg" alt="Fallback image">

</picture>

As you can see, I also highlighted the <img src="grandmasters-default.jpg" alt="Fallback image"> tag. This is the fallback image for those browsers that do not support the <picture> element.

One thing to keep in mind is that not so long ago, this fallback image caused double download in some modern browsers. My last tests showed that this was not the case in Chrome and Firefox, which do support the <picture> element. So make sure you run all necessary tests to see where you stand and then think of a solution if you need to support those legacy browsers.

Here's a demo I created for this in CodePen: http://codepen.io/ricardozea/pen/cf6c0965785d552bad5e200acb761ffe

Using the srcset and sizes attributes

The srcset and sizes attributes actually come from the <picture> specification, but are implemented in the <img> element. When using the srcset and sizes attributes, the browser does all the work of deciding which image to use for each specific circumstance. You can also use media queries if you want, although, not required. The word vw means viewport width and it's used to let the browser know that it should display an image at a certain percentage in relation to the width of the viewport. If you see something like 80vw, it means that the image should be 80 percent of the width of the current viewport.

The w descriptor means the width of the image. If you see something like 255w, it means the browser will understand that specific image is 255px wide.

Let's take a look at an <img> tag with the srcset and sizes attributes:

<img src="images/grandmasters-default.jpg"

srcset="images/grandmasters-small-rsz.jpg 255w,

images/grandmasters-medium-rsz.jpg 511w"

sizes="(min-width: 30em) 80vw, 100vw"

alt="Mastering RWD with HTML5 and CSS3">

The letters rsz are an abbreviation of the word resize. That's because for images that are just going to be resized in RWD, the srcset attribute keeps things a bit simpler.

The following markup is truncated in order to focus on the specific explanations easily.

The first thing we see is the already known src attribute which acts as the fallback image:

<img src="images/grandmasters-default.jpg"…

Keep in mind that the image grandmasters-default.jpg will not be used by browsers that do understand srcset. In other words, the default image in browsers that support srcset is going to be first image in the list. In our case, it is grandmasters-small-rsz.jpg. Then, we see the srcset attribute.

This is where the magic starts happening:

srcset="images/grandmasters-small-rsz.jpg 255w,images/grandmasters-medium-rsz.jpg 511w"

In this example, our plan is to show two different image files in browsers that support srcset. This is accomplished by listing the images separated by commas. Also, the value defined after each image is the width of the image:

images/grandmasters-small-rsz.jpg 255w

Tip

We can use height as well:

grandmasters-small-rsz.jpg 170h

However, the most common use case is that dealing with the width and allowing the height to adjust proportionally gives authors a bit more control over the image.

Giving the size of the image to the browser will allow it to make a more informed decision about what image to use based on the media query in the sizes snippet:

sizes="(min-width: 30em) 80vw, 100vw"

Remember, 30em is the same as 480px. With the media query min-width: 30em, the browser goes through the following process:

· If my viewport is 30em (480px) or less, I should show the image that's 255px wide. There's no need to show the image that's 511px in a viewport that's only 480px. That's a waste of bandwidth!

· But if my viewport is more than 30em (480px), then I should show the image that's 511px wide.

The last part of the sizes attribute is the viewport widths: 80vw, 100vw.

sizes="(min-width: 30em) 80vw, 100vw"

This means that if the viewport is 30em (480px) or less, the browser will show the image at 80 percent width. If it's more than 30em (480px), it will show the image at 100 percent width.

Finally, we have the alt attribute:

alt="Mastering RWD with HTML5 and CSS3">

Adding an alt attribute is always a good accessibility practice for users with assistive technology. Also, in case the images aren't loaded, browsers can display this text instead.

Tip

The order of the attributes doesn't matter. In other words, you can have srcset first, then alt, then sizes, and then the src attribute (or vice versa).

Targeting high-density screens with srcset

High-density screens will always be something in the RWD world that we'll never get away from. So if you can't defeat them, join them.

Here's a snippet that addresses normal and high-density screens:

<img src="images/grandmasters-default.jpg"

srcset="images/grandmasters-small-rsz.jpg 1x,images/grandmasters-medium-rsz.jpg 2x">

As you can see, this is a much shorter and concise markup. It's really self-explanatory: use a fallback image in case there's no srcset support. If there is support, then use the 1x image if the device has a normal density display. You will have to use the 2x image if the device has a high-density display up to two times the density. If we are supporting even higher than 2x density devices, a 3x suffix should be added.

The sizes attribute is not required. If your design or conditions merit the use of the sizes attribute, you're free to use it.

Here's a demo I created for this in CodePen: http://codepen.io/ricardozea/pen/a13993f05a4cdc5f714a311a94f48a69

<picture> versus srcset

Some web designers and developers say that using media queries inside HTML like we saw with <picture> and srcset goes against the principle of separation of concerns: styling and markup should always remain as separated, independent assets.

Others, as I mentioned before, think that a new HTML element is unnecessary and that any solutions should be based on enhancing and extending already existing elements like the <img> tag.

All I can say is that at the end, none of that matters. What matters is that as web designers and developers, we should be using anything we have at our disposal to make users happy and create memorable experiences, while adhering to the best practices for long lasting implementations.

Replacing 1x images with 2x images on the fly with Retina.js

The Retina.js script is one of those scripts that makes things so much easier that sometimes you wonder why responsive images are so difficult.

If you don't feel ready to deal with the <picture> and/or srcset and sizes attributes, I don't blame you. It's scary but I recommend that you keep trying to understand these tools since that's the state of the art of responsive images.

The Retina.js script was developed by the folks at Imulus (http://imulus.com/). The Retina.js script isn't a JavaScript-only solution; they also have a Sass mixin that produces the same results without the dependency on JavaScript.

Let's take a look at the JavaScript solution first.

Retina.js – a JavaScript solution

Using the script couldn't be any simpler. We need to download the script from https://github.com/imulus/retinajs/blob/master/dist/retina.min.js

Then, we place the script at the bottom of the HTML, right before the closing <body> tag:

<!DOCTYPE html>

<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->

<!--[if IE 9]> <html class="no-js ie9" lang="en"> <![endif]-->

<!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]-->

<head>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, initial-scale=1">

<meta http-equiv="X-UA-Compatible" content="IE=edge">

<title>Retina.js - JavaScript Solution</title>

</head>

<body>

...

<script src="js/retina.min.js"></script>

</body>

</html>

Tip

The Retina.js script is not framework dependent. In other words, it doesn't need jQuery or Mootools or Dojo or any framework to… well, work.

Then, we add an image to our markup:

<!DOCTYPE html>

<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->

<!--[if IE 9]> <html class="no-js ie9" lang="en"> <![endif]-->

<!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]-->

<head>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, initial-scale=1"><meta http-equiv="X-UA-Compatible" content="IE=edge">

<title>Retina.js - JavaScript Solution</title>

</head>

<body>

<img src="images/grandmasters-default.jpg" alt="">

<script src="js/retina.min.js"></script>

</body>

</html>

That's it! We don't have to do anything to the markup, unless we want to exclude an image from being replaced. I explain how to do this coming up next.

The basic function of the JavaScript solution of Retina.js is that it looks for images in the page and replaces them with high-resolution versions if they exist on the server.

You need to name your high-resolution images with the @2x modifier right at the end of the name.

In other words, if you have the following image:

<img src="images/grandmasters-default.jpg" alt="">

Retina.js replaces it with the following one:

<img src="images/grandmasters-default@2x.jpg" alt="">

As long as the @2x image exists on the server, Retina.js replaces it. If the image doesn't exist, then it won't replace it.

Excluding images

If you have excluded or want to exclude, images from being replaced by Retina.js, you can add the data-no-retina attribute to your images:

<img src="images/grandmasters-default.jpg" alt="" data-no-retina>

Retina.js – a Sass mixin solution

Well, this is weird—a JavaScript solution that somehow also happens to have a CSS solution? Sweet! Note that this Sass mixin is for applying background high-resolution images.

The Sass mixin looks like this:

@mixin at2x($path, $ext: "jpg", $w: auto, $h: auto) {

$at1x_path: "#{$path}.#{$ext}";

$at2x_path: "#{$path}@2x.#{$ext}";

background-image: url("#{$at1x_path}");

@media all and (-webkit-min-device-pixel-ratio : 1.5),

all and (-o-min-device-pixel-ratio: 3/2),

all and (min--moz-device-pixel-ratio: 1.5),

all and (min-device-pixel-ratio: 1.5) {

background-image: url("#{$at2x_path}");

background-size: $w $h;

}

}

The usage is quite simple:

.hero {

width: 100%;

height: 510px;

@include at2x('../images/grandmasters-default', jpg, 100%, auto);

}

We need to declare the file extension, the width, and the height as comma-separated values. The preceding Sass snippet will compile to this:

.hero {

width: 100%;

height: 510px;

background-image: url("../images/grandmasters-default.jpg");

}

@media all and (-webkit-min-device-pixel-ratio: 1.5), all and (-o-min-device-pixel-ratio: 3 / 2), all and (min--moz-device-pixel-ratio: 1.5), all and (min-device-pixel-ratio: 1.5) {

.hero {

background-image: url("../images/grandmasters-default@2x.jpg");

background-size: 100% auto;

}

Tip

For videos with 16:9 aspect ratio, use padding-bottom: 56.25%;.

For videos with 4:3 aspect ratio, use padding-bottom: 75%;.

All we need to do now is define the width of the whole thing. We do that by adding a width to the outer container, the .video-container wrapper:

.video-container {

width: 80%; /* This can be any width you want */

}

The Vector Formats

We're going to see some HTML and CSS/SCSS snippets to get an idea of how to work with icon fonts and SVGs, but we're not going to go through the creation of such assets since that process is out of the scope of this section.

Vectors or bitmaps/raster images

When people ask what the difference between vectors and bitmaps/raster images is, the answers I often hear are usually around the idea, "If you enlarge it, it won't lose its quality. No worries for mobile devices." Although true, it doesn't fully answer the question. So here are the differences:

A vector image is a file made out of mathematical equations. The results of these equations are represented by a graphic (lines, shapes, colors). If the size of the image changes in any way, the values of those equations are recalculated and the resulting graphic is painted again.

A bitmap or raster image is a file made out of pixels. These pixels have a specific/defined width, height, and color. If an image is enlarged, the pixels are stretched and that's why the image looks blurry or pixelated.

With those definitions out of the way, let's talk about some of the vector formats used for RWD. Vector formats include:

· Web fonts

· Icon fonts

· SVGs

Let's see how to rapidly implement icon fonts and SVGs; web fonts will be addressed in the next chapter.

Icon fonts

Icon fonts are basically a font file but instead of having letters as glyphs it has, well, icons. Some people love icon fonts (I do), and some aren't really too fond of them, especially since SVG has gained so much popularity.

Let's see the pros and cons of icon fonts.

Some advantages are:

· Icon fonts are very likely smaller in file size than their SVGs counterparts. We can have many more icons in a single font file and it weighs a lot less than having an SVG sprite.

· The properties of icon fonts can be modified with any properties used to modify text, for example, color, font-family, font-weight, and so on. After all, it's a font. This means that we don't have to learn any new syntaxes or properties.

· They are relatively easy to implement. Once all the @font-face properties are set once, calling an icon font is a matter of adding a class to the HTML and calling a specific code called the Unicode Point in the CSS.

· Icon fonts are vectors so they retain their optimum quality on any screen density, screen size, and zoom level.

· They're very design-versatile. A single icon font can be wrapped in a colored container, have the icon reserved (knockout), and still be the same icon—no need for a separate file.

Some disadvantages are:

· Updating a custom-designed icon can take some work, since we'd have to work with a third-party app to generate our icon font files.

· Icon fonts can only use a single color. I honestly don't think this is a disadvantage.

· One of the main disadvantages of icon fonts is that implementing a fallback in case the font file doesn't load is a bit complex and if you ask me, verbose. The name of the pattern is "A Font Garde". If you want to read about it, check out Zach Leatherman's postBulletproof Accessible Icon Fonts (http://www.filamentgroup.com/lab/bulletproof_icon_fonts.html). The GitHub repo can be found at https://github.com/filamentgroup/a-font-garde.

Here are a few recommendations I can give you when using icon fonts:

· If possible, avoid using them for critical content.

· Always provide a title="" attribute in the element you're using the icon font on. If the font file fails to load, at least the text in the title tag can be seen.

· If you're ok with it, use an extra HTML element to hold the icon. If the icon font file fails to load, users with and without assistive technologies can still use the feature the icon font represents.

· In my years of experience, I have yet to see icon font files failed to load, but that doesn't mean it can't happen. So I recommend staying on top of your server logs to determine if the icon font file is or isn't being downloaded. If it's not, then you need to remedy the issue as soon as possible.

Let's implement an icon font then.

Implementing icon fonts

The fastest way to get icon font files is by using a third party web app like IcoMoon.io or Fontello.com. You can also get a copy of Font Awesome.

Tip

Be careful when considering using Font Awesome. Using a full font file with tenths of icons only to use a fraction of them is wasted bandwidth. If you're only going to use a handful of icon fonts, using IcoMoon.io or Fontello.com for custom icon selection is a better option.

Once you are able to unzip the provided files, the only file you're going to need is the .woff file. The reason you only need this file is because browser support for .woff files goes all the way back to IE9. Unless you want/need to support legacy browsers (desktop and mobile), you can then use .eot, .ttf, and .svg files.

Tip

I recommend that you keep it simple and avoid unnecessary headaches when trying to support icon fonts in legacy browsers. All they get is the text instead of the icon, or display the text in the title="" attribute.

Let's name our icon font file icon-font.woff. Create a /fonts folder and save the icon-font.woff file in it. This is what we are going to try to accomplish: a soft-blue link with an icon on the left, no underline, and 40px Arial/Helvetica font:

Implementing icon fonts

Using a pseudo-element

The great thing about using a pseudo-element is that our source markup always stays clean. In this case, we're going to use the :before pseudo-element, but this technique also works with an :after pseudo-element.

Let's take a look at the build.

This is the HTML snippet:

<a href="#" class="icon icon-headphones" title="Headphones">Headphones</a>

Here's the SCSS. The first thing we need is a mixin to handle any custom web fonts. In this case, it is an icon font:

//Web font mixin

@mixin fontFace($font-family, $file-path) {

@font-face {

font: {

family: $font-family;

weight: normal;

style: normal;

}

src: url('#{$file-path}.woff') format('woff');

}

}

Tip

Notice the nested properties in the font: {…} block. By doing this, we keep things DRY and avoid repeating the term font for the following instances: font-family, font-weight and font-style.

Then, we create a rule using attribute selectors to handle the basic styling properties of the icon font:

//Icon Font specific rule

[class^="icon-"], [class*=" icon-"] {

font: {

family: icon-font, Arial, "Helvetica Neue", Helvetica, sans-serif;

weight: normal;

style: normal;

variant: normal;

}

text-transform: none;

line-height: 1;

speak: none;

// Improve Font Rendering

-webkit-font-smoothing: antialiased;

-moz-osx-font-smoothing: grayscale;

}

Tip

Notice the ^ and * characters in the attribute selectors. The first one means select elements starting with the term icon- and the second select elements containing the term icon-.

Then, we need to call the fontFace mixin in order to bring the font into the compiled CSS file:

@include fontFace(icon-font, '/fonts/icon-font');

The great thing about the fontFace mixin is that all we need to do is declare the font name and then the file path. There is no need to declare the file extension; that's taken care of by the mixin.

This will compile to:

@font-face {

font-family: icon-font;

font-weight: normal;

font-style: normal;

src: url("/fonts/icon-font") format("woff");

}

Here is the rule that makes the magic happen using :before:

.icon-headphones:before {

content: "\e601";

margin-right: 10px;

}

For basic styling enhancement, we create these other two rules. However, they are not required. The code is as follows:

.icon { font-size: 40px; }

a {

padding: 5px;

text-decoration: none;

color: #2963BD;

transition: .3s;

&:hover { color: lighten(#2963BD,20); }

&:focus { outline: 2px solid orange; }

}

The final compiled CSS looks like this:

[class^="icon-"], [class*=" icon-"] {

font-family: icon-font, Arial, "Helvetica Neue", Helvetica, sans-serif;

font-weight: normal;

font-style: normal;

font-variant: normal;

text-transform: none;

line-height: 1;

speak: none;

-webkit-font-smoothing: antialiased;

-moz-osx-font-smoothing: grayscale;

}

@font-face {

font-family: icon-font;

font-weight: normal;

font-style: normal;

src: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/9988/icon-font.woff") format("woff");

}

.icon-headphones:before {

content: "\e601";

margin-right: 10px;

}

.icon {

font-size: 40px;

}

a {

padding: 5px;

text-decoration: none;

color: #2963BD;

-webkit-transition: .3s;

transition: .3s;

}

a:hover {

color: #6d9adf;

}

a:focus {

outline: 2px solid orange;

}

Here's a demo I created for this in CodePen: http://codepen.io/ricardozea/pen/e62b201350efe7f59f91c934f9fc30fa

Here's another demo I created in CodePen with the icon fonts a bit more advanced: http://codepen.io/ricardozea/pen/5a16adffb6565312506c47ca3df69358

Using an extra HTML element

To be honest, using an extra HTML element goes a little against the principle of separating content from styling, since adding an extra HTML element for styling reasons is not something some developers recommend. However, we can also argue that the icon itself really is content, not styling. Either way, here's the run down.

Here's the HTML snippet:

<a href="#" title="Headphones"><i class="icon-headphones" aria-hidden="true"></i>Headphones</a>

Tip

In order to hide irrelevant content from screen readers, we use the aria-hidden="true" directive.

The SCSS code from the previous example is practically the same, except we move the font-size: 10px; declaration from the .icon class to the a rule and then delete the .icon class altogether. You will also see some extra properties but only for styling reasons.

The final SCSS looks like this:

//Web font mixin

@mixin fontFace($font-family, $file-path) {

@font-face {

font: {

family: $font-family;

weight: normal;

style: normal;

}

src: url('#{$file-path}.woff') format('woff');

}

}

//Icon Font specific rule

[class^="icon-"], [class*=" icon-"] {

font: {

family: icon-font, Arial, "Helvetica Neue", Helvetica, sans-serif;

weight: normal;

style: normal;

variant: normal;

}

text-transform: none;

line-height: 1;

speak: none;

// Improve Font Rendering

-webkit-font-smoothing: antialiased;

-moz-osx-font-smoothing: grayscale;

}

@include iconFont(icon-font, '/fonts/icon-font');

.icon-headphones:before {

content: "\e601";

margin-right: 10px;

}

a {

font-size: 40px;

//Styling stuff

padding: 5px;

text-decoration: none;

color: #2963BD;

transition: .3s;

&:hover { color: lighten(#2963BD,20); }

&:focus { outline: 2px solid orange; }

}

The compiled CSS looks like this:

[class^="icon-"], [class*=" icon-"] {

font-family: icon-font, Arial, "Helvetica Neue", Helvetica, sans-serif;

font-weight: normal;

font-style: normal;

font-variant: normal;

text-transform: none;

line-height: 1;

speak: none;

-webkit-font-smoothing: antialiased;

-moz-osx-font-smoothing: grayscale;

}

@font-face {

font-family: icon-font;

font-weight: normal;

font-style: normal;

src: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/9988/icon-font.woff") format("woff");

}

.icon-headphones:before {

content: "\e601";

margin-right: 10px;

}

a {

font-size: 40px;

padding: 5px;

text-decoration: none;

color: #2963BD;

-webkit-transition: .3s;

transition: .3s;

}

a:hover {

color: #6d9adf;

}

a:focus {

outline: 2px solid orange;

}

Here's a demo I created for this in CodePen: http://codepen.io/ricardozea/pen/8ca49cb06aeb070f4643f0a8e064126c.

Scalable Vector Graphics

SVG graphics have gained incredible popularity very quickly. Browser support is 100 percent, even Opera Mini supports SVG images. Let's discuss some pros and cons of SVG images:

The pros of SVGs:

· They can be created and edited with a text editor.

· They are 100 percent accessible.

· They can have multiple colors.

· They are SEO-friendly since they can be indexed.

· Since they are vectors, they maintain their quality on any screen density, screen size, or zoom level.

· They can be animated, even the elements inside the <svg> tag.

· The SVG spec is an actual, open standard developed by the W3C.

· It's arguably more semantic than using a font for graphics.

· Third-party online icon tools can also export to SVG in addition to icon font.

· Browser support is 100 percent available in modern browsers.

The cons of SVGs:

· An SVG sprite file can weigh more than its icon font counterpart.

· If legacy browser support is required (IE8 and below), an image fallback is required.

· Software that can save as SVG usually adds extra unnecessary markup in the final file, so we either have to remove it manually or use a third-party optimization tool to do it for us for every file. This in turn adds another layer of complexity to development workflow.

· Although SVGs are made with XML structure, it requires a pretty advanced level of understanding to perform edits in a text editor.

An SVG file is basically an XML-formatted file. This is what the markup of the headphones graphic looks like:

<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">

<path id="left-ear-pad" d="M9 18h-2v14h2c0.55 0 1-0.45 1-1v-12c0-0.55-0.45-1-1-1z"/>

<path id="right-ear-pad" d="M23 18c-0.55 0-1 0.45-1 1v12c0 0.6 0.5 1 1 1h2v-14h-2z"/>

<path id="headband" d="M32 16c0-8.837-7.163-16-16-16s-16 7.163-16 16c0 1.9 0.3 3.8 1 5.464-0.609 1.038-0.958 2.246-0.958 3.5 0 3.5 2.6 6.4 6 6.929v-13.857c-0.997 0.143-1.927 0.495-2.742 1.012-0.168-0.835-0.258-1.699-0.258-2.584 0-7.18 5.82-13 13-13s13 5.8 13 13c0 0.885-0.088 1.749-0.257 2.584-0.816-0.517-1.745-0.87-2.743-1.013v13.858c3.392-0.485 6-3.402 6-6.929 0-1.29-0.349-2.498-0.958-3.536 0.62-1.705 0.958-3.545 0.958-5.465z"/>

</svg>

There are many ways to use SVG images: inline via the <img>, <object>, <use>, or <svg> tags; as background images with CSS; using Modernizr in conditional classes to address fallbacks; or with jQuery or plain JavaScript, using third-party services such as grumpicon.com, you name it.

To keep things simple, we're going to focus on two methods:

· Inline via the <svg> tag.

· File-based with the <img> tag.

Inline via the <svg> tag

Inlining SVGs is the go-to method of many web designers and developers. The fact that we can control individual parts of the SVG with CSS and JavaScript makes it very appealing for animations.

One of the drawbacks of inlining SVG markup is that the image is not cacheable. In other words, every time the image appears, the browser has to read the XML of the SVG. If you have too many SVGs on your page, these can potentially be detrimental to the page speed and eventually the user experience. So be careful of the objective of the page and the types of visitors using your website/app.

Here's an HTML snippet of the SVG of the headphones inlined in a link tag:

<a href="#">

<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">

<path id="left-ear-pad" d="M9 18h-2v14h2c0.55 0 1-0.45 1-1v-12c0-0.55-0.45-1-1-1z" />

<path id="right-ear-pad" d="M23 18c-0.55 0-1 0.45-1 1v12c0 0.6 0.5 1 1 1h2v-14h-2z" />

<path id="headband" d="M32 16c0-8.837-7.163-16-16-16s-16 7.163-16 16c0 1.9 0.3 3.8 1 5.464-0.609 1.038-0.958 2.246-0.958 3.5 0 3.5 2.6 6.4 6 6.929v-13.857c-0.997 0.143-1.927 0.495-2.742 1.012-0.168-0.835-0.258-1.699-0.258-2.584 0-7.18 5.82-13 13-13s13 5.8 13 13c0 0.885-0.088 1.749-0.257 2.584-0.816-0.517-1.745-0.87-2.743-1.013v13.858c3.392-0.485 6-3.402 6-6.929 0-1.29-0.349-2.498-0.958-3.536 0.62-1.705 0.958-3.545 0.958-5.465z"/>

</svg>Headphones

</a>

To control its size, distance from the text, and appearance, we add the following CSS:

svg {

width: 40px;

height: 40px;

margin-right: 10px;

fill: #2963BD;

}

a {

font-size: 40px;

text-decoration: none;

color:#2963BD;

}

Tip

SVGs files called via the <img> tag are not affected by CSS. If you want to make any style changes to it, you have to either make them in the actual SVG file or place the SVG markup inline.

However, this markup has a problem. It doesn't provide a fallback for legacy browsers, specifically IE8 and below. Let's try to fix this.

Providing fallback images to legacy browsers for inline SVGs

There are two ways to provide fallback images to legacy browsers for inline SVGs.

Using the <foreignObject> and <img> tags

Create a <foreignObject> element inside the <svg> tag and include an <img> tag that calls the fallback image:

<a href="#">

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="32" height="32" viewBox="0 0 32 32">

<path d="M9 18h-2v14h2c0.55 0 1-0.45 1-1v-12c0-0.55-0.45-1-1-1z"/>

<path d="M23 18c-0.55 0-1 0.45-1 1v12c0 0.6 0.5 1 1 1h2v-14h-2z"/>

<path d="M32 16c0-8.837-7.163-16-16-16s-16 7.163-16 16c0 1.9 0.3 3.8 1 5.464-0.609 1.038-0.958 2.246-0.958 3.5 0 3.5 2.6 6.4 6 6.929v-13.857c-0.997 0.143-1.927 0.495-2.742 1.012-0.168-0.835-0.258-1.699-0.258-2.584 0-7.18 5.82-13 13-13s13 5.8 13 13c0 0.885-0.088 1.749-0.257 2.584-0.816-0.517-1.745-0.87-2.743-1.013v13.858c3.392-0.485 6-3.402 6-6.929 0-1.29-0.349-2.498-0.958-3.536 0.62-1.705 0.958-3.545 0.958-5.465z"/>

<foreignObject>

<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/9988/headphones.png" alt="Headphones">

</foreignObject>

</svg>Headphones

</a>

Using an <image> tag

As we all know, there's isn't an <image> tag… or is there? In the SVG world, there is! This solution is very similar to the first method. The two differences are that we do not use a <foreignObject> element and we use an <image> tag. This is all inside the <svg> tag:

<a href="#">

<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">

<path id="left-ear-pad" d="M9 18h-2v14h2c0.55 0 1-0.45 1-1v-12c0-0.55-0.45-1-1-1z" />

<path id="right-ear-pad" d="M23 18c-0.55 0-1 0.45-1 1v12c0 0.6 0.5 1 1 1h2v-14h-2z" />

<path id="headband" d="M32 16c0-8.837-7.163-16-16-16s-16 7.163-16 16c0 1.9 0.3 3.8 1 5.464-0.609 1.038-0.958 2.246-0.958 3.5 0 3.5 2.6 6.4 6 6.929v-13.857c-0.997 0.143-1.927 0.495-2.742 1.012-0.168-0.835-0.258-1.699-0.258-2.584 0-7.18 5.82-13 13-13s13 5.8 13 13c0 0.885-0.088 1.749-0.257 2.584-0.816-0.517-1.745-0.87-2.743-1.013v13.858c3.392-0.485 6-3.402 6-6.929 0-1.29-0.349-2.498-0.958-3.536 0.62-1.705 0.958-3.545 0.958-5.465z"/>

<image src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/9988/headphones.png" xlink:href="" alt="Headphones">

</svg>Headphones

</a>

Now, the reason this works is because we are combining a feature of SVGs and HTML into one element.

The SVG feature is that the <image> tag is a valid element within the SVG world. Now, as weird it sounds, all browsers see the <image> tag as an out-of-standards tag that resembles the <img> tag from HTML.

The HTML feature is that normally we use the src attribute to point to the asset's location. In the SVG world, assets are called with the xlink:href attribute. If we add a src attribute pointing to the asset and leave the xlink:href attribute empty, then legacy browsers will see the fallback image while modern ones won't because the xlink:href attribute is empty.

I recommend sticking with the second method; it's just more succinct and less hassle. Just remember that instead of <img>, we use <image>. Also, for the purpose of the book, I left the xlink:href attribute in the markup but this is optional. If it's empty, you can remove it altogether if you want.

Tip

Throughout the book, I've taken out the trailing slash /> on self-closing tags such as <hr> or <img> elements, for example. In HTML5, it is ok to go with or without it. However, the trailing slash is required in the path elements in SVGs, that's why you're seeing them here in these examples.

None of these methods I just mentioned cause double download on browsers that support SVG. That's a win-win situation if you ask me.

File-based with the xlink:href and src attributes

SVG is a type of image file, so calling it within an <img> is perfectly valid:

<img src="images/headphones.svg" alt="Headphones">

We know that SVG has flawless support in modern browsers, but the prior image isn't displayed in legacy browsers (IE8 and below).

Remember the previous explanation about the xlink:href and src attributes in SVG and HTML? Well, we're going to do pretty much exactly the same we did there. However, instead of inlining the SVG markup, we're just going to link to an SVG file while providing a fallback image for old browsers.

This clever trick was created by Alexey Ten. Here's the markup:

<a href="#">

<svg width="39" height="39">

<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/9988/headphones.svg" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/9988/headphones.png" width="39" height="39">

</svg>Headphones

</a>

There are issues here as well. Alexey's technique is not the offender, it is the browsers—specifically IE9, 10 and 11 as well as iOS 3 and 4. They download both the SVG and the fallback image.

If this double download is acceptable for you and you understand the consequences, go for it. Nonetheless, keep a mental note of where you can improve things like this for your next project.

Here's a demo I created for this in CodePen:

http://codepen.io/ricardozea/pen/594e718f36976f8e77d4f9cf1640e29a

Other sources to learn about SVG

We can't talk about SVGs without referencing three of the most noticeable names in the web design and development industry today: Amelia Bellamy-Royds, Sara Soueidan, and Chris Coyer. Amelia and Chris created one of the most complete guides about how to use SVG with fallbacks that I've read, A Complete Guide to SVG Fallbacks (https://css-tricks.com/a-complete-guide-to-svg-fallbacks/).

Sara Soueidan's blog is a must-read if you want to learn everything about SVG: http://sarasoueidan.com/articles/.

Summary

Here we are, looking at the horizon and thinking something along the lines of srcset or <picture>? Resize or art direction? Retina.js or Sass mixin? FitVids or FluidVids? Icon fonts or SVG? Inline SVG of file-based SVG? What's the best way to offer our visitors the best experience?

Yes, I know the feeling. And you know what? That's a good problem to have. Otherwise, we wouldn't be learning how to master RWD.

Since most of the time we're just resizing images, srcset is the way to go. Wrapping our videos in a container and a few lines of CSS make those videos responsive in no time. Boom! Too many videos to make responsive? No problem, FitVids.js makes it happen with a single jQuery function. Icon fonts weigh less than their big brother SVGs, but keep an eye on those server logs in case the icon font files aren't downloading. Using SVGs is always going to be a win, even if there are double downloads, but keep leveling up by using different techniques and sharing your findings and experiences with others.

Let's change gears and talk about a fascinating subject that can make or break your responsive design: typography.

Let's ride!