Responsive Images, The sizes Attribute, and Unexpected Image Sizes
tl;dr: When not constrained by CSS or the width attribute, the new HTML5 sizes attribute — even if left to its default — will stretch an image, potentially making it larger than its “physical” dimensions.
My Outdated [Technically Correct] Assumption About Image Sizes
Here’s the simplest HTML you can have for an image:
Other than the above being inaccessible, that image is perfectly fine and will appear on a computer screen at it’s natural size by default. If example.jpg is 2000px wide, it’ll be 2000px wide on the screen.
Of course in the world of responsive images, we put constraints on our images with CSS:
Now the image appears at it’s natural size unless it’s constrained by the parent container! Excellent.
That’s all I needed for the responsive websites I built in the last few years.
Responsive Images Make Things Better (and More Complicated)
Responsive images are a huge win for us front-end developers because we can specify srcset and sizes attributes and encourage browsers to only download the largest image they need:
For browsers that support it, we’ve now got responsive images. The sizes attribute tells browsers to assume the image fills the entire browser width (vw = “viewport width”). Importantly, if you specify srcset but not sizes, “100vw” is the assumed default of the sizes attribute. (Foreshadowing!)
It’s then up to the browser to pick the most appropriate image from the srcset to use based on the sizes we tell them. For example:
- Smart Watch? You might get example-thumb.jpg.
- Brand new 27" iMac? You get email@example.com!
(Browsers that don’t understand srcset just gracefully fall back to src like they always do!)
Where Things Get Tricky
So we’re telling browsers the perfect image to select. That’s great! Here’s the problem: what happens when the sizes attribute wants a bigger image than we have?
The answer? Bad stuff. Let’s take a look:
sizes="(min-width: 400px) 400px, 100vw"
This time, we’re telling the browser that the image will fill the browser window up to 400px wide, after which it will always appear as 400px wide. This is the kind of thing you might see for a logo in a user-edited directory.
We might even make sure that this image never exceeds 400px with CSS:
The problem is that the well-intentioned but technologically-clueless nonprofit Widgets for the World only uploaded a 200px-wide logo. What happens? The browser stretches the image to 400px wide, even though the image is only 200px wide!
Why? Because of what we told the browser in sizes.
The first time I ran into this, I did not expect it, but it turns out to be the intended behavior. From MDN‘s <img> specification:
The selected source size affects the intrinsic size of the image (the image’s display size if no CSS styling is applied).
Here’s how that looks in practice in Chrome, Firefox, Safari, and Edge:
That image only contains 200 pixels horizontally, but the browser stretches it to 400px wide or even farther!
Luckily, you’ll see there’s an easy “fix” there at the end: our old good friend the width attribute!
sizes="(min-width: 400px) 400px, 100vw"
width="200" /* <=== TA-DA! */
As long as you can specify the width attribute so it reflects the true maximum size of your largest image, you won’t run into this problem of having sizes make your image wider than it naturally should go.
Does This Really Happen?
Yes. I just ran into this problem on a project much like the example where we had logos of unknown widths for use in a layout with a standardized profile layout. Any situation with a Content Management System that doesn’t enforce minimum image sizes when uploading is ripe for this issue.
I wish we lived in a world where everyone understood pixel density and Photoshop and why you shouldn’t save your logo as a JPG, but we don’t, nor will we ever.
These little edge cases have a way of turning into major headaches. I hope I saved you one.
- Unfortunately, there’s a bug in iOS right now where it downloads the first image specified in srcset in most cases. That means you can either use a polyfill or just specify a good-enough size first in srcset as I did in the first srcset example above.
- As I mention, Microsoft Edge gets this right for those on Windows 10, but IE 11 actually does constrain the image to it’s natural size. This may feel right — like the IE Box Model Bug — but it’s not.
About The Author
Mark Root-Wiley makes WordPress websites for nonprofits and other mission-driven organizations. He usually blogs at MRW Web Design, but that’s for less technical stuff about usability and nonprofit websites. He also just started NonprofitWP.org for giving free advice to nonprofits doing DIY WordPress websites.