9

My website hosts a lot of images of all sizes. These images are responsive and change size at all browser widths from desktop to mobile. I see in my Google Search Console that I have a poor CLS (cumulative layout shift) of .25s. The layout of my website shifts as the images load.

Since my images are responsive, I can't specify exact sizes of the images, or have placeholders to reserve the space.

What is a modern way to prevent CLS with responsive images?

Layout here: https://jsfiddle.net/exspwgab/


Update: I tried one suggestion on the internet which is to specify the image file's width and height within the img tag like:

<img src="photo.jpg" width="1504" height="752">

And then in CSS you do:

width: 100%;
height: auto;

This didn't seem to work in any browser. And the elements on my webpage still moved all over as the images loaded.

If anyone has a solution that works in all browsers please let me know. I essentially need placeholders to hold the space while the images load to prevent the page jank issue.

JSFiddle of my responsive layout here: https://jsfiddle.net/exspwgab/

javinladish
  • 907
  • 1
  • 9
  • 18
  • Please find recommandation here: https://web.dev/optimize-cls/ – MaxiGui Sep 23 '20 at 21:53
  • I tried applying width and height within the image tags and then setting width: 100% and height: auto in the CSS, but for some reason that breaks the images on Safari. Currently I have to have width and height set to 100% for it to not break my layout. – javinladish Sep 24 '20 at 01:35

5 Answers5

1

I am not sure if this is exactly "a modern solution" to the CLS issue but just trying to be helpful as much as I can.

Obviously, it's not logically possible to put constant-sized placeholders for the responsive image elements. What if we use placeholders/elements with fixed-sizes for the responsive contents?

For example:

img.placeholder-image {
    width: 100%;
    height: 256px;
    object-fit: contain; 
}

With the fixed-height, this element won't add up anything negative to the CLS policy while keeping the whole image content inside the element itself even if the viewport gets resized.

I'd very much suggest you consider using <div>s instead of <image> elements to display image contents (using background property), however, I can't vouch that's not another violation of audit rules.

My two cents.

Nick Song
  • 1,988
  • 3
  • 29
  • 35
0

HTML:

<img width="300" height="450" src="300x450.jpg">

CSS:

    img {
        height: auto;
        aspect-ratio: 2/3;
        max-width: 100%;
    }

Target browsers:

  • Chrome 88+
  • Edge 88+

Articles:

Artur INTECH
  • 6,024
  • 2
  • 37
  • 34
0

I had absolutely same problem. Solution is change width: 100% to max-width: 100%

this is implicitly stated on https://web.dev/optimize-cls/

img {
 width: 100%; /* or max-width: 100%; */
 height: auto;
}
-1

If you need to do what you're doing... don't worry about it.

Tools that identify potential problems with your site don't know the context. For example, suppose my site had a huge 20 MB image that took several seconds to load. Google's tools would undoubtedly flag this as a problem. But, maybe in my example, my site is hosting scientific imagery or something that requires a lossless large image size. My users would happily spend a few seconds loading the data they need.

If the layout of your site requires that you load images that are then resized dynamically, then that's what it requires and you shouldn't worry about it.

Brad
  • 159,648
  • 54
  • 349
  • 530
  • 2
    I like your response. However, I agree with Google that "cumulative page shift" is not a great experience, and I would like to somehow have placeholders for the images so my page isn't shifting a bunch as the images load on the page. Currently, all the titles/captions of the images shift as the page loads and it looks very janky. – javinladish Sep 24 '20 at 02:42
  • @javinladish Without seeing your code or an example of the specifics, it's hard what to suggest to you. There is no one single solution. – Brad Sep 24 '20 at 02:47
  • I will make a JSFiddle – javinladish Sep 24 '20 at 02:51
  • @javinladish Sounds good. It might be also worth seeing a screenshot of your layout. Usually, you'll simply have to do something fundamentally different, but sometimes there are workarounds. – Brad Sep 24 '20 at 02:54
  • Here is the exact layout I'm working with https://jsfiddle.net/exspwgab/ – javinladish Sep 24 '20 at 03:09
  • @javinladish Alright, so with that layout, there isn't going to be a one-size-fits-all solution. You'll have to get creative. I'm assuming this is more of a problem when you have thousands of items that require layout? For that, you might actually have to dynamically add/remove items from the document with JavaScript. That is, display a few things in the current viewport at the scroll offset, and then as the user scrolls down, display the other things. If they scroll back up, switch again. This is a real hassle for you, unfortunately, but probably required. – Brad Sep 24 '20 at 03:29
  • @javinladish You might also try other layouts to see if you can get away with something other than CSS-driven columns. For example, could you detect via script how many columns are needed, and then have the items stack vertically? That would give better performance, but is a hack for sure. Grid layout would also likely give you better performance, but that's probably not quite what you're looking for. – Brad Sep 24 '20 at 03:30
  • I would like to keep the current layout/html structure and find a solution for it. I am currently seeing if the lazySizes JS plugin will work. Let me know if you have any other suggestions. – javinladish Sep 24 '20 at 04:13
  • https://stackoverflow.com/questions/23416880/lazy-loading-with-responsive-images-unknown-height This seems promising – javinladish Sep 24 '20 at 04:28
-1

I ended up using the solution found here: http://davidecalignano.it/lazy-loading-with-responsive-images-and-unknown-height/#:~:text=And%20here%20is%20the%20formula,flashes%20on%20lazy%20loaded%20images.

HTML

<a class="thumb lazy-container" href="#">
 <img class="lazy" data-original="image.jpg" alt="">
</a>

CSS

.lazy-container {
 display: block;
 position: relative;
 height: 0;
}

.post .lazy-container {
 padding-bottom: 55.3%;
}

.lazy-container img {
 position: absolute;
 top: 0;
 left: 0;
 width: 100%;
 height: 100%;
}

Since all of my images have a different height, I put the padding-bottom percentage as an inline style for each image.

javinladish
  • 907
  • 1
  • 9
  • 18