Somewhere in your codebase there's a CSS rule that looks like this, and you've probably typed it from memory without thinking about why it's shaped like that:
.video-wrap {
position: relative;
width: 100%;
padding-top: 56.25%;
}
.video-wrap > iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
A wrapper <div> you didn't want to add, a padding-top percentage that means nothing to anyone reading it for the first time, and a child element yanked out of the document flow with position: absolute just so it can fill the box the padding created. If you've ever had to explain this to a junior dev, you know how it goes: you say "padding percentages are based on the width of the parent, so this fakes a 16:9 box," and you watch their face do the thing where they nod but don't believe you.
It works. It's also three rules and a wrapper element to do something that should be one line.
Why anyone ever wrote this
Before 2021, CSS had no way to say "keep this box at a 16:9 shape no matter how wide it gets." You could hardcode a height, but that breaks the second the viewport resizes. So people reached for the one property that is calculated as a percentage of an ancestor's width in every browser back to CSS2.1: padding. Set padding-top: 56.25% (9 ÷ 16) on an empty box, and its height becomes 56.25% of its own width — a perfect, responsive 16:9 rectangle with nothing but a padding declaration. The catch is that the padding now is the box's content area, so whatever you actually wanted to show — a video embed, an image, a map — has to be absolutely positioned on top of it, disconnected from normal layout.
It was clever. It was also fragile: get the percentage wrong (people used padding-bottom too, same math, same effect) and nobody notices until a designer points at a slightly-off video embed. Every ratio box needed its own wrapper <div>. And it never worked cleanly inside a CSS Grid or Flexbox item, because you were fighting the box model instead of using it.
One property, no wrapper
aspect-ratio does what the hack was simulating, directly:
.video-wrap {
aspect-ratio: 16 / 9;
}
That's the whole thing. No wrapper, no absolute positioning, no percentage math to double-check. Give an element a width (or let it stretch to fill a grid track or flex container) and the browser derives the height from the ratio you named — or the other way around if you constrain the height instead. It works as a grid item, a flex item, a plain block box, sitting in normal document flow the entire time.
🎮 Try it yourself
▶️ Open the interactive playground →
Runs right in your browser — poke at it and watch the concept react live.
The gotcha nobody mentions: it doesn't crop
Reach for aspect-ratio on a real <img> the same way you did on the video wrapper, and you'll hit the one case where it behaves differently than people expect:
img.hero {
width: 100%;
aspect-ratio: 16 / 9;
}
Drop in a portrait photo and it does not get cropped to fit — it gets squished. aspect-ratio sets the size of the box; it says nothing about how the image's own pixels should be laid out inside that box. An <img> is a replaced element with its own intrinsic ratio, and when you hand it an explicit aspect-ratio that doesn't match, the browser stretches the bitmap to fill the box you asked for. Faces get wide. Circles become ovals. It looks like a bug, and it ships anyway because it renders fine in a browser preview with an image that happens to already be 16:9.
The fix is one more line, and it's the same property you'd reach for anywhere else you crop a background image:
img.hero {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
object-fit: cover tells the image to fill the box and crop the overflow, the same way background-size: cover does for a CSS background. aspect-ratio decides the shape of the frame; object-fit decides how the picture sits inside it. Skip the second one on a photo and you've built a fun-house mirror.
The part that quietly fixes layout shift too
Here's the detail that makes this worth caring about even if you never write another padding-top hack: modern browsers already compute an aspect ratio for you, from the plain HTML width and height attributes on an <img> tag — not the CSS properties, the actual attributes:
<img src="hero.jpg" width="1600" height="900" alt="…" />
If both attributes are present, the browser's own stylesheet applies aspect-ratio: 1600 / 900 before a single byte of the image has downloaded, reserving the exact box the picture will need. That's why the advice to "always set width and height on your <img> tags" survived the shift to fully responsive, fluid-width design — it isn't there to size the image (your CSS width: 100% still does that); it's there so the browser can lock in the shape early and never have to shove the rest of the page down once the image arrives. Drop those attributes because "we do responsive layout now" and you've reintroduced the layout jump you were trying to avoid.
When the old hack still earns its keep
There's exactly one place I'd still reach for padding-top over aspect-ratio: generating an embed snippet meant to survive being pasted into someone else's CMS, email template, or AMP page — an environment you don't control and can't assume supports anything past CSS2.1. aspect-ratio has been supported in every evergreen browser since late 2021, which covers essentially everyone reading your own site today. It does not cover the sanitizer on a third-party platform you've never tested against. If you're writing that kind of portable snippet, the padding hack's ugliness is the price of it working literally everywhere. If you're writing CSS for a page you control, that tradeoff doesn't apply to you anymore.
🧠 Test yourself
Think it clicked? Take the 7-question quiz →
Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.
Go find the hack in your own codebase
Grep your stylesheets for padding-top: next to a percentage, or padding-bottom: doing the same job. Odds are you'll find at least one video wrapper or hero image still doing it the old way — and now you know the one-line replacement, plus the one line you'd forget to add if you skipped straight to aspect-ratio on an image.
Did yours squish a photo the first time you tried it? Tell me in the comments — I want to know if I'm the only one who found out the hard way.
🚀 Want more like this? Every guide, playground, and quiz lives on bestpractic.org — open it and sign up free so the next one finds you.
Thanks for reading! Let's stay connected:
- ⭐ GitHub — follow me and star the projects: github.com/parsajiravand
- 💬 Discord — join the frontend best-practices community: discord.gg/d9KRhuAwQ
- 📸 Instagram — frontend best practices, daily: @bestpractice___
Top comments (0)