One of the hurdles I faced when beginning to work with responsive designs was the lack of an obvious way to assign an aspect ratio to a container in CSS. This kind of thing can be handled with an image placeholder or Javascript, but neither is an elegant solution. Fortunately, with a little bit of digging, an answer soon presented itself!
The Box Model
CSS gives us an easy way to give an element a "responsive" width declaration relative to the browser window size or other containing parent element by simply using a percentage. What we don't have is a way to simply declare a responsive height relative to width, maintaining an aspect ratio. The solution I found was a lot simpler than expected and came from examining the W3C's box model recommendations.
Padding to the Rescue
The answer lies in the box model's padding specs, of all places. We find that when declaring percentages instead of fixed values for padding, the percentage is calculated based on the WIDTH of the element in question, even if we are declaring a vertical value such as padding-top or padding-bottom. To take advantage of this for the purpose of maintaing our height relative to width, all we have to do is a little math!
Let's look at the CSS:
figure {
width: 36%;
margin: 8px auto;
}
div.stretchy-wrapper {
width: 100%;
padding-bottom: 56.25%; /* 16:9 */
position: relative;
background: black;
}
div.stretchy-wrapper > div {
position: absolute;
top: 0; bottom: 0; left: 0; right: 0;
color: white;
font-size: 24px;
text-align: center;
}
And the HTML:
<figure>
<div class="stretchy-wrapper">
<div>Relative Aspect Ratio! Try resizing the browser.</div>
</div>
</figure>
As you can see in the CSS, all we have to do is nest an element with 100% width inside a "responsive" percentage-based-width parent element, and then declare a % for bottom or top padding based on the ratio we want to maintain. To calculate the percentage needed for any aspect ratio we can use the following formula:
B / (A / 100) = C%
So for 16:9 (where 16 is A and 9 is B):
9 / .16 = 56.25 (%)
And that's all there is to it!
Some Other Common Ratios
For the not so mathematically inclined, here are some of the percentages for common aspect ratios:
75% = 4:3
66.66% = 3:2
62.5% = 8:5
Have fun and happy coding!
this is great idea thanks
You Rock! THX
Wow, I’ve been looking for this! Thanks!
I’m using this to style an image (background: url();) but in Safari and on ios, it won’t display. any thoughts?
The technique needs to be implemented as shown in the example using a nested element, not applied directly to the image. Start by copying the example above and then work from there!
Hi,
Thanks for this great tip. Just a word though, if you forget to delete “box-sizing: border-box”, adding borders will deform the aspect ratio.
I’m not able to break the example with or without a box-sizing rule, maybe I misunderstood. Can you be more specific?
The problem is that if the parent’s aspect ratio is wider then the aspect ratio of the element we want to preserve the aspect ratio of, it will overflow vertically.
So it is not contained inside the parent element, the same way background-size:contain; would do for a background image for example
In our example the parent element exists solely to facilitate the aspect ratio of it’s child element, so the parent needs to have the % width that is desired for the child… no more, no less!
You my friend, are a genius! Thank you so much!
Thank you so much for taking the time to share this. So much better than the js hacks I’ve been using!
😀
BTW the padding hack doesn’t work with Flex containers on Firefox unfortunately (which is actually following the flex standard – https://stackoverflow.com/questions/23717953/padding-bottom-top-in-flexbox-layout.)
The solution is to use object-fit: cover instead. It’s a recent standard to keep aspect ratio fixed.
Thanks for the object-fit reminder!
Together with this new property you can even use the viewport-width unit for setting a relative height according to the viewport-width.
.container > img {
width: 100%;
object-fit: contain;
height: 40vw; }
Only IE and Edge will need a polyfill or this nice workaround:
https://medium.com/@primozcigler/neat-trick-for-css-object-fit-fallback-on-edge-and-other-browsers-afbc53bbb2c3#.8xxn54b4k
Thank you very much. It is a nice technique.
If I understand correctly, this only works if you change the element’s width. But if you also want to limit the height, it might not work (instead become higher than allowed).
Another way to calculate the ratio is (B*100)/A
75% = 4:3 = (3*100)/3 =300/3 = 75%
66.66% = 3:2 = 200/3 = 66.66%
62.5% = 8:5 = 500/8 = 62.5%
i think this is easy