Responsive Images

In the modern world we have many different devices with which we access the internet and as such the traditional methods for displaying images are no longer adapted for our use case. When we set an image into a web page using the img tag, we also define a single source, the image is resized to suit its environment but the file source remains the same. As a result of this we often find ourselves loading images that are far larger than the image that we actually use. Responsive images can help us to make designs significantly more efficient, by providing a selection of image sources and supplying a source image that is much closer to the size that we actually need.

Attributes, Descriptors, and Elements

  • scrset
  • w
  • x
  • sizes
  • <picture>

srcset

Comma-separated list of one or more URL's pointing at images to be used by the user agent.


<img src="medium.png"
	srcset="small.png, medium.png, large.png">
				

Within the srcset list we can add either a width descriptor or a pixel density descriptor, to each of the listed URL's.

w Width Descriptor

The width descriptor indicates the actual width of each image file listed within the srcset.


<img src="medium.png"
	srcset="small.png 400w, medium.png 800w,
	        large.png 1200w">
				

x Pixel Density Descriptor

The pixel density descriptor is used to tell the browser what display density an image is appropriate, if the picture is destined for a device with a retina display, we would write 2x, for some higher resolution devices we would write 3x, the default value for the density descriptor is 1x.


<img src="medium.png"
	srcset="small.png, medium.png 2x, large.png 3x">
				

We can use one or the other, but not both at the same time.

sizes

Tells the browser the target width of the image in relation to the total width of the viewport. This is achieved by creating a list of media conditions followed by the intended size of the image. If an image is supposed to take up a total of 50% of the total width of the viewport, sizes would be set to 50vw for viewport width.


<img src="medium.png"
	srcset="small.png 400w, medium.png 800w"
	sizes="(min-width: 800px) 50vw, 100vw">
				

<picture>

Specifies media conditions for the use of specific image files; Typically used for art direction. Used to give the specific media condition under which the specified files should be loaded.


<picture>
	<source srcset= "large.png" media="(min-width: 800px)">
	<source srcset= "medium.png" media="(min-width: 600px)">
	<img srcset="small.jpg" alt="Cherry blossoms">
</picture>
				

Responsive Images: When and How

There two main methods of displaying images responsively, either using the traditional img tag in conjunction with the srcset and sizes attributes, or, the picture tag with different sources for different media queries.

Whenever displaying a visibly large image or high-resolution image where the high-resolution is to be maintained as much as possible, we should use the img element tag with srcset and sizes.

On the other hand, if what we are doing more closely resembles artistic direction, perhaps at specific sizes we want to swap out the image entirely, or for progressive enhancement, then we should use the picture element tag with media queries.

Preparing Images For Responsive Display

We need to consider at what sizes our page separates out into different displays and from this discern which images sizes will be required, whilst also taking account of the fact that different renditions of the same layout may be displayed on screens of differing resolutions. As such a 1200px image displayed as the largest required size on a desktop layout of our page, will be 2400px on a retina the display, and an enormous 3600px on a 3x display.

Image Size Breakdown (Width)

device 1x 2x 3x
Mobile 480px 960px 1440px
Tablet 800px 1200px1800px
Desktop1200px2400px3600px

First identify which numbers are close to one another, we see that 800px and 960px are very close, 1200px and 1440px are also close. We then consider which sizes will be most frequently be used until we settle upon the sizes that are likely to be the best suited to our use case.

It will be rare to get the case of the smallest image required, and as such we are better of making the smallest size larger than the target, here 600px so as to serve more devices efficiently at the smaller end of the scale. We also insure that likelihood that same image can serve for either a horizontal or a vertical display, so that we are not loading new images just because a device has been rotated.

Thus once we have made our list of target sizes, we must then think about which images will be required in which scenarios and adjust our selection in consequence. Which is to say that we need to think about our choices here very carefully so as to best fit the requirements of our particular use case.

Introducing srcset and w

In there most basic form responsive images consist of a regular img tag containing a traditional src attribute an alt attribute and now the srcset attribute containing a comma separated list of image URL's and their respective width descriptors.


<img src="cherry.jpg"
	alt="Cherry blossoms"
	srcset="images/small.jpg 600w,
	        images/medium.jpg 800w,
	        images/large.jpg 1200w,
	        images/extralarge.jpg 1600w">
				

The width descriptor is the actual width of the listed image, how this works is that the browser, if it has no support for the srcset attribute, it will just load the initial image give in the image tag. If it has support for srcset it will take not if of the viewport width of the browser, then look at the width descriptors for each of the URL's in the srcset list, then pick the best suited image for the environment, based on the width, replacing the initial src image that has been specified.

