Skip to content
videoembeds

Responsive video embeds without the padding hack

aspect-ratio replaced the padding-bottom trick years ago. What to use now, including for vertical video.

6 minute readAssumes you write code

For about a decade, making a video embed scale meant a wrapper with zero height, 56.25% bottom padding and an absolutely positioned iframe. That trick existed because CSS had no way to say "this box is sixteen by nine". It has had one since 2021. If you are still copying the padding version, you can stop.

Why this was ever hard

An <iframe> is a replaced element with a default size of 300 by 150 pixels and no intrinsic ratio of its own. Give itwidth: 100% and it stays 150 pixels tall. Give it a height in pixels and it stops being responsive. Percentage heights need a parent with a definite height, which the page does not have.

The padding trick worked around that by exploiting a genuine oddity: percentage padding resolves against the container's width, even for top and bottom. So 56.25% of the width is exactly nine sixteenths, and the box gets the right height for the wrong reason.

What to write now

.video {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: 12px;
}

.video iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

aspect-ratio has been supported in every current browser since 2021, which by now covers effectively all real traffic. If you support a browser old enough to lack it, the padding version still works and the two can coexist: set the padding rule first and the aspect-ratio rule second, and anything that understands the latter uses it.

Keep the ratio on the wrapper rather than on the iframe. Space is then reserved before anything loads, so the page does not jump, and it stays reserved if a thumbnail 404s or a player is blocked. That is the whole layout-shift story in one decision.

Why the child is still absolutely positioned

You can put aspect-ratio directly on the iframe and skip the wrapper. It works, right up until you need a second thing in the box: a thumbnail, a play button, a watermark, a control bar. The wrapper is what gives all of those a positioning context, so it is worth having from the start rather than retrofitting.

Vertical video

Shorts, Reels and TikToks are 9:16, and dropping one into a 16:9 box gives you a small letterboxed strip with black bars either side that is nearly unwatchable on a phone. Change the ratio, and cap the width so it does not become a skyscraper on a desktop:

.video--portrait {
  aspect-ratio: 9 / 16;
  max-width: 24rem;
  margin-inline: auto;
}

A useful default is to detect the ratio rather than assume it. Both YouTube and Vimeo return width and height from their oEmbed endpoints, so one request while you are setting the embed up tells you whether the video is landscape, square or portrait, and you can pick the nearest ratio automatically.

When the video does not match the box

You will sometimes want a fixed shape regardless: a square for a feed, or 21:9 for a hero. The video then does not fill it, and what shows in the gap is your choice.

For a <video> element, object-fit decides directly. contain letterboxes and shows everything;cover fills the box and crops. For a platform iframe you have no such control, because the player is doing its own letterboxing on the other side of the boundary. All you can decide is the colour behind it:

.video { background: #0b0b0d; }

Picking a background close to the video's own dark bars makes the seam invisible. Leaving it white makes it obvious.

Thumbnails are not the same ratio as the video

A detail that catches people: YouTube's hqdefault.jpg is 480 by 360, which is 4:3, not 16:9. It contains a 16:9 image with black bars top and bottom. maxresdefault.jpg and sddefault.jpgbehave differently again. So a poster image inside a 16:9 wrapper needsobject-fit: cover, or you get bars inside your bars:

.poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Making controls survive 390 pixels

Most embeds are checked at desktop width and shipped. A control bar that looks balanced at 900 pixels can be unusable at 390, because the elements do not shrink evenly: icons have a minimum sensible size, a timestamp has a fixed character count, and the scrubber is what gets squeezed to nothing.

Three habits that keep it working:

  • Give the scrubber a minimum width and let it be the flexible one. flex: 1; min-width: 56px; means everything else keeps its size and the track takes what is left.
  • Make everything else flex: none. Buttons that shrink turn into unhittable slivers well before they look wrong.
  • Drop what does not fit. At narrow widths, the elapsed and total time is usually the first thing worth hiding, and a volume slider is close behind, since phones have hardware volume anyway.

Scale the bar from one variable rather than sizing each part. If icon size, button size and padding all derive from a single height, the whole bar has one number to tune and cannot fall out of proportion:

.bar { --h: 48px; min-height: var(--h); }
.bar button { width: calc(var(--h) * 0.58); height: calc(var(--h) * 0.58); }
.bar svg { width: calc(var(--h) * 0.29); height: calc(var(--h) * 0.29); }

Sizing to the container, not the viewport

A media query asks how wide the window is. An embed usually cares how wideits column is, which is a different question in a sidebar or a two-column layout. Container queries ask the right one:

.video-wrapper { container-type: inline-size; }

@container (max-width: 30rem) {
  .bar .time { display: none; }
}

This matters for an embed specifically, because the same snippet gets pasted into a full-width article on one site and a 320-pixel sidebar on another, and the viewport tells you nothing about which.

Actually checking it

Narrow the browser to 390 pixels and use the thing. Press play, scrub, change the volume, open fullscreen. Most layout problems at that width are obvious within ten seconds and invisible at any other width, which is why they ship so often. The builder on this site has a mobile toggle above the preview for exactly that reason.

While you are there, check the ratio you chose against the video's real one, and check that the box holds its shape before the thumbnail arrives. Throttle the connection if you need to see it. The relationship between reserved space and layout shift is covered in more depth inhow to embed video without wrecking your page speed.