Please Support - Ride for the child

Responsive images have always been quite problematic since the birth of responsive design. A while back I made a little script that changes the image paths based on the screen resolution. I’m excited to announce that browsers can now do this themselves!

There are two new kids on the block which I’ve heard a lot about, but haven’t really taken much notice of, mainly because of the lack of browser support and the forever changing specs.

These two kids are the “picture” element and the “srcset & sizes” attributes. The browser support is still relatively poor but this week I came across picturefill 2, an awesome picture/srcset/sizes polyfill which means it’s now possible to use these two new shiny toys today.

Picture

The picture element is a markup pattern that allows developers to declare multiple sources for an image. By using media queries, it gives developers control as to when and if those images are presented to the user.

Srcset & sizes

The srcset and sizes attributes extend the img and source elements to provide a list of available image sources and their sizes. Browsers can then use this information to pick the best image source.

*descriptions taken from http://responsiveimages.org/

When developing for Retina I make use of media queries and change the background size of each image accordingly. It’s great, but the downsize is that I have to write multiple queries that cover every possible case.
I might have small(phone), medium(tablet) and large(desktop) queries and these all have a banner image at different sizes. I need something like this (SASS nested queries with mixin).

.top-banner {
  background-image: url('banner-small.jpg');
  @include breakpoint(retina) {
   background-image: url('banner-small@2x.jpg');
  }
  @include breakpoint(medium) {
   background-image: url('banner-medium.jpg');
   @include breakpoint(retina) {
    background-image: url('banner-medium@2x.jpg');
   }
  }
  @include breakpoint(large) {
   background-image: url('banner-large.jpg');
    @include breakpoint(retina) {
     background-image: url('banner-large@2x.jpg');
    }
   }
 }

You can see that I’ve had to write 6 queries in order to cater for that banner. It would be much cooler if we could let the browser work out which images to use and when.

Srcset and Sizes

Lets look at an example and see how it works step by step.

<img src=“img-700.jpg"
     srcset=“img-400.jpg 400w,
     img-700.jpg 700w,
     img-1400.jpg 1400w,
     img-2800.jpg 2800w"
     alt="an image">

The img tag is just a default element, it’s the attributes that we’re interested in.

The src is the same as we’re used to. Just stick the default/base image in there. Generally people are sticking a default size img and setting the img{max-width: 100%}. Meaning it’s resizing itself accordingly and the page changes. If you’ve got a 700px container on desktop then your default src should point at an image that’s 700px wide. This doesn’t cover any retina screens and it also means that you’re serving a 700px image to a 300px mobile screen too, but this is a default fallback to ensure that all browsers are accounted for.

Srcset is where the magic happens. In here we declare the different image sizes that we want the browser to pick from. So in our example we’ve declared an array of 4 images.

  • 400px is for smaller non retina screens
  • 700px is for tablet & non retina desktop screens
  • 1400px is for desktop retina screens
  • 2800px is to make this massively future proof incase a ultra retina screen is announced (not needed just trying to demonstrate)

The browser does the hard work and selects the best option. Lets have a look at some examples:-

  • A 320px non-retina iPhone 3 = 400px (little bit bigger but the nearest one)
  • A 320px retina (2x) iPhone 5 = 700px (Needs exactly 640px but 700 is nearest)
  • A macbook pro retina (2x) = 1400px

Pretty simple eh? In theory it looks to be, but there’s an error in my examples and it’s related to the “sizes” attribute. Lets see what’s wrong.

I’ve not declared any “sizes” attributes yet. This means the by default the browser assumes sizes=”100vw”. Which means that it thinks that the images are going to be the entire width of the VIEWPORT. Notice I said viewport there and not the container!
So if I’m viewing my 700px container on a 3000px retina screen (2x) then my browser is going to say

“I need a 6000px image, the closest thing to that is the 2800px size so I will use that.”

Of course this is not what we want! Our container never needs an image bigger than a 1400px (if designing for max 2x retina). So what do we do here?

Well first of all I could have just left the 2800px image off. I don’t really need it. However I said that I wanted to include it in order to make things future proof. Fortunatly I can use sizes to do that.

Before I continue I think it’s important to let you know that using sizes isn’t always a good thing as there should be separation between content and presentation. You can read more about that here.

