This “double HTML” pattern must stop!

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 img elements get expanded with additional CSS classes, srcset and sizes attributes, etc., for all of the images on the page…
  • Bloated CSS with those “toggle” declarations.
  • More confusing code to debug, as the dupe img elements are typically nested within sibling div elements, 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/or fetchpriority="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

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

To respond on your own website, enter the URL of your response which should contain a link to this post's permalink URL. Your response will then appear (possibly after moderation) on this page. Want to update or remove your response? Update or delete your post and re-enter your post's URL again. (Find out more about Webmentions.)