Responsive Video Embeds With CSS aspect-ratio
To make an embedded video elastic, give the iframe width: 100%, height: auto and aspect-ratio: 16 / 9. The browser works out the height from the width, so the player scales with its container and never distorts. That is the whole technique, and it works for YouTube, Vimeo, maps, slide decks and anything else that arrives as an <iframe>.
For years this took a wrapper element and a padding hack, because an iframe has no intrinsic shape the way an image does. The aspect-ratio property removed the need for it. Below you get the modern recipe, the cases it needs (fixed maximum width, vertical video, native <video>), the old method so you can recognise and replace it, and the accessibility and performance attributes that belong on every embed.

The modern way: aspect-ratio on the iframe
An iframe is a replaced element with no natural ratio, so by default it is 300 by 150 pixels regardless of the page. Setting an explicit ratio tells the browser what shape the box should be. The one rule to remember is that the ratio only applies when at least one dimension is automatic. If you set both width and height, the ratio is ignored.
.embed {
display: block;
width: 100%;
height: auto; /* required: lets aspect-ratio decide the height */
aspect-ratio: 16 / 9;
border: 0;
}
<iframe
class="embed"
src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
title="Short description of the video"
loading="lazy"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen></iframe>
Replace VIDEO_ID with the ID from the video’s URL. Vimeo, Loom, Wistia and most other hosts work the same way: copy their embed code, then add the class and drop any fixed height you do not need.
MDN lists aspect-ratio as Baseline Widely available, supported in all major browsers since September 2021. You do not need a fallback for current audiences.
Why this beats the old padding trick
The original technique, popularised in the early responsive design era, wrapped the iframe in a container with height: 0 and padding-bottom: 56.25%. Percentage padding is calculated from the container’s width, so the empty box grew in proportion, and the iframe was stretched over it with absolute positioning. It worked everywhere, and you will still find it in old themes and plugins.
/* Legacy: replace this */
.video-container {
position: relative;
padding-bottom: 56.25%;
height: 0;
overflow: hidden;
}
.video-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
It has real costs. It needs an extra element in every embed, the ratio is hidden in an unrelated-looking percentage (56.25 is 9 divided by 16), and changing the ratio means doing arithmetic. The <object> and <embed> selectors from the original version are also dead weight now, since Flash and plugin-based players are gone.
| Concern | Padding hack | aspect-ratio |
|---|---|---|
| Extra wrapper element | Yes | No |
| Ratio readable in CSS | No, a percentage like 56.25% | Yes, 16 / 9 |
| Absolute positioning | Required | Not needed |
| Switch ratio per breakpoint | Change padding and keep wrapper in sync | Change one property |
| Works with video, image, canvas too | Only by wrapping | Same property |
Cap the width without a second wrapper
An elastic video that stretches across a 2000 pixel monitor is rarely what you want. Because the ratio now lives on the iframe itself, you can cap the width on the same element with max-width. The embed shrinks inside narrow containers and stops growing at the cap.
.embed {
display: block;
width: 100%;
max-width: 720px;
height: auto;
aspect-ratio: 16 / 9;
margin-inline: auto; /* centre it when it hits the cap */
}
The old version needed an extra .video-wrapper div for this. If your layout already constrains the column width, as most article templates do, you can skip the cap entirely.
Other shapes: vertical, square and cinematic
The ratio is a plain value, so any format is one line. Short vertical clips, square social embeds and ultra-wide trailers each get their own modifier.
.embed--vertical { aspect-ratio: 9 / 16; max-width: 320px; }
.embed--square { aspect-ratio: 1; }
.embed--cinematic { aspect-ratio: 21 / 9; }
/* Change the shape on small screens */
@media (max-width: 40rem) {
.embed--cinematic { aspect-ratio: 16 / 9; }
}
Match the ratio to the actual video. If a 4:3 video sits in a 16:9 box, the player shows black bars inside the frame. That is not a CSS bug: the iframe only controls the outer box, and the player decides how to letterbox its content.
Native video and images
For the HTML <video> element, the original advice still holds. Videos and images have an intrinsic ratio from their files, so these two lines are enough, and they are a good global default:
video,
img {
max-width: 100%;
height: auto;
}
When you want a video to fill a fixed shape, such as a hero banner that must be 16:9 whatever the file is, combine aspect-ratio with object-fit. MDN notes that object-fit applies to replaced elements like <img> and <video>, and has no effect on <iframe> or <embed>. That is why third-party players keep their own letterboxing while your own files can crop.
.hero-video {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover; /* crop to fill; use contain to letterbox */
object-position: center;
}
Images benefit too. Writing width and height attributes in the HTML, combined with height: auto in CSS, lets the browser reserve the right space before the file arrives. For a placeholder shape on an image that has not loaded yet, aspect-ratio: 3 / 2 auto uses your ratio first and the real file’s ratio afterwards. If you are styling photos too, our CSS image styles tutorial covers borders, shadows and frames that pair well with elastic media.
Attributes every embed should have
Sizing is only half of a good embed. Four attributes keep it accessible and cheap to load. Browser-level lazy loading for iframes is supported in all the major browsers, according to web.dev, and the iframe element itself is Baseline Widely available on MDN.
title. MDN advises giving every iframe a concise title describing its content. Without one, a screen reader user has to enter the frame to find out what it is, which is confusing on pages with several embeds.loading="lazy". This defers fetching the iframe until it is near the viewport. Use it for any embed below the fold, and leave it off for a video that is the first thing on the page. It only takes effect when JavaScript is enabled.allow. A Permissions Policy for the frame. Grant only the features the player needs. The olderallowfullscreenattribute still works and is defined as the equivalent ofallow="fullscreen *".referrerpolicy. The default isstrict-origin-when-cross-origin, which is a sensible choice. Setno-referrerif you do not want the host to see where the embed was viewed.
Drop frameborder="0" from old snippets. It is deprecated, and border: 0 in your CSS does the same job.
A drop-in component for your own site
Put the styles in one place and reuse them. This is the complete version I would paste into a stylesheet: a base class plus three shape modifiers, with a subtle rounded corner and a dark placeholder colour that shows while the player loads.
.embed {
display: block;
width: 100%;
max-width: 100%;
height: auto;
aspect-ratio: 16 / 9;
border: 0;
border-radius: 0.75rem;
background: #111; /* visible while the player loads */
}
.embed--vertical { aspect-ratio: 9 / 16; max-width: 320px; }
.embed--square { aspect-ratio: 1; }
.embed--cinematic { aspect-ratio: 21 / 9; }
On phones the same rules reflow without any media query: the embed fills the column and keeps its shape.