We should take note that it is the width of the actual viewport that is deciding which image is to be shown and not the size that has been requested in the layout. We also need to take note of the difference between a css px and a device px. On a 2x display the css pixel width of 800px will actually mean a device pixel width of 1600px.

To re-iterate the browser, when using the w width attribute, will select the image based upon the pixel width of the entire viewport, as such images that do not take up the entire viewport will be served with data that exceeds the required size. If the viewport width keeps growing but css styling holds the image at one size then the calculation using w is not good, we could get around this by limiting the size of the image that we serve but then this would not look good on screens that are using higher resolutions, this is where we need the sizes attribute.

Introducing Sizes

In the markup the sizes attribute is a comma separated list of media conditions followed by the css pixel size, you can add as many media conditions as you wish, the only condition is that the last be only a width without any media condition, the last lone width specifies what is to happen when no condition is met.

The vw unit is often used for the final condition, in the following example, if no conditions are met, then the image will be the same size as the viewport width.


<img src="cherry.jpg"
	alt="Cherry blossoms"
	srcset="images/small.jpg 600w,
	        images/medium.jpg 800w,
	        images/large.jpg 1200w,
	        images/extralarge.jpg 1600w"
	sizes="(min-width: 44em) 800px, 100vw">
				

If no conditions are met, the image will be the same size as the viewport width, but if the screen size is 44em or wider, the image will always be 800 pixels wide.

If you decide not to use a sizes tag, then the browser will assume that all images are to match 100vw, the same as the viewport width.

Using Sizes

In order to use sizes effectively, we have to look at the css of our page, any dimensions that are fixed at a particular value in the layout should be mirrored in the sizes list.

Advanced Layout With srcset and sizes

For more advanced layouts, say when using flexbox and or grid to layout media cards of different numbers at different viewport widths, then more advanced css can be used with the sizes attribute, to insure that images are used in their most suited places.


<img src="cherry.jpg"
	alt="Cherry blossoms"
	srcset="images/small.jpg 600w,
	        images/medium.jpg 800w,
	        images/large.jpg 1200w,
	        images/extralarge.jpg 1600w"
	sizes="(min-width: 40em) 250px,
	       (min-width: 30em) calc(50vw - 1em),
	       100vw">
				

An issue with this technique, and it is a point of contention within the web community for the way that the responsive images have been designed, is that it breaks the rule of separation of concerns, we are mixing layout design information in with the markup when we would normally seek to keep this aspect cleanly separated in the css files that style the page. The reason that this is far from idea is that if we alter the size of our layout in any way, in the css, we must then go to every place in the markup that uses this style and alter the images corresponding media attribute, which can in some layouts represent a vast quantity of work. The best way to use this device is though automated markup creation, via templates or similar.

Using The Pixel Density Descriptor

There are two different descriptors that we can use to tell the browser how big our image is, the w width descriptor, that we have been using so far, and x the pixel density descriptor.

So far we have not looked at this descriptor, and it is not something that we will use all that often. The pixel density descriptor enables us to specify to the browser under which resolution conditions it should be loading which image. The width descriptor makes much more sense when your images are being dynamically adjusted via css, as we can then clearly see what is going on. The pixel density descriptor is normally only ever used for fixed width elements, this provides the ability to specify which images are to be used for different resolution displays in the case of fixed width elements.

The pixel density descriptor is added to the markup in the same way as the width descriptor, instead of declaring 600w 1200w etc, we would specify 1x, then 2x etc.

For most situation, as already mentioned, the width descriptor with dynamic css is the best option, however for some use cases the density is the better option, one such use case is that of the flex container that we have already described. When using this type of dynamic display, in which more media cards are displayed rather than resizing the images, then using the display density descriptor makes much more sense.

We are currently listing four different images sizes for our images, but when using the pixel density descriptor we can reduce this code, by using the descriptor instead and removing any sizes that fall between the density multiples.


<img src="cherry.jpg"
	alt="Cherry blossoms"
	srcset="images/small.jpg 1x,
	        images/large.jpg 2x"
	sizes="(min-width: 40em) 250px,
	       (min-width: 30em) calc(50vw - 1em),
	       100vw">
				

We no longer require the 800w, or the 1600w sizes, only multiples of the image size will ever be requested. Our sizes attribute will still be required in some odd edge cases, such as 1.5x screen resolutions but for the most part we will be serving either one of the other.

