While trying to implement a full-bleed background image I think I've discovered an issue with the combined use of the responsive img
srcset
/sizes
attributes and CSS's object-fit: cover;
.
Given the following simple test case:
<img class="background"
srcset="//placehold.it/320x180 320w,
//placehold.it/640x360 640w,
//placehold.it/960x540 960w,
//placehold.it/1280x720 1280w,
//placehold.it/2560x1440 2560w"
src="//placehold.it/320x180"
sizes="100vw">
.background {
position: absolute;
display: block;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
(JSFiddle here; isolated results page here)
If the viewport is sized to resemble a portrait smartphone (e.g. to 320×568), the browser must scale up the image vertically to achieve the "cover". This extends the image outside of the viewport in the horizontal dimension, however (at least in Chrome with cache disabled†) because of sizes='100vw'
(and when omitting it, as 100vw
is the default) the browser still selects the source most appropriate to the viewport width of 320px, resulting in a pixelated image:
I think I understand why the browser is doing this (the image element itself is still 320px wide, regardless of the visible size of the image) but for my use-case it's a bummer.
The only workaround I could think of was to "overload" the sizes
attribute to force a higher-resolution source selection. Because my sample images are 16:9, I can use a simple calculation plus an orientation
media query to help the browser understand the situation, and tell it the desired width using dynamic units:
<img class="background"
srcset="//placehold.it/320x180 320w,
//placehold.it/640x360 640w,
..."
src="//placehold.it/320x180"
sizes="(orientation: landscape) 100vw,
calc(100vh / 9 * 16)">
(JSFiddle here; isolated results page here)
This seems to work, but I don't like having to rely on support for the orientation
media query, and I'm not confident this approach is bulletproof even with that support. It just seems a bit fragile.
Am I missing something? Is this a known issue? I haven't been able to find any mention of this conflict in my research today. If there's a more robust way to work around this, I'd love to hear it. Thanks!
† If allowed to read from its cache, Chrome (and others) will use any higher-resolution source that was previously loaded, giving inaccurate results.