Responsive Images Demo—Srcset, Picture, and Image-Set

It’s one thing to understand how to do something and another to actually do it. For the last few weeks I’ve been talking about responsive images, specifically the srcset attribute, the picture element, and the image-set() value on the background-image property. Along the way I promised a demo, which I’m presenting today.

title

Screnshots of the demo (single column left and two column right)

You can view the demo here and might want to keep it open while reading this post. You can also download a zip with all the files I used.

Let me remind you again my experience with all these things is pretty much limited to the demo I’m presenting here. It’s probably not the most realistic demo either. I set it up so I could test srcset, picture, and image-set() and their associated attributes and elements.

I also don’t have a high resolution screen on my laptop, which made it a little more difficult to test @2x images. I mainly tested in Chrome, since it’s the browser with the best support. To test for high resolution images I set the zoom to 200% and the font-size to 50%, which effectively doubled the resolution of the viewport. While Chrome was the main browser for testing, I did check other browsers.

I included picturefill.js so outside of a background image that uses image-set() everything should work across recent versions of all modern browsers.

An Overview of the Demo

The demo is a simple, but flexible 2-column layout on wider screens, with the right column dropping below the left one on smaller screens. You can see what the demo looks like in the image at the top of the post. The single column view is to the left and the two column view to the right.

The basic HTML structure is relatively simple.

1
2
3
4
5
6
7
<div class="container">
  <header></header>
  <div class="hero-shot"></div>
  <article></article>
  <aside></aside>
  <footer></footer>
</div>

Hopefully the HTML above doesn’t require much explanation. It’s a bunch of elements wrapped inside a container.

You can check the file for the CSS details, but the general idea with the CSS for the layout is that the container gets a max-width of 75em and all the elements inside are set to 100% width. At 48em (768px) the article’s width is changed to 67% and the width of the aside is changed to 33% so it can sit to the right of the article.

There’s some padding and margin and background-color in the CSS too, but again if you want the details you can check the file. I’d rather focus on the images here. Speaking of the images I made sure they were all flexible.

1
2
3
4
img {
  max-width: 100%;
  height: auto;
}

The images are located in the hero-shot div, the article, and the aside. The header and footer are just gray bars for presentation. To create the images I started with images 1600px wide and then created new images at 800, 400, and 200 pixels wide. I didn’t use every version of every image in the demo, but I didn’t know that when I created the images.

Since my screen is @1x resolution, I wanted to do something to make sure I knew which image I was seeing. I added text to each to show it’s width in pixels.

The Hero Shot Background Image

The hero-shot image is set up using image-set(). In fact, the HTML is just

with nothing inside. Here’s the CSS.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.hero-shot {
  width: 100%;
  height: 10em;
  background: #fcc;
  background-image: url("images/1600px/abstract-1600.jpg");
  background-image: -webkit-image-set(
    url("images/800px/abstract-800.jpg") 1x,
    url("images/1600px/abstract-1600.jpg") 2x
  );
  background-image: image-set(
    url("images/800px/abstract-800.jpg") 1x,
    url("images/1600px/abstract-1600.jp") 2x
  );
}

I set the width and height of the div (since nothing is inside) to force it to remain open. The magic is in the background-image. I set a background color since it’s good practice and a default background-image for non-supporting browsers.

I choose the 1600px image as the default since the page can open to 1200px wide. The 800px wide image is the @1x image. I wouldn’t use it in a real project because it’s not as wide as it’s container can become, but for the demo it works.

Depending on your browser you’ll see either the 800px image or the 1600px image. The smaller image will repeat and fortunately I chose an image that was created to repeat, since it connects seamlessly (other than the repeated text).

Browsers that don’t support image-set() should see the 1600px image. Those that do will show either 800px or 1600px depending on your screen’s resolution.

The Article Image

The card and money image inside the article uses srcset and the sizes attribute. Here’s the HTML.

1
2
3
4
5
<img sizes="(min-width: 48em) 67vw, 100vw"
     srcset="images/400px/cards-gambling-400.jpg 400w,
             images/800px/cards-gambling-800.jpg 800w,
             images/1600px/cards-gambling-1600.jpg 1600w"
     src="images/400px/cards-gambling-400.jpg" alt="My image&quot"

