Image elements do not have explicit width and height
Asked Answered
G

1

35

Lighthouse keeps telling me that "Image elements do not have explicit width and height" eventhough i've tried to add it both in css or in img but the problem persists:

img.medium {
  max-width: 29rem;
  width: 100%;
}

or

<img
   src={user.seller.logo}
   alt={user.seller.name}
   width="100%"
   height="auto"
/>

How can i tackle this?

Galactometer answered 30/11, 2020 at 15:52 Comment(0)
T
44

Short Answer

Add the image's native width and height in pixels as attributes on the image. This lets the browser calculate the aspect ratio for the image.

<img width="600" height="400" src="some-image.webp"/>

Long Answer

width: 100% does not give an element an explicit width.

What it does is define the width relative to it's parent container.

height:auto also does not give an image an explicit height, you are instructing the browser to make the image height whatever it thinks is appropriate based on the image proportions or the size of the container.

The biggest problem with this is the height of an image (as although the width of 100% is not explicit, it will be easy to calculate the actual width purely from the CSS).

When the page is requested the browser does not know the proportions of the image until it starts downloading.

For this reason the page renders (assuming you have inlined your critical CSS) then it requests the image and finds out how tall the image is. The container for the image will then change size to accommodate this image and you will get a layout shift, contributing to cumulative layout shift.

How to fix it

Option 1 - define the width and height using attributes

Option one is to define the image height and width as integers representing pixels by using attributes:

<img width="600" height="400" src="some-image.webp"/>

In modern browsers this will then be used to calculate an aspect ratio for the image and sufficient space will then be allocated on the page before the image starts downloading.

You can then use width:100%; height: auto; as you do now and it will all work as expected.

Using width and height attributes is the recommended best practice.

please note - the width and height values only have to be the correct aspect ratio if you are using CSS to override the dimensions (so width=200 height=100 would give the same result as width=400 height=200 assuming you set the width in the CSS).

Option 2 - use "Aspect Ratio Boxes"

In this technique you define the image height as a proportion of the width within your CSS.

In essence we give the image a zero height and then use padding to allocate the correct amount of space.

.image-div {
  overflow: hidden;
  height: 0;
  padding-top: 56.25%; /*aspect ratio of 16:9 is 100% width and 56.25% height*/
  background: url(/images-one.webp);
} 

If you don't need to support Internet Explorer (as it is a little temperamental) you can use calc().

padding-top: calc(900 / 1600 * 100%);

This padding technique is explained in detail in this article from css-tricks.

Although this is a bit of a hack, the advantage is no inline attributes on your images, so it is useful for people who like to keep their HTML clean (and for certain scenarios such as if you want make all images the same aspect ratio)

The problem with both

There is only one issue with both techniques, you need to know the image width and height before they are rendered in order to calculate the aspect ratio.

There isn't much you can do to avoid this unless you are willing to make a fixed height and width container for the image.

Then if the image is a different aspect ratio to your container you will have some white space around the image but the page will not have a layout shift (which would be preferable in most circumstances) - assuming you use overflow:hidden etc. on the container.

.container{
  width:50vw; 
  height:28.125vw; /*fixed height, doesn't have to be aspect ratio correct, see example 2 */
  overflow:hidden;
  margin: 20px;
}
.container2{
  width:50vw; 
  height:40vw; /*fixed height, but this time there is extra white space (shown as dark grey for the example) below the image.*/
  overflow:hidden;
  background: #333;
  margin: 20px;
}
img{
    width: 100%;
    height: auto;
}
<div class="container">
<img src="https://placehold.it/1600x900"/>
</div>

<div class="container2">
<img src="https://placehold.it/1600x900"/>
</div>
Troxell answered 30/11, 2020 at 16:25 Comment(7)
Do you know if it's a problem - either for worse CLS, or otherwise - if the explicit dimensions are different to the actual rendered dimensions? I read that the the explicit size is only used to calculate the ratio of the image, and thus prevent CLS, with images that are set to be width:100% in your css (This is mainly why the element gets flagged in the first place.) I'm using a picture tag with multiple data-srcset, and then a hybrid WP function to output the img tag that contains the height and width of the largest image size - it seems to be working correctly.Artificer
Under "Option 1 - define the width and height using attributes" I did add a note at the bottom to say that as long as the ratio of the width and height is correct it does not matter if you override the width in CSS. It makes no difference provided that your CSS is served before the image is rendered on screen (inlining critical CSS for above the fold images and CSS is served before loading off-screen images). Hope that makes sense. From what I can tell from what you said it will work fine provided all images at all different sizes are the same aspect ratio (you don't use art direction)Troxell
Sorry Graham, I realise now that when I said 'I've read' that..., I was actually quoting you!...i was jumping between articles trying to get to the bottom of it!Artificer
hehe not a problem, I know the feeling trying to get things straight when you are learning them, I do that all too often (and even worse the "I could have sworn I read that somewhere last week" and I haven't bookmarked the page!)Troxell
Ah man, the amount of times I have to search random keywords in my Chrome history to find the right SO post...! :DArtificer
Really awesome contribution @GrahamTheDev. I've created another SO question which builds on your answer here. One question that pops to mind is, if my two images have different intrinsic heights and width. I believe that to mean they have different aspect ratios (hope I understood that correctly?) Are you able to apply fixed height and width to a parent container which will pick the largest width and height of both images and will this fix the lighthouse scores? #75473137Carlenecarleton
Hey @Lauro235, You can set width and height on each item in a <picture> element. It doesn't have perfect support, but it is the most straight forward solution. You can see an example here: https://mcmap.net/q/450325/-specify-explicit-width-and-height-for-picture-tagTroxell

© 2022 - 2024 — McMap. All rights reserved.