home file-empty pencil screen user heart reply share social loading

Keith J. Grant

Photo of Keith

Atlanta, GA
United States

Twitter
@keithjgrant
Github
keithjgrant
Author
CSS in Depth

A better approach to CSS aspect ratios

There’s an old hack for creating elements with a fixed aspect ratio that involves using a percentage-based padding. You may be familiar with it. It looks something like this:

.tile {
  height: 0;
  overflow: hidden;
  padding-bottom: 25%;
  background-color: bisque;
}

The element is forced to have no height, then its bottom padding is set to the actual desired height. This produces an element something like this:

4:1 aspect ratio

This works because of a peculiar quirk of padding: Any padding specified in percent computes to a percentage of the element’s width—even if it is a top or bottom padding. (The same is true for margin as well.) This is a bit counter-intuitive, but it comes in handy. I think the original reasoning was so you could declare something like padding: 5% and get an equal padding on all four sides of the element, regardless of its shape.

Of course, this approach has a problem: overflow is cut off:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam laoreet tellus ut erat egestas vestibulum. Aliquam erat volutpat. Fusce ut nibh quis lectus fermentum aliquet. Suspendisse potenti.

This approach creates fixed height of the element, which in CSS is an anti-pattern. For this reason, I’ve always felt dirty using it.

Dealing with overflow

A while back, I stumbled across an approach that is similar, but prevents the overflow problem. Instead of setting our element’s height, we can set the height on a floated ::before pseudo-element. Watch what happens when we do this:

.tile {
  background-color: darkseagreen;
}
.tile::before {
  content: "";
  float: left;
  padding-bottom: 25%;
}
.tile::after {
  clear: left;
  content: " ";
  display: table;
}

The ::after is just a familiar clearfix. This produces a very similar result:

4:1 aspect ratio

However, the element will still grow to contain overflow if necessary:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam laoreet tellus ut erat egestas vestibulum. Aliquam erat volutpat. Fusce ut nibh quis lectus fermentum aliquet. Suspendisse potenti.

How it works

The trick is, instead of explicitly controlling (and thus limiting) the height of the element, we set a known height on the floated pseudo element. By using a padding-based percentage, this will be a ratio of the element’s width. We float it left and leave it empty, producing a width of zero. Here is the floated element, with a black outline added:

Floated pseudo element provides a min height to the element.

Then, in the ::after pseudo-element, we clear the float, forcing the element to grow to contain the floated ::before. Since the float has a width of 0, it doesn’t interfere with the content layout in any way. And when the content extends below the bottom of the float, the box simply grows naturally to contain it:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam laoreet tellus ut erat egestas vestibulum. Aliquam erat volutpat. Fusce ut nibh quis lectus fermentum aliquet. Suspendisse potenti.
Loading interactions...

Recent posts