There is no CSS as everything the browser needs to choose an image is contained within the HTML above. The fallback image for non-supporting browsers is the 400px wide image. Since I included picturefill.js no one should see this image. If you want to see it, remove the script tag and check in Firefox, IE or Opera.

There is one major breakpoint in the layout so I needed two different sizes. Below 48em (768px) the layout is a single column and the image should fill 100vw (minus some padding on the article). When the browser is open wider than 48em the aside moves up to the right and the article takes up 67% of the page, which is where the 67vw value comes from.

From there your browser gets to decide which of the images in the srcset attribute is the best to show. As I increase the width of my browser on my @1x screen, I see the 400px image up to 400px browser width and then it switches to the 800px image. There’s another switch to the 1600px image when the browser is around 1200px wide. You might see different depending on your screen’s resolution.

The Images in the Aside

The four images in the aside were set using the picture element in order to give the art direction use case a try. Maybe each image represents a link that would take you to a different section. I’m imagining a scenario where on small screens the images no longer have enough detail and so a different image for each section needs to be used instead.

To keep things simpler I used the same image for each of the four. There’s one image for wider screens and one for narrower screens. Again each of the two images comes in more than one resolution version.

Here’s the code for one of the images. The other three are identical are right below each other in the HTML.

1
2
3
4
5
<picture>
  <source media="(min-width: 48em)" srcset="images/400px/vegetables-400.jpg 1x, images/800px/vegetables-800.jpg 2x">
  <source media="(min-width: 30em)" srcset="images/200px/utensils-200.jpg 1x, images/400px/utensils-400.jpg 2x">
  <img src="images/200px/utensils-200.jpg" class="sidebar" alt="responsive images test">
</picture>

The smallest screens (and those without support for the picture element) will see the 200px utensils.jpg. At 30em the browser will choose either the 200px or 400px version of untensil.jpg depending on your screen’s resolution. At 48em (when the layout changes) we switch to the other image and browsers get to choose whether to use vegetables-400px.jpg or vegetables-800px.jpg.

Are you expecting some complicated CSS to make the switch? There’s really not much to show and it’s all for layout.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
picture {
 float: left;
 width: 23%;
 margin: 1em 1%;
}

@media screen and (min-width: 48em){
  picture {
    display: block;
    width: 100%;
    margin: 2.5em auto 1.25em auto;
    padding: 0 10%;
  } 
}

I decided to style the picture element just to see if I could. Alternatively this CSS can be applied to the .sidebar class I added to each of the images. The CSS in the media query includes adjustments for when the images change from being side to side to being one on top of another.

Things I Didn’t Do

Because I was feeling my way through the demo there were a few things I could have done, that unfortunately I didn’t do.

Image compression—I didn’t think about how I was setting image compression when saving images. It didn’t occur to me until I was nearly done with the demo.

I set some as high quality and some as low quality. Forgive me if any of the images aren’t up to quality standards. Go by the width of each image as shown in the text to see which image is appearing under what conditions.

Performance—I also didn’t set anything up to test the before and after performance. I would have had to pay more attention to how I was compressing the images to do so.

Fortunately just as I’m getting ready to schedule this post for publishing, Eric Portis did test performance and he shared his findings in an article on A List Apart titled, Responsive Images in Practice.

Don’t forget you’ll need to test on different resolution screens or use a similar trick as I did in Chrome to see the demo working with different resolution images.

Closing Thoughts

As I mentioned last week, I found it easy to work with srcset, sizes, picture, source, and image-set(). It’ll take a little practice to feel comfortable, but it really is easy once you have that practice under you belt.

The sizes attribute can take a little longer to get comfortable with as you have to consider the size you want an image to display and also it’s relative size compared to the viewport, but again it comes with a little practice.

You can view the demo here and download a zip with the files and images here. Play around with my code or set up a demo for yourself. Start with srcset, without the sizes attribute to get things working and then add in the sizes attribute. Once both are working try setting up images using the picture element. Finally give image-set() a try. By the time you do it’ll probably feel old hat.

I do think it’s fine to work with srcset and picture now, especially if you use the picturefill.js polyfill. Most browsers (as well as Picturefill) don’t support image-set(), but you can still use it as long as you leave a fallback image for non-supporting browsers.

Next week I’ll move on to another topic. I hope you enjoyed the demo today as well the entire series about responsive images.

Download a free sample from my book, Design Fundamentals.

Leave a Reply

Your email address will not be published. Required fields are marked *