Introducing <picture>

Responsive images also adds the picture element used for more artistic design choice reasons, the picture element wraps around an img element, it can have the same srcset and alt attributes and we also have a new source element that lists out other source sets for the same picture. The trick of this source tag is that it allows us to use media query tags to specify different image resources.


<picture>
	<source srcset="wide.jpg, wide-2x.jpg 2x"
		media="(min-width: 800px)">
	<source srcset="tall.jpg, tall-2x.jpg 2x"
		media="(min-width: 600px)">
	<img src="images/elemonkey-square.jpg"
		srcset="elemonkey-square-2x.jpg 2x"
		alt="Elephant and Monkey">
</picture>
				

Using the picture tag gives us more fine grained control than using the img tag alone.

This is the crux of the difference between the picture and the img tags, with the img tag we are allowing the browser to pick which image to use at specific points, where as within picture element tags we are explicitly telling the browser which to use when.

So when would you want to tell the browser which images to use? ... There are two main scenarios the first is artistic direction and the second is progressive enhancement.

Art Direction

The use of picture tags for art direction is the case in which the specific environment for the image, alters the actual choice of image used, when at some specific screen size image a is the best fit, where as when at another size, image b is the better fit. We can use the dimensions of the page or media query, to explicitly define which image resource to use.

Using the picture tags in this way effectively alters the way that we go about designing our layouts and as such requires a full top down design approach, techniques such as preparing in advance a mock-up of the layout and preparing the images in advance, and naming those images in such a way that the name includes both the size and the specific layout that it is targeting, all help to bring the page design together when using the picture element tag.

Using the picture element


<figure class="featured-image">
	<img src="images/elecmonkey-square.jpg"
		alt="Elephant and Monkey"
		srcset="images/elemonkey-square.jpg,
		images/elemonkey-square-2x.jpg">
</figure>
				

The picture element is really just a container that allows us to wrap an img and to treat it as if it were css. This means that in order to add our picture element we also need to understand what is going on within the css of our page.

Looking at the css for our media queries.


@media screen and (min-width: 375px) {
	.featured-image {
		float: right;
		width: 50%;
		margin: 0 0 1em 1em;
	}
}

@media screen and (min-width: 600px) {
	.featured-image {
		width: 33.3%;
	}
}

@media screen and (min-width: 800px) {
	.featured-image {
		float: none;
		width: 100%;
		margin-left: 0;
	}
}
				

The two break points that we need to address within our picture element are 600 and 800 pixels, armed with this information we can build out our picture element.


<figure class="featured-image">
	<picture>
		<source srcset="images/elemonkey-wide.jpg,
			images/elemonkey-wide-2x.jpg 2x" 
			media="(min-width: 800px)">
		<source srcset="images/elemonkey-tall.jpg,
			images/elemonkey-tall-2x.jpg 2x"
			media="(min-width: 600px)">
		<img src="images/elecmonkey-square.jpg"
			alt="Elephant and Monkey"
			srcset="images/elemonkey-square.jpg,
			images/elemonkey-square-2x.jpg">
	</picture>
</figure>
				

Of note, the picture element tag will not work in internet explorer 9, if you want to target this browser there are workarounds but you will have to investigate and explicitly implement them.

Picture Element Caveats

For all of these reasons using the picture element can be tricky, consider that the alt attribute will remain the same for all images displayed, this can be a guide for us as to how to use them effectively. The target of the alt caption should be the target for every image, suppose that a detail is plainly visible in a large image on a large screen, but that it is lost when the image is scaled down for smaller devices, it can bring more sense to the alt caption if we display a different crop of the same image that focuses or zooms in on that specific detail. This way the change in the source of the image is accentuating the message from the alt caption and is not deviating from it. If we work with this thought in mind, we can not stray far from an effective use of these tags as and artistic device in our layouts.

Progressive Enhancement

The picture tag can also be used to selectively provide access to different images types, depending upon the browsers ability to display them.

As an example of this, we can use the picture tag to provide a fall back to png for svg images.


<figure class="progressive-image">
	<picture>
		<source type="image/svg+xml" srcset="image/hero.svg">
		<img src="images/hero.png" alt="Superhero">
	</picture>
</figure>
				

In our example, a browser that is able to display the svg type will open the first source that it comes to, but in the case of a browser that does not recognise the svg type then it will fall back to the initial png image that has been defined as the img source and display the png. As such we can use the picture element for the progressive loading of image types.