The art of responsivenes
This is a guide for dealing with responsive images on the web.
There are to approaches, if your goal is to
Increase performance then
<img srcset="" src="" alt="">
Here the browser chooses between a 300×300 image or a 600×600. If the browser only needs the 300×300, that’s potentially a 4× bytes-over-the-wire savings!. This is accomplished with using srcset
The easiest-possible responsive images syntax is adding a srcset attribute with x descriptors on the images to label them for use on displays with different pixel-densities.
<img
alt="A baby smiling with a yellow headband."
src="baby-lowres.jpg"
srcset="baby-highres.jpg 2x"
>
You can do as many pixel-density variants as you like.
<img
alt="A baby smiling with a yellow headband."
src="baby-lowres.jpg"
srcset="
baby-high-1.jpg 1.5x,
baby-high-2.jpg 2x,
baby-high-3.jpg 3x,
baby-high-4.jpg 4x,
baby-high-5.jpg 100x
"
>
Using srcset / w + sizes
This accounts for around 85% of responsive images usage on the web.
<img
alt="A baby smiling with a yellow headband."
srcset="
baby-s.jpg 300w,
baby-m.jpg 600w,
baby-l.jpg 1200w,
baby-xl.jpg 2000w
"
sizes="70vmin"
>
We’re still providing multiple copies of the same image and letting the browser pick the most appropriate one. But instead of labeling them with a pixel density (x
) we’re labelling them with their resource width, using w
descriptors. So if baby-s.jpg
is 300×450, we label it as 300w
.
Using srcset
with width (w
) descriptors like this means that it will need to be paired with the sizes
attribute so that the browser will know how large of a space the image will be displaying in. Without this information, browsers can’t make smart choices.
Calculating the sizes attribute
calculating sizes with 3 different breakpoints
<img
...
sizes="
(max-width: 500px) calc(100vw - 2rem),
(max-width: 700px) calc(100vw - 6rem),
calc(100vw - 9rem - 200px)
"
/>
A sizes attribute that gives the browser the width of the image across all three breakpoints, factoring in the layout grid, and all of the surrounding gap, margin, and padding that end up impacting the image’s width.
If you also need…Design Control
using the picture element…
Another goal with responsive images is not just to serve different sizes of the same image, but to serve different images. For example, cropping an image differently depending on the size of the screen and differences in the layout. This is referred to as “art direction.”
<picture>
<source
srcset="baby-zoomed-out.jpg"
media="(min-width: 1000px)"
/>
<source
srcset="baby.jpg"
media="(min-width: 600px)"
/>
<img
src="baby-zoomed-in.jpg"
alt="Baby Sleeping"
/>
</picture>
This code block is an example of what it might look like to have three stages of an “art directed” image.
- On large screens, show a zoomed-out photo.
- On medium screens, show that same photo, zoomed in a bit.
- On small screens, zoom in even more.
Art direction can do a lot more than just cropping
Although cropping and zooming like this is the most common form of art direction by far, you can do a lot more with it. For instance, you can:
- Dark-ify™ images for users in dark mode,
- avoid sending animated GIFs to users with a “prefers reduced motion” accessibility preference,
- re-arrange image content so that it all fits “above the fold” on short viewports,
- set a maximum resolution cap, to save users on 3×-plus devices a lot of bytes,
- send static, high-res, monochrome images to printers and e-ink devices.
Combining source
and srcset
Because <source>
also uses the srcset
syntax, they can be combined. This means that you can still reap the performance benefits of srcset
even while swapping out visually-different images with <source>
. It gets pretty verbose though!
<picture>
<source
srcset="
baby-zoomed-out-2x.jpg 2x,
baby-zoomed-out.jpg
"
media="(min-width: 1000px)"
/>
<source
srcset="
baby-2x.jpg 2x,
baby.jpg
"
media="(min-width: 600px)"
/>
<img
srcset="
baby-zoomed-out-2x.jpg 2x
"
src="baby-zoomed-out.jpg"
alt="Baby Sleeping"
/>
</picture><picture>
<source
srcset="
baby-zoomed-out-2x.jpg 2x,
baby-zoomed-out.jpg
"
media="(min-width: 1000px)"
/>
<source
srcset="
baby-2x.jpg 2x,
baby.jpg
"
media="(min-width: 600px)"
/>
<img
srcset="
baby-zoomed-out-2x.jpg 2x
"
src="baby-zoomed-out.jpg"
alt="Baby Sleeping"
/>
</picture>
if you want to use an image in the WebP format. It’s a pretty great image format, often being the most performant choice, and it’s supported everywhere that the element is, except Safari. A fallback can be used
<picture>
<source srcset="party.webp">
<img src="party.jpg" alt="A huge party with cakes.">
</picture>
Using Webp images
It can be 10% of the jpg version of the image. There are online converters, command line tools, and some modern design software, like Sketch, helps you export in that format. My preference is to use an image hosting CDN service that automatically sends images in the perfect format for the requesting browser, which makes all this unnecessary (because you can just use img/srcset).
CDN service that do this, to name a few:
- Cloudinary offers it
- Netlify offers it
- imgix offers it
- Image Optim offers it
- Filestack offers it
- Cloudflare offers it
Automated responsive images
The syntax of responsive images is complex to the point that doing it by hand is often out of the question. I’d highly recommend automating and abstracting as much of this away as possible. Fortunately, a lot of tooling that helps you build websites knows this and includes some sort of support for it. Here are some examples…
- Cloudinary has this responsive breakpoints tool including an API for generating the perfect breakpoints.
- WordPress generates multiple versions of images and outputs in the responsive images syntax by default.
- Gatsby has a grab-bag of plugins for transforming and implementing images on your site. You ultimately implement them with gatsby-image, which is a whole fancy thing for implementing responsive images and other image loading optimizations. Speaking of React, it has component abstractions like “An Almost Ideal React Image Component” that also does cool stuff.
- Nicolas Hoizey’s Images Responsiver Node module (and it’s Eleventy plugin) makes a ton of smart markup choices for you, and pairs nicely with a CDN that can handle the on-the-fly resizing bits.
CSS with background images
The trick is to use @media queries to change the background-image source.
const { createApp, ref } = Vue;
const appcommon = createApp({
data() {
return {
navbar: false,
out: 'hello vue'
};
},
methods:{
toggleNav(){
this.navbar = !this.navbar;
if(this.navbar === true){
const menu = document.getElementById('menu-menu-main');
const menuItems = menu.querySelectorAll('.menu-item');
gsap.fromTo(menuItems, {x: -20, opacity: 0}, {x: 0, opacity: 1, duration: 0.3, stagger: 0.2});
}
},
itemAnimation(){
//
}
}
});
appcommon.mount('.theme-wrapper');
<div class="row">
<div class="col-11 col-md-8 m-auto">
<?php the_content(); ?>
<hr>
</div>
</div>