This article from July 2013 describes a method of using psuedo elements to maintain an elements aspect ratio, even as it scales.
Here’s a Sass mixin that simplifies the math a bit.
@mixin aspect-ratio($width, $height) {
position: relative;
&:before {
display: block;
content: "";
width: 100%;
padding-top: ($height / $width) * 100%;
}
> .content {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
}
The mixin assumes you’ll be nesting an element with the class of content inside your initial block. Like this:
<div class="sixteen-nine">
<div class="content">
insert content here
this will maintain a 16:9 aspect ratio
</div>
</div>
Using the mixin is as easy as:
.sixteen-nine {
@include aspect-ratio(16, 9);
}
Result:
.sixteen-nine {
position: relative;
}
.sixteen-nine:before {
display: block;
content: "";
width: 100%;
padding-top: 56.25%;
}
.sixteen-nine > .content {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
Demo
Here’s a demo showing the above code in action. The animation is added to show the element maintaining the assigned aspect ratio as it resizes.
See the Pen Maintain Aspect Ratio Demo by Sean Dempsey (@seanseansean) on CodePen.
Thanks to Sean Dempsey (@thatseandempsey) for this one!
This technique can be simplified into something more generic by making use of data-attributes and generalizing the child selector.
It’s a nice technique. The only issue is that if the content of the box expands for some reason, the height doesn’t adjust. So if the box is filled with a few more words than what fits, or a user increases the text size, the text might overflow out of the box. Would be nice to have the best of both worlds: maintain aspect ratio if possible, but also expand if needed.
Hi Matt,
You can do a “Maintain aspect ratio or fit to content” version like this:
…and kill the wrapper :-)
pen: http://codepen.io/jakob-e/pen/LEdWNB
Hi Jakob,
That is a very clean solution! Very nice, thanks
Jakob’s solution works on compilation time, which means if the ratio is independant from screen size. But that’s still no true responsive aspect-ratio, allowing the stretch to work if the height is the limiting dimension.
Any other solution to suggest maybe?
Hi Jakob, nice work on this approach. I had been using it and came across an issue in FF today. If the element with the aspect ratio applied has
display:flex;
, it won’t work in FF.http://codepen.io/robertwbradford/pen/gMYZeV
I’ve been trying to find a fix but haven’t been able to…yet.
Robert, I’ve run this issue with flex and Firefox, and one solution I’ve found was to wrap the apect-ratio element with a div, so the flexbox will affect this wrapper instead of the aspect-ratio element itself. That will fix the issue in FF
(BTW: sorry for my english)
I just forked your pen
http://codepen.io/italodr/pen/wzmxxB
As a related aside I wrote this sass mixin for having a css background image maintain it’s aspect ratio.
This allows you to not download/display the image on smaller (mobile) screens, via breakpoints, but have it behave like an html < img > and maintain it’s aspect ratio responsively when you do show it
Could you please tell me how to use this mixin for a div with css background-image ?
@Zolu
div{
@include intrinsic-ratio-bg-img(“/path-to-image/image.jpg”)
}
Is that what you were asking?
Genius! Thanks, this saved me hours of frustration!
This helped me solve an issue with fixing the height of a child element in a bootstrap column to ensure contiguous distribution of and columns in a masonry layout. Thanks much!
A friend showed me this trick and it seems to work well while I was developing in Chrome. But then when I went to test it in Firefox it didn’t work. Any clue why? It seems the the before padding is not being respected.
It turns out that my parent div had the style “display:flex” and I fixed it by changing the style to “display:block”.
this works! much appreciated :)
I love you guys for this fix!! Going crazy over Firefox here :D
This worked great! I’m not a programmer, so I’m putting my site together kind of piecemeal, and adding things I find here and there, and trying to figure them out. This was fully understandable! I used this to stop my slideshow from jumping on the top of the page. Awesome!!
Thank you.
Thora
Very cool! You can also just use css
calc()
to compute that if you are not using a css preprocessor. Working example: http://codepen.io/anon/pen/bBLGWLDon’t forget to resize the viewport!
There’s a quirky behaviour when using this method and applying either a bottom margin or border to the parent element. If it’s as little as say 1 pixel, you’ll see in some cases Safari won’t render the property respectively, resulting in no space or border. Resize your window in Safari to see what I mean.
Demo: http://codepen.io/mjoanisse/pen/zZNOGr/
To get around it, you can use the
::after
pseudo-class and absolutely position the element so that it’s fixed to the bottom of your box. That said, it’s hacky and has it’s limitations.Curious to hear from someone who might have a clever solution to this problem.
I’d like to have a DIV that resizes keeping the ratio inaltered also when the height of the container changes.
Does anybody knows how to do it?
Thank you in advance!
Related CSS tickets:
https://github.com/w3c/csswg-drafts/issues/333
https://github.com/w3c/csswg-drafts/issues/1173
It’s also possible without the before pseudo element. It can be done with calc() and the vw unit instead of the % unit.
Another advantage is that you can use flex-box or grid for example.
Demo:
I made a mixin from the solution of Sérgio Gomes you mentioned here:
So far, this is definitely the best one. It does not require wrapper elements and it expands if necessary. Wonderful!