A recent audit (client to remain nameless), reminded me of a pattern that I have been seeing a LOT lately, which looks something like this:
<div class="image-wrapper">
<img class="image--desktop"
src="/image-desktop.jpg"
width="1200" height="400"
alt="Super descriptive text">
<img class="image--mobile"
src="/image-mobile.jpg"
width="600" height="800"
alt="Super descriptive text">
</div>
Then there is some associated CSS that “toggles” which image should be visible, depending on the viewport width, something like:
.image--desktop {
display: none;
}
@media (min-width: 1200px) {
.image--mobile {
display: none;
}
.image--desktop {
display: block;
}
}
Basically, hide the desktop image and show the mobile image until 1200px, then hide the mobile image and show the desktop image…
There are a number of problems with this pattern, starting with:
- Unnecessary, duplicate code, which bloats the file that the user has to download, the browser has to render, and the JS engine crawl and monitor.
- The above issues gets far worse when those
imgelements get expanded with additional CSS classes,srcsetandsizesattributes, etc., for all of the images on the page… - Bloated CSS with those “toggle” declarations.
- More confusing code to debug, as the dupe
imgelements are typically nested within siblingdivelements, not side-by-side in the HTML, like my simplified example. - Most grievously, both the mobile and desktop images are downloaded, because the CSS only prevents the display, not the download.
- All of these images downloading blocks the tubes for other important assets, which could increase your INP.
- Made worse when the hero image gets a
loading="eager"and/orfetchpriority="high"attribute/value, because now you are jumping the queue with images you don’t even need! - For some reason, I typically see the desktop image appearing first in the HTML, so that larger image gets downloaded first, meaning mobile users have to wait before “their” image can start to download, often on slower connections, which increases their LCP.
- Similarly, when Early or Resource Hints get involved, things often get even worse, causing unnecessary preloads.
I’m sure there are more issues that aren’t popping into my brain as I rant this, but I think you get the picture…
And speaking of picture, that’s actually the solution! :-)
Instead of all that duplicate HTML and unnecessary CSS, all you need is this:
<div class="image-wrapper">
<picture>
<source
src="/desktop-image.jpg"
width="1200" height="400"
media="min-width: 1200px">
<source
src="/mobile-image.jpg"
width="600" height="800"
media="max-width: 1199px">
<img src alt="Super descriptive text">
</picture>
</div>
As is often the case, this comes back to using the right element for the right job.
Use CSS when you need it. JS, too. But HTML is wonderful, often all on its own.
Now please, stop using this NSFWing pattern!
Happy reducing,
Atg