Skip to content
FLAVIO COPES
flaviocopes.com

The CSS aspect-ratio property and the math behind it

By

Learn how the CSS aspect-ratio property replaces the padding-top hack, the math behind contain and cover, and how width and height attributes prevent CLS.

~~~

An aspect ratio is the relationship between width and height. A 1920×1080 video is 16:9. A square is 1:1.

For years, keeping an element at a fixed ratio while its width changed required a hack. Now CSS has a property for it. Let’s look at both, and at the math behind ratio calculations, because it comes up more often than you’d think.

The old way: the padding-top hack

Percentage padding on an element is calculated from the width of its parent. Even padding-top.

That quirk was the basis of the classic trick. You give a box zero height and a percentage top padding, and the padding becomes the height:

.box {
  position: relative;
  width: 100%;
  padding-top: 56.25%;
}
.box > * {
  position: absolute;
  inset: 0;
}

Where does 56.25 come from? It’s the ratio math:

height / width × 100 = 9 / 16 × 100 = 56.25%

The content then has to be absolutely positioned inside, because the box itself has no real height to flow into.

I used this exact technique in responsive YouTube video embeds. It works, but it’s indirect, it needs a wrapper element, and nobody remembers the formula.

The modern way: aspect-ratio

Now you write what you mean:

.box {
  aspect-ratio: 16 / 9;
}

Set a width (or let the layout set one), and the browser computes the height. Set a height instead, and it computes the width. No wrapper, no absolute positioning, no percentage tricks.

It works for any ratio:

aspect-ratio: 1;        /* square */
aspect-ratio: 4 / 3;    /* old TV */
aspect-ratio: 2.39 / 1; /* cinemascope */

One important detail: aspect-ratio sets a preferred ratio, not a forced one. If you set both width and height explicitly, or if the content is taller than the computed height, the ratio gives way. That’s usually what you want — text won’t overflow a box just to preserve a ratio. If you truly need to force it, add overflow: hidden or set min-height: 0.

I wrote about the video embed use case specifically in how to embed YouTube videos using the correct aspect ratio.

The ratio math

Behind every ratio calculation there’s one relationship:

width / height = ratio

From it you derive everything:

height = width / ratio
width = height × ratio

So a 16:9 element that’s 800px wide is 800 / (16/9) = 450px tall.

To reduce dimensions to their simplest ratio, divide both by their greatest common divisor. For 1920×1080, the GCD is 120, so you get 16:9. That’s how you find out that 1280×720 and 1920×1080 are the same shape.

I put all of this into an aspect ratio calculator: it reduces any dimensions to the simplest ratio, solves the missing dimension when you lock one side, and computes the fit calculations we’re about to cover.

Contain and cover math

Things get more interesting when a source (an image, a video) has one ratio and the box it goes into has another. There are two ways to fit it, and they’re exactly what object-fit: contain and object-fit: cover do.

Contain: scale the source until it fully fits inside the box. Compare the two ratios first:

The leftover space is the letterbox — the black bars you see when a widescreen movie plays on a squarer screen. The bar size is just the difference, split in two:

letterbox = (box height − rendered height) / 2

Cover flips the logic: scale the source until it fills the box completely, and whatever sticks out gets cropped. Same comparison, opposite branches. If the source is wider than the box, now the height matches the box, the width overflows, and:

crop per side = (rendered width − box width) / 2

A concrete example. A 1920×1080 video (ratio 1.78) in a 400×300 box (ratio 1.33):

This math is worth knowing even if object-fit handles it for you, because it answers real questions: “how much of my hero image gets cropped on this screen?” is a cover calculation.

width, height attributes and CLS

There’s a performance angle to aspect ratios too.

When a browser loads a page, images arrive after the HTML. If the browser doesn’t know how tall an image will be, it lays out the text first, then shoves everything down when the image arrives. That jump is layout shift, and Google measures it as CLS (Cumulative Layout Shift) in its Core Web Vitals.

The fix: always set the width and height attributes on images.

<img src="/images/dog.jpg" width="1600" height="900" alt="my dog">

These attributes don’t fix the displayed size — your CSS still controls that, typically width: 100%; height: auto. What they do is give the browser the ratio. Modern browsers internally derive aspect-ratio: 1600 / 900 from those attributes and reserve the correct space before the image loads.

So the modern setup is: attributes in the HTML for the ratio, CSS for the responsive sizing. No layout shift, no wrapper hacks.

My advice: reach for the aspect-ratio property for boxes, embeds and placeholders, and keep the padding hack in your back pocket only for the rare old browser you still have to support. Everything current understands the property.

Tagged: CSS · All topics
~~~

Related posts about css: