When designing, developing and optimizing a responsive website, you will often encounter elements which maintain their aspect ratio while scaling. Images do by default in HTML, but videos, background images, calls to action, ads, and other graphical elements do not.
A developer’s first inclination may be to set the width
and height
of an element to a percentage value. However, because percentages in CSS are calculated based on the dimensions of the equivalent property of the elements parent container (i.e. a percentage based height
will be based on the parent’s height
, width
will be based on width
), decreasing an element’s width will not directly affect its height.
Some may advocate the use of JavaScript to calculate and set the height
of the element on resize
, while others might suggest using images set to visibility: hidden
or data URLs akin to a spacer.gif
. These solutions are inelegant and difficult to implement.
There is, however, a simple elegant, pure CSS
solution to the problem:
.aspect-ratio {
position: relative;
overflow: hidden;
}
.aspect-ratio::after {
content: '';
height: 0;
padding-bottom: 33%; /* 1:3 Ratio - Change this value */
display: block;
}
.aspect-ratio > * {
position: absolute;
}
.aspect-ratio > .cover {
top: 0;
right: 0;
bottom: 0;
left: 0;
}
So, what’s the trick?
The proof is in the padding
. According to the spec, when padding
is defined as a percentage it is calculated based on the “width of the containing block” (i.e. parent container).
This may seem odd at first, but let’s examine the concept for a moment. If you define padding: 10px
for an element, you would correctly assume that the padding would be a uniform 10 pixels around the element. Similarly, if you assign padding: 10%
to an element, you would expect the padding to be uniform. If the top and bottom padding were calculated based on the height or the parent container rather than the width, the vertical padding
would only be equal to the horizontal padding
when the parent container was a perfect square.
Because pseudo elements are children of the element on which they are declared, setting a pseudo element to display: block
will allow it to fill its parent horizontally and will allow it to expand vertically to the height of it’s contents. Assigning a percentage based padding
value to either property on the horizontal axis (top
or bottom
) will create a box which maintains an aspect ratio based on the parent’s width.
Any content within the parent will also contribute to the height of the parent element. To prevent this, the direct descendants the containing block must be absolutely positioned. The .cover
class in the example code above, can be used to allow a direct descendant to fill the container. Other content can be positioned manually, as needed.
Note: Some content – like videos – will require the use of width: 100%
and height: 100%
when covering an element.
Why not margins?
If you read the spec you will know that margins are also calculated based on the width of the containing block, so why shouldn’t we use margins? Margins collapse, padding doesn’t.
Adding Images & Content
Using this trick in combination with CSS viewport units (like vw
) and background-size: cover
allows developers to create completely fluid elements.