Load the player only when asked: facades
A YouTube or Vimeo iframe pulls in a full player and several scripts before anyone presses play. The usual fix for pages with video is a click-to-load facade: show a poster image and a play button, and swap in the real iframe on click. The page then ships one image instead of a player. The lite-youtube-embed project packages this for YouTube, and web.dev points to it as a lighter alternative to the standard embed. A hand-rolled version is short, and it keeps the same ratio so nothing shifts when the swap happens.
<button class="facade" type="button" data-src="https://www.youtube-nocookie.com/embed/VIDEO_ID?autoplay=1"
data-title="Short description of the video" aria-label="Play video: Short description of the video">
<img src="poster.jpg" width="1280" height="720" alt="" loading="lazy">
</button>
.facade { display: block; width: 100%; aspect-ratio: 16 / 9; padding: 0; border: 0; cursor: pointer; }
.facade img { width: 100%; height: 100%; object-fit: cover; }
document.querySelectorAll('.facade').forEach((button) => {
button.addEventListener('click', () => {
const frame = document.createElement('iframe');
frame.className = 'embed';
frame.src = button.dataset.src;
frame.title = button.dataset.title;
frame.allow = 'autoplay; encrypted-media; picture-in-picture';
frame.allowFullscreen = true;
button.replaceWith(frame);
frame.focus();
});
});
Use the facade for any video below the fold or any page with more than one embed. For a single hero video, a plain iframe is simpler.
What youtube-nocookie.com does
The examples above use youtube-nocookie.com instead of youtube.com. That is YouTube’s Privacy Enhanced Mode. Per YouTube Help, it keeps views of embedded videos from influencing the viewer’s recommendations on YouTube, and any ads shown are non-personalised. You switch it on by changing the domain in the embed URL. It is available only for embedded players on websites. It is a sensible default, but it is not a substitute for a consent notice where the law requires one.
Troubleshooting
- The ratio is ignored and the iframe has the wrong height. A
heightattribute or an inline height style is overriding the ratio. Setheight: autoin your CSS: it beats the presentationalheightattribute, though an inline style still needs removing or!important. An iframe with no height at all falls back to MDN’s default of 150 pixels. - Black bars appear inside the player. Your ratio does not match the video. Check its real dimensions and update the ratio.
- The page still jumps when the embed appears. That usually means a script injects the iframe into an empty container after load, so nothing reserved the space. Give the container the same
aspect-ratioas the iframe, as the facade below does. - A plugin wraps your iframe with the old padding hack. Remove the wrapper, or leave it alone, since it still works. Do not stack both techniques on the same element.
Where to go next
Elastic media is one piece of a responsive layout. For the surrounding structure, read Responsive Design in 3 Steps and 5 Useful CSS Tricks for Responsive Design. If you are deciding when a layout should change shape, Setting Breakpoints in Responsive Design explains how to choose breakpoints from your content rather than from device widths. To lay several videos or photos out as a grid, see Responsive Column Layouts, and for a ready-made gallery with a lightbox, try the Bootstrap 5 photo gallery with modal lightbox snippet.
Reference pages for the properties used here: aspect-ratio, object-fit and the iframe element on MDN.
Wow. I changed our layout mainly because of this problem and use scale now for the responsiveness. But this solution is awesome! Thanks for sharing.
Hi,
tnx for the post. I was wondering how you could test all these devices fast. Do you have any tooling for this perhaps?
Cool trick for 100% width videos, thanks for sharing! :)
A very awesome simple theme! and appreciate the workup of how to do this. great!
Amazing! Great simple solution to a problem I’m sure loads of people have. Can’t count the number of times I’ve seen Youtube videos escape their enclosing boundaries. Props.
Thanks, good idea. And certainly something I’ll built in my own base theme.
ToM Hermans.
This is just what I’ve been looking for. It works almost perfect too. I noticed something I wonder if is possible to fix though. With your code here, YouTube videos embed perfectly edge to edge while Vimeo videos get’s a back bar added to the top and bottom. Likewise, if I set the padding to .video-container { padding-top: 0; } Vimeo videos embed perfectly edge to edge while YouTube videos gets a black bar added to the left and right.
Is it possible to make both Vimeo and YouTube videos have a perfect edge?
Hi, Did you find the solution for this? i’m having the exact same issue, would be good to know if there is a work around
This is phenomenal and precisely what I was looking for. Thank you so much for sharing! I’ve been looking for a way to keep YouTube videos under control ;)
Hey great post..
This should make all user’s life a lot easier from now on. Thanks a lot for posting the project files
Its now easier to understand Elastic CSS better…Thank You
Awesome technic! But I didn’t understand the meaning of that value for padding-bottom (56.25%?). Could you better explain that please?
it’s the 16:9 aspect-ratio in percentage (9/16*100).
you might want to define classes for this and for other ratios, e.g.:
figure.ar169 { padding-bottom: 56.25%; }
figure.ar43 { padding-bottom: 75%; }
figure.ar11 { padding-bottom: 100%; }
then you can allow the user to change the class value by a js button or whatever…
(anyway, i use the html5 figure element as the container.)
you can remove the “padding-top: 30px;” part from the css if you don’t want fixed control bar for youtube videos. just add the parameter after the video url: ?autohide=1 :) (and there’s really no need to add extra 30 pixels in case of the vimeo player.)
Excellent. I was looking for a solution for a website based on the 1080p (1920×1080) resolution. The videos work awesome! Thank you for sharing.
perfect tips. i tried and its work. thank you very much.
Ahhhh, I didn’t see what was special about this until I read what you mentioned about the padding. Very creative.
Yes, max-width trick is really helpful. I want to create a page at Web tips and tricks to centralize these techniques.
Thank you.
Say, did you manage to teach WordPress to wrap oembeds into a div automatically? If so – how the heck? :)
One thing I’d recommend doing is ensuring that your videos are being set to the proper aspect ratio. There is no guarantee that they will be in 16:9.
height / width * 100 = padding-bottom
I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts.
I have had an issue with this for ages, especially now as I have just created a site purely built on video content. The ideas in this will come in handy big time, thanks! I may even share my site with you to see how your fix may have worked.
Thanks for this information. I had the same issue with a site that had a lot ofvideo content. My problem is now solved.
Thanks
Ian
Neat trick… :) Could you explain the reason behind 50%/60% padding (as opposed to any other padding value)?
Hi,
Thanks.
Are your talking about 56.25% padding value for padding-bottom.
This is just i copied the code from your example. No Brainer… :D
.video-container {
position: relative;
padding-bottom: 56.25%;
padding-top: 30px;
height: 0;
overflow: hidden;
}
Hi,
Thanks.
Are your talking about 56.25% padding value for padding-bottom.
This is just i copied the code from your example. No Brainer… :D
.video-container {
position: relative;
padding-bottom: 56.25%;
padding-top: 30px;
height: 0;
overflow: hidden;
}
I’ve been designing web sites for over 15 years but I haven’t really done much HTML work as of late. More importantly I haven’t done anything in HTML 5 since most everything I do now is databases and scripting. I’m going to have to look more into HTML5 as it looks like it has a lot more oompf than HTML4 had. The video trick is pretty neat.
There’s no doubt that this is certainly essentially the most vital information personally. And i am glad reading your article. But should remark on few general things, Your website style is perfect, the articles is very excellent : D. Good job, cheers
I’ve been building sites for about 2 1/2 years now. This site is on a short list of web design blogs I reference continually. Embedding YouTube videos into a responsive design was something I had to deal with recently on my hobby site, yesterday actually. I plan on going home and implementing this solution immediately. Thanks!
Thank you for this and all the other work you share.
I am working on a new responsive design and found this very helpful. In my situation I am using BuddyPress and a plugin called BuddyPress Activity Plus. I am relatively new to web design so I am not technical enough to edit the plugin.
After playing with it for a little while I found that just using the a max-height and max-width it seemed to do the job. All I added to my css was:
.activity-inner iframe,
.activity-inner object,
.activity-inner embed {
max-height: 100%;
max-width: 98%;
}
Like I said, I am pretty new to this, so I am not sure how cross browser compatible it is but I think it will serve my purposes.
Any thoughts on if this will work across the board?
The CSS design always has helped millions of people on the earth and i am also one of them, Thanks for the great design and great comments to it.
This is really a very vital information about HTML coding which helped me a lot in completing my project based on this codes!
This worked great, had a few work arounds to make it work in my situation, but thanks for this share!!
Did you find a good solution for JWPlayer? I’m having issues with Youtube through JW on an adaptive site.
Cool, thanks very much for the fix. Seems silly that YouTube doesn’t have an option for filling in the auto height (if height is not supplied). It’s already a bit complicated for general users so this cuts down on problems with the automation/integration process.
It works great for IE and Chrome but it doesn’t work for FF 11. Do you have an idea how to get this in FF done?
You may have found this tip at tjkdesign.com, but you rock for bringing it to the rest of the world! Your site came up first in Google (and a very nice site btw).
Passing a small # shortcode [video] to # wordpress for all his videos responsive with the # css link above.
In the functions.php file add:
caption_shortcode function ($ atts, $ content = null) {
return ”. $ content. ”;
}
add_shortcode (‘video’, ‘caption_shortcode’);
And after in the articles or posts surrounded the code from youtube without width = “#” height = “#”.
example
[video] [/ video]
I was trying using the method but when I put float left, it failed miserably.
Sorry but never mind, fixed the issue by making another wrapper on it. You can’t float the video container directly.
Thanks for the awesome tip!!!
Extra video-container made it work like crazy!!! :D
Awesome Tip!! This will really help with a responsive theme I am working on.
If you could help with this problem, that would be great! I’m trying to set a div with a border background that will auto height itself with added content. Trouble is, the content has absolute divs in there, which just don’t obey auto height – they are a rule unto themselves. How would I code a div that will auto adjust? Any help appreciated.
Videos are good ways for connecting with your audiences and readers of your website are getting chance to see you in action.So videos must be used in website
Excellent post. I’ve been working with responsive design for the past couple of months and stretching my wings a bit. I appreciate how concisely you spelled all this out. Wonder if you know of a responsive audio player that can be duplicated on same page (I’ve only found one using jS that allows just one music clip per page)?
Hi,
I’m currently viewing the demo page with Firefox 13 and I cannot resize the width of the Firefox window.
Seems like a Firefox issue to me as it works everywhere else (Chrome, Safari, Opera, IE), and there is also a bug report about it: https://bugzilla.mozilla.org/show_bug.cgi?id=765788
Just wanted to let you know ;)
Thanks for your work
This is great. Has saved me so much work as I am new to responsive design but the more I do, the more I like it!
Thanks! My client keeps mentioning a .css movie that can be used in ipad and other mobile devices and they really had me confused.
great post, thanks. nevertheless, there seems to be a problem with Firefox 13. i cannot resize the window’s width, it “jumps” automatically to the previous size. i think this issue has been introduced with FF13… anyone found a solution for that?
I always wrap my iframe or embedded code in a div that I stylize/size separately, I will have to try out this trick. I don’t use much video, but I am sure it will come in handy for other things as well. Thanks.
evertheless, there seems to be a problem with Firefox 13. i cannot resize the window’s width, it “jumps” automatically to the previous size. i think this issue has been introduced with FF13… anyone found a solution for that?
Nice! I would like to use that trick to embed a Vimeo video on Google Sites. Any chance to get this work?
thanks. nevertheless, there seems to be a problem with Firefox 13. i cannot resize the window’s width, it “jumps” automatically to the previous size. i think this issue has been introduced with
This is a nice article. I found a problem when a mobile device is rotated from portrait to landscape.
The youtube video restarts because of the resized elements. It’s quite annoying and I’m not sure how to stop the video from restarting.
Any hints would be appreciated.
I have a shape 5 template up, installed fitvids, can’t make it work could you would you share HTML code to insert into module, I would prefer embedding video if that is possible.
The site is not live so I can’t share without providing log in HELP!!!!!!!!!!!!!!
Thank you so much for this code! Works perfectly!
Thank, exactly what i was after and works great!
Since upgrading my iPad to iOS 6 this fix doesn’t seem to work any more, I think it’s height: auto causing the issue. Anyone experienced similar and have a fix?
Hi,
In case any of you are trying to use 4:3 video in an iframe like youtube adjust the css to this:
.video-container {
height: 0;
overflow: hidden;
padding-bottom: 75%;
padding-top: 0;
position: relative;
}
Simply wish to say your article is as astonishing.
The clarity in your post is simply excellent and i could assume you’re an expert on this subject.
Fine with your permission allow me to grab your feed to keep uodated with forthcoming post.
Thanks a million and please keep up the enjoyable work.