At Unsplash, one very common problem we have to deal with is responsive images. How can we serve appropriately sized images to the vast variety of devices that visit our website — for all types of screen sizes, device pixel ratio, and so forth?
To solve this problem, we use a mixture of the
img element with its
sizes attributes along with the
picture element. In our experience, however, the hardest part in getting this right is meticulously finding the correct values for the
sizes attribute. Once we've done that, we want to write tests for it, to ensure it stays in sync with the image's actual size in the page layout. In this article we'll review the approach we took at Unsplash in order to do this, which is facilitated by a small module we wrote called
Responsive images recap
sizes attribute tells the browser how wide an image will appear for all media conditions (namely window width), so that it can then use this information to download the closest matching image source—from the source set (
srcset attribute)—according to its width descriptor. (The browser can't work this out for itself because it needs to start downloading images before it starts laying out the page.)
In this example, the
sizes attribute tells the browser that this image will be
200px wide on screens smaller than
300px wide on screens equal to or larger than
500px. The browser then uses this information, together with the current device pixel ratio, to pick the most suitable image source from the
Testing responsive images
In order to make sure we’re serving our responsive images efficiently, we need to take extra care to make sure the
sizes attribute is accurate, in that it should reflect the size of the image in the page layout.
Deciding what value to use for
srcset isn't so difficult. We can simply provide a set of images on a spectrum of small to large widths.
We want to ensure our
sizes continues to stay in sync with the page layout. If a change to the page layout effects the image size, we will need to update the
sizes attribute to reflect this. For example, our
sizes may specify an image on desktop will always be
300px wide, but a layout change means it is now
400px wide. Ideally, if we forgot to update
srcset alongside any layout changes, our tests would fail and serve as a reminder to update it.
Firstly, we navigate to the website. For example, in Puppeteer:
Next, we perform any test setup such as resizing the window.
Next, we parse the
sizes attribute to calculate the current value. For example, given a
sizes attribute of
(min-width: 500px) 300px, 200px, if our window width was
600px, the current value after parsing would be
300px. This is achieved using
parse-sizes, evaluating any
calc expressions in the result, and then reading the computed value.
Finally, we can assert that the parsed value from the
sizes attribute (
parsedSize) equals the image's actual width (
getAllImageSizes function also returns the width of the image's current source (
currentSrcWidth), which would be helpful if we wanted to test we had a well defined
srcset. For example, we could assert that the current source width is approximately equal to the image's actual width.
We have also found these helpers to be useful in development, by observing and logging the size values as they change, e.g. on resize:
In practice, here is what that looks like:
You can try this out now on your own responsive images by importing the
responsive-image-test module we wrote, which bundles the code demonstrated above.