Using sizes I can declare some rules that will make the browser understand that our container is never larger than 700px, so that it’s aware to use a prefined rule as opposed to using the viewport width.

Here’s the syntax required.

sizes="[media query] [length], [default length]"

We’ve got a 700px container so my code is:

<img src=“img-700.jpg"
     srcset=“img-400.jpg 400w,
     img-700.jpg 700w,
     img-1400.jpg 1000w,
     img-2800.jpg 2800w"
     sizes="(min-width: 700px) 700px, 100vw"
     alt="an image">

This means the when our website is opened on a mobile which is 300px it tells the browser and the browser will pick the nearest one. Which is 400px in our example (You can also use ems in these size attributes).

The browser goes over each sizes attribute query until it finds one that matches and then uses the matching query’s paired length. If no media queries match, then the browser uses the “default” length, i.e. any length it comes across that doesn’t have a paired query.

If needed I can also add more breakpoints (largest values first).

sizes="(min-width: 700px) 700px, (min-width: 320px) 320px, 100vw"

Obviously you can see that the more we delve into it the more complicated things can get and the uglier the markup becomes. It’s important that I point out my above methods are using a viewport-based selection approach. I could also do a device-pixel-ratio-based approach….

<img src=“img-300.jpg"
     srcset=“img-450.jpg 1.5x,
     img-600.jpg 2x,
     img-750.jpg 2.5x"
     alt=“an image”>

NOTE: The x descriptor is not appropriate when the rendered size of the image depends on the viewport width (viewport-based selection), but can be used together with art direction (something I cover in the picture description).(source)

I’ve made some code examples which can be viewed here and check the sourecode out here. And if you’re feeling super confident and want to read more then check out these detailed examples here.

Please note you should only use use only w/sizes as the browser takes resolution into account when selecting a candidate. You never need to use w and 2x together.

Picture

The picture element differs from srcset because it allows “art-direction”. If you’re just changing resolutions, use srcset. However if you want something to change shape or size throughout the breakpoints then the picture element might be of assistance.

Lets look at an example.

<picture>
 <source media="(min-width: 700px)"
         srcset="700.jpg 1x, 1400.jpg 2x">
 <source media="(min-width: 300px)"
         srcset="150.jpg 1x, 300.jpg 2x">
 <img src="300.jpg" alt="">
</picture>

So what’s happening here? (The largest queries should come first and if they’re false it jumps onto the next one).

  • If our page is min of 700px then we show the 700px image, if the browser detects it’s a retina screen then it can choose to use the 1400px image
  • Remember the highest stuff come first. So if our screen is less than 700px it jumps to the next one.
  • If our screen is a min width of 300px then the browser will use a 150px image or 300px image based on the screen resolution.
  • If it’s smaller than 300 then it uses the default image which is 300.jpg

Notice here how I’m instructing the browser what images to use. I’m telling it what to do hence the name “art direction”. For example when my screen is 300px wide I’m telling the browser to use an image half it’s size as I want white space to the right. When I get up to 700px I want the picture back to full width. Obviously this is a very simple example but there are plenty of possibilities.

What do I use then?

Generally speaking most people will use srcset and sizes. The art direction approach should only be used when you want to manipulate the layout in some way.

Good or bad?

It’s certainly a step in the right direction. There is no excuse not to use it going forwards as the fallback caters for older browsers and of course the polyfill has your back. My only worry is that the spec has changed so manys times I worry that it might be dropped or changed again?

One amazing fact I learnt from Martin is that the browser is also be capable of loading a lower resolution source when it detects that bandwidth is slow!

One of the most confusing aspects is the fact that the widths are based on the viewport and not their containing elements. It would be cool if the browser was smart enough to know how big an image should be inside it’s container.

I’m very new to it all and yet to implement them into a larger scale project, but It’s a technique I will be looking to adopt in the near future. Researching and writing this article was a massive help and I can certainly see the positives of using these “new kids” going forwards.

I’ve made some code examples which can be viewed here and check the soure code out here.

Been a bit of a mind f*&$ learning this and wouldn’t be surprised if I’ve made some mistakes both technical and grammatical. If so feel free to hit me up on Twitter @webknit

Thanks for reading!

ALSO! These are some of the the excellent sites I referred to.

Responsive images
Martin Wolf
HTML spec
CSS tricks
Eric Portis
HTML5 doctor
Smashing magazine