I am playing around with the HTML5 <picture>
tag and I am trying to understand how it works.
What I have got so far:
I've created the little snippet below:
<picture>
<source srcset="https://placehold.co/360/webp 360w, https://placehold.co/768/webp 768w, https://placehold.co/1280/webp 1280w, https://placehold.co/2560/webp 2560w" type="image/webp" sizes="(max-width: 360px) 360px, (max-width: 768px) 768px, (max-width: 1280px) 1280px, (max-width: 2560px) 2560px">
<img src="https://placehold.co/150/webp" alt="this is a fallback">
</picture>
As far as I've understood, it should load the best fitting (sources are more important than the image as it is the fallback) image-source from placehold.co
for the given sizes.
What does not work
The sources change with the viewport, but it does not behave as expected. The changes seem to be happening somewhat arbitrarily.
For example, I'd imagined the picture-element to load up the image with a resolution of 360px
on a viewport width of <360px
, but it loads the 768px
variant instead. When I make it bigger it will load the 2560px
straight without loading the smaller variant first and it will never go back to loading smaller image sizes when I resize the viewport. I've read this mdn article here, but I did not get the desired result for my use-case.
Questions:
How does the loading logic work with the different viewports for the <picture>
element?
360px
I might want to load a small image that fits <360px
and not be forced to load 768px
or even a bigger image.I do understand, however, why it is beneficial to have source-sets as such and different image variants. That is not the question here.
Tested your code snippet in Chrome Version 114.0.5735.106 (Official Build) (x86_64) and Firefox 113.0.2 (64-bit).
Seems to behave as expected - viewports up to 360px get the 360px image, viewports from 361px on get the 720px image, and so on. It's a little hard to see the resolution-label on the higher res images, as they go flying off the viewport.
Some things that may shed some light on the issues you're seeing:
Images not changing when making the viewport smaller: Your browser might cache the 768px image. When you then make the viewport smaller, it doesn't switch to the smaller one because hey, it already got a larger one here. (Chris Coyer mentions this here, Kevin Powell mentions caching in Chrome specifically here)
Breakpoints behaving differently from the ones specified: you might wanna check the DPR (device pixel ratio) of the screen your using. You can also set it in your dev tools. DPR is also a factor in what image the browser chooses to download, mentioned in the spec here:
The picture element is a container which provides multiple sources to its contained img element to allow authors to declaratively control or give hints to the user agent about which image resource to use, based on the screen pixel density, viewport size, image format, and other factors.
(emphasis mine)
BTW It seems that you can also cut out the whole sizes
attribute in your specific case, because you're basically setting a size of 100vw
for each viewport. 100vw
is the default that the browser assumes, when there's no sizes
attribute given. (Can't find it clearly stated in the specs, but here's Kevin Powell talking about it in a video)
This is extra weird, because MDN states:
Note: If the srcset attribute uses width descriptors, the sizes attribute must also be present, or the srcset itself will be ignored.
This doesn't seem to be the case on either of the browsers mentioned at the top, at least. From reading about this topic, I gather that the spec gives considerable freedom as to how user agents implement the decision of which image to actually fetch over the network.
Here's your code with no sizes
attribute:
(I added max-width: 100%; height: auto
so that the resolution label stays legible)
<picture>
<source srcset="https://placehold.co/360/webp 360w, https://placehold.co/768/webp 768w, https://placehold.co/1280/webp 1280w, https://placehold.co/2560/webp 2560w" type="image/webp">
<img src="https://placehold.co/150/webp" alt="this is a fallback" style="max-width: 100%; height: auto">
</picture>