To avoid layout shifting and optimize for the Cumulative Layout Shift web vital in you web pages, you need to reserve space for any content that might be rendered later in time. This is the case for images, videos and any asynchonously loaded content (e.g with AJAX calls). Here’s a new way to do it.

Boxes of different aspect ratios and the text: "A modern way to reserve space for lazy images and async content in responsive design"

The good old way

The traditional way to reserve space for images is to use the vertical padding trick.

<div class="image-wrapper">
  <img
    alt="An image"
    src="image.jpg"
  />
</div>
.image-wrapper {
  width: 100%;
  height: 0;
  padding-bottom: 150%;
  /* 👆 image height / width * 100% */
  position: relative;
}
.image {
  width: 100%;
  height: auto;
  position: absolute;
}

The modern way - mapped

The modern and simpler way is to define a width / height aspect ratio implicitly by defining the width and height attributes on images and videos. This is called “mapped aspect-ratio”.

<img
  alt="An image"
  src="image.jpg"
  width="200"
  height="300"
/>

<video
  alt="A video"
  src="video.mp4"
  width="1600"
  height="900"
>
  ...
</video>
/* Modern browser stylesheets will add a default
  aspect ratio based on the element's existing 
  width and height attributes */
  img, video {
    aspect-ratio: attr(width) / attr(height);
  }

Firefox and Chromium browsers (Chrome, MS Egde, Opera) have already shipped this feature. Mapped aspect-ratio is not supported by Safari yet, but it will be supported in Safari 14 later in 2020.

Check out this pen and note how the paragraph is rendered below the images even before the images start loading.

Lazy images (JS) not supported

Unfortunately, this is not working for images lazy loaded using Javascript. See this pen and see that the width and height attributes have no effect. This is probably because the src/srcset attributes are both missing.

On the other hand, if you use native lazy loading via the loading=lazy attribute on images, it works good. But you can use native lazy loading only for images and iframes (not videos). 🤷‍♂️

The modern way - explicit

In the near future, you will also be able to explicitly set the aspect ratio in your CSS code using the aspect-ratio CSS rule.

<div
  class="async"
>
  Content is loading...
</div>
.async {
  aspect-ratio: 16/9;
}

This is experimental and it has currently (July 2nd, 2020) only shipped to Chrome Canary. We still don’t know if this will be supported in Safari 14, which announced support only for mapped values.

Conclusion

Will we be able to ditch the vertical padding trick in favor of mapped or explicitly set aspect-ratio CSS rule?

It depends on what Safari 14 will ship and on how quickly Internet Explorer 11 will disappear from the market share.

I’m not that optimistic but… Fingers crossed, developers! 🤞