https://jakearchibald.com/2022/img-aspect-ratio/
Jake Archibald wrote...who?
Avoiding layout shifts: aspect-ratio vs width & height
attributes
Posted 11 July 2022
By default, an takes up zero space until the browser loads
enough of the image to know its dimensions:
When you run the demo, you'll see the immediately. Then,
after a few seconds, this paragraph and subsequent page content
shifts downwards to make room for the image. This makes the user
experience massively frustrating, as content moves out from under the
user's eyes/finger/pointer.
For over a decade, we had to use silly hacks to manually apply an
aspect ratio, and then, bloody typical, two better solutions arrived
at roughly the same time. They are CSS aspect-ratio, and width &
height presentational hints.
So, which should you use? First, let's take a look at how the
features work, as there's quite a bit of misinformation out there...
CSS aspect-ratio
If you do this:
.aspect-ratio-demo {
aspect-ratio: 16 / 9;
}
...you get this:
16 / 9
This feature became cross-browser compatible once it landed in Safari
15, late 2021, having arrived in Chrome & Firefox earlier that year.
It works on any element, but here's a demo with :
img {
aspect-ratio: 16 / 9;
width: 100%;
}
This time, the image reserves space for its content as soon as it
appears in the document, so stuff doesn't shift around once it loads.
The other solution is...
Width & height presentational hints
If you set dimensions on your image:
And set height to auto:
img {
height: auto;
}
...the image will have an aspect ratio applied, even before it loads.
This landed in Chrome and Firefox back in 2019, and became
cross-browser compatible when it landed in Safari 14 a year later.
So, this feature has been around a little longer than CSS
aspect-ratio.
In addition to , this feature also works on and .
However, there's a bit of misinformation floating around...
No, this doesn't use attr()
A lot of articles say this feature works via a user-agent stylesheet
like this:
This isn't how it actually works
img,
input[type='image'],
video,
embed,
iframe,
marquee,
object,
table {
aspect-ratio: attr(width) / attr(height);
}
Firstly, this feature doesn't work on embed, iframe, marquee, object,
or table. But also, this usage of attr() wouldn't work in practice
because it returns a string. To make it work properly, you'd need to
cast the attribute to a number, which CSS supports!
This isn't how it works either
img,
input[type='image'],
video {
aspect-ratio: attr(width number) / attr(height number);
}
But this isn't how it works, because:
No browser supports attr on all properties Support for attr() on
caniuse.com
...no browser supports attr(), aside from very particular cases like
content on pseudo-elements. Hopefully that will change one day!
How does it actually work?
Here's what the spec has to say:
The width and height attributes map to the aspect-ratio property
(using dimension rules) on img and video elements, and input
elements with a type attribute in the Image Button state.
-- Attributes for embedded content and images - HTML
So, it does map to the aspect-ratio property. Specifically:
...the user agent is expected to use the parsed dimensions as a
presentational hint for the 'aspect-ratio' property of the form
auto w / h.
-- Mapping to aspect-ratio - HTML
You can think of a "presentational hint" as something a bit like a
zero-specificity inline-style that's applied internally.
This uses a feature of aspect-ratio I didn't mention earlier:
.aspect-ratio-demo {
aspect-ratio: auto 16 / 9;
}
The new bit is auto. Here's what the spec says:
If both auto and a are specified together, the preferred
aspect ratio is the specified ratio of width / height unless it
is a replaced element with a natural aspect ratio, in which case
that aspect ratio is used instead.
-- CSS-sizing level 4
A lot of articles gloss over this, probably because the spec text is
a little hard to read, but it adds an important bit of behaviour:
An is a "replaced element", but it doesn't have a "natural
aspect ratio" until the browser has loaded enough of the image to
know its real width & height. That means the 16 / 9 bit is ignored
once the browser has the real data from the image. This doesn't
usually matter, because the result is the same. But, let's say I got
the width and height wrong:
The browser will use an presentational hint of aspect-ratio: auto 4 /
3, but the image is actually 16 / 9. Here's what happens:
When the image is added to the page, it takes up the 4 / 3 area I
specified. But once it loads, the auto rule kicks in, and the browser
corrects the aspect ratio to 16 / 9.
Compare this to the behaviour of:
img {
aspect-ratio: 4 / 3;
width: 100%;
}
Without auto, the remains 4 / 3, and the image appears
stretched. You can avoid the stretching with object-fit:
img {
aspect-ratio: 4 / 3;
width: 100%;
object-fit: cover;
}
In this case, parts of the image are cropped. Oh, one more thing:
A slight issue in Firefox
Responsive images let you provide different images to use at
different widths:
In this case, the two images have different aspect ratios. Chrome and
Safari use the correct width and height depending on which source is
used, but Firefox will always use the dimensions from the ,
resulting in a content shift when it realises the calculated
aspect-ratio is incorrect.
Here's the Firefox bug to track this. Hopefully it'll get fixed soon!
So, which method should we use?
Ok, until now the article has been a massive side-quest. Now we're at
the actual point. And, in my opinion, the answer is... nothing new.
We've got one solution, aspect-ratio, which is CSS-based. And the
other solution, presentational hinting, which uses width and height
attributes. The question is very similar to "should this image be a
or a CSS background-image?" and the answer is the same: Is it
content or design?
If I'm adding an image to an article on my blog, that's content. I
want the reserved space to be the aspect ratio of the content. If I
get the width and height attributes wrong, I'd rather the correct
values were used from the content image. Therefore, width and height
attributes feel like the best fit. This means I can just author
content, I don't need to dip into inline styles.
If it's a design requirement that the layout of an image is a
particular aspect ratio, enforcing that with aspect-ratio in CSS can
be appropriate. For example, a hero image that must be 16 / 9 - if
the image isn't quite 16 / 9 I don't want it messing up my design, I
want the design to take priority. Although, if the image isn't
actually that aspect ratio, it'll either end up stretched
(object-fit: fill), letter-boxed (object-fit: contain), or cropped
(object-fit: cover). None of which are ideal.
You could use aspect-ratio and media queries to make up for the lack
of support in Firefox when it comes to and art direction.
But, I'm hoping that they'll fix that bug sooner rather than later,
so we don't need to hack around it.
And that's it! It took a decade for us to get a real solution to this
problem, but now you can avoid layout shifts using width and height
attributes, or aspect-ratio in CSS, whichever is most appropriate.
View this page on GitHub
I hate that Disqus doesn't work without JavaScript. It should.
Comments powered by Disqus
Jake Archibald next to a 90km sign
Hello, I'm Jake and that is my tired face. I'm a developer advocate
for Google Chrome.
Elsewhere
* [twitter-18]Twitter
* [github-e70]Github
* [youtube-89]YouTube
* [podcast-c8]Podcast
Contact
Feel free to throw me an email, unless you're a recruiter, or someone
trying to offer me 'sponsored content' for this site, in which case
write your request on a piece of paper, and fling it out the window.