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. We will look at both methods and the ratio calculations behind them.

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

With aspect-ratio, you can write the ratio directly:

.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 */

aspect-ratio sets a preferred ratio rather than 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 because 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

Every ratio calculation starts from this 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

When a source (an image, a video) has one ratio and its box has another, you can fit it in two ways. They are 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):

Even when object-fit handles this, the math tells you how much of a hero image gets cropped on a specific screen.

width, height attributes and CLS

Aspect ratios also help prevent layout shifts.

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">

Your CSS still controls the displayed size, typically with width: 100%; height: auto. The attributes give the browser the ratio. Modern browsers internally derive aspect-ratio: 1600 / 900 from them and reserve the correct space before the image loads.

Use the HTML attributes for the ratio and CSS for responsive sizing. The browser can then reserve space without a wrapper hack.

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

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about css: