A modern aspect ratio hack

One of the handiest things the web got this year is the aspect-ratio property. It will certainly make a web developer's life easier once it is available in all major browsers. Until then, most will probably continue to search-on-the-internet™ for "css aspect ratio hack". That's precisely what I did yesterday.

A bit of context first. I had to implement a visualisation of collected loyalty points inside a shape with a specific aspect ratio. The content can vary and so should the size of the box. Anyway, while working on this I discovered a modern alternative to current aspect ratio hacks.

42Points
Representation of the element I had to build. The number can grow.

Now, see how this component behaves with the methods we will discuss in this articles!

The current solutions

The tricks involved in enforcing an aspect ratio are described in this great CSS-tricks article by Chris Coyier. He starts by describing how relative vertical paddings work and how they can be used for aspect ratio hacks. He then describes solutions for different usecases. Among those are two approaches for putting content inside aspect ratio boxes.

Method 1

The first implementation puts the content inside an element with absolute positioning. Your alarm bells might be ringing already. The sibling a pseudo-element which takes care of holding an aspect ratio is not aware of its content and thus cannot resize according to overflow. The demo below demonstrates this problem. On the other hand, position: absolute makes orienting the content easy.

<div class="aspect-ratio-box">
  <div class="aspect-ratio-box__content">
    <p style="white-space: nowrap">This is horizontally overflowing content</p>
    <p>This is vertically overflowing content. Yes, this is indeed a lot of text for a demo.</p>
  </div>
</div>
.aspect-ratio-box {
  position: relative;
  width: 120px;
  border: 3px solid black;
}
.aspect-ratio-box::before {
  content: '';
  display: block;
  padding-bottom: 100%;
}
.aspect-ratio-box__content {
  position: absolute;
  top: 0;
  width: 100%;
}

Method 2

Chris describes another solution that mitigates this problem. Instead of giving the content position: absolute, the sibling floats to the left and is moved outside the box. Another pseudo-element is needed for the good ol' clearfix. We can now use min-content or max-content on the wrapper! This will allow us to contain the previously vertically overflowing text. Also, the box will prevent horizontal overflow as it will increase the height to fit the content. Unfortunately, the width does not scale. Also, it is less convenient to position the content than with the previous method.

<div class="aspect-ratio-box">
  <div class="aspect-ratio-box__content">
    <p style="white-space: nowrap">Indefinitely long text</p>
    <p>This is vertically overflowing content. Now, I even need more text to prove my point. Here is another really long sentence.<br>I am really sorry that you have to read this nonsense.<br>See!</p>
  </div>
</div>
.aspect-ratio-box {
  min-width: 120px;
  width: min-content;
  border: 3px solid black;
}
.aspect-ratio-box::before {
  content: '';
  padding-bottom: 100%;
  float: left;
  width: 1px;
  margin-left: -1px;
}
.aspect-ratio-box::after {
  content: '';
  display: table;
  clear: both;
}
.aspect-ratio-box__content {
  width: 100%;
}

The modern alternative

To break down the previous two methods, they consist of two parts:

  1. achieving the aspect ratio
  2. positioning the content

For the first part, both use a relative padding. Our new solution will work the same way in that regard. However, we will try a different mean of placing the content. The first method relied on position: absolute for the content. The second method positions the content and the aspect ratio element next to each other. Instead of moving the aspect ratio element outside the box, I want to suggest that we place the elements on top of each other. For this purpose, we have a different CSS hack at our disposal: If you place elements in the same column and row of a grid element, the parent will take the height of the largest child. Please see a demonstration of this below.

<div class="parent">
  <div style="height: 75px; background-color: blue"></div>
  <div style="height: 50px; background-color: black"></div>
</div>
.parent {
  display: grid;
  outline: 2px solid hotpink;
} 
.parent > * {
  grid-row: 1;
  grid-column: 1;
}
/* IE 11 support */
.parent {
  display: -ms-grid;
}
.parent > * {
  -ms-grid-row: 1;
  -ms-grid-column: 1;
}

Now, if we combine the padding and CSS Grid techniques we get the same results as in method 2. This time, however, we have all the advantages of CSS Grid.
Want to center the content vertically? → align-self: center
Want to align it to the right? → justify-self: end

<div class="aspect-ratio-box">
  <div class="aspect-ratio-box__content">
    <p style="white-space: nowrap">Indefinitely long text</p>
    <p style="max-width: 120px">This is vertically overflowing content. Now, I even need more text to prove my point. Here another really long sentence.<br>I am really sorry that you have to read this nonsense.<br>See!</p>
  </div>
</div>
.aspect-ratio-box {
  display: grid;
  min-width: 120px;
  width: min-content;
  border: 3px solid black;
}
.aspect-ratio-box::before, .aspect-ratio-box__content {
  grid-row: 1;
  grid-column: 1;
}
.aspect-ratio-box::before {
  content: '';
  padding-bottom: 100%;
}

Conclusion

There is a new aspect ratio hack which feels a bit more modern and should facilitate the waiting on the saviour aspect-ratio. But be warned: CSS Grid might give you more or less street cred than float. Evidence is inconclusive.