Facundo Corradini Frontend developer, CSS specialist, best cebador de mates ever.

A closer look at the CSS aspect-ratio property

5 min read 1409

A closer look at the CSS aspect-ratio

Keeping a given aspect ratio in CSS has always been a challenge, so much so that it’s the origin for one of the most well-known memes, the CSS IS AWESOME overflown square. Well, the new aspect-ratio property from the CSS box sizing level 4 module has recently been implemented in all major browsers, allowing us to get rid of old hacks and finally giving us perfect control on aspect ratios for any element."CSS is awesome" with small box bordered around it

The old ways

For years we’ve been relying on a very creative but troublesome hack — the padding-top percentage technique, that gets the job done but requires us to plan everything around it. The trick relies on a very obscure behavior of CSS, where vertical paddings (or technically, block-direction padding) are calculated based on the element’s width instead of its height when declared as a percentage, otherwise, they would go into a redundant loop of getting the element taller and taller. We could use this to keep an aspect ratio, simply applying a padding-top percentage calculated as height / width * 100.

For instance, if we wanted a 1 to 1 aspect ratio (i.e. a square) we would simply declare the element’s padding-top: 100%.

The typical image / video ratios of 4:3 and 16:9 could be achieved with padding-top: 75% (3 / 4 * 100) and padding-top: 56.25% (9 / 16 * 100) respectively.

This worked great for creating empty containers that we could use for decorative purposes. If we wanted to have a container with a background image that adjusted to the container size and kept aspect ratio, it would be enough to declare:

.block-element{
  width: 100%;
  padding-top: 75% /* 4:3 aspect ratio */
  background: url(/img/example.svg);
  background-size:cover; /* prevents cropping */
}

See the Pen
Aspect ratio for background images, old way
by Facundo Corradini (@facundocorradini)
on CodePen.


But things get a little more complicated as soon as we start adding actual content to the box (for instance, a video), as the padding will be pushing our content down. That means we have to rely on abstracting the hack to a container and absolute positioning the content just to make our content ignore the padding. Putting it all together, the typical aspect ratio implementation for a 16:9 video will be:

.video-container{
  position: relative;
  width: 100%;
  padding-top: 56.25%;
}

iframe[src*="https://www.youtube.com"] {
  position: absolute;
  top: 0;
  width: 100%;
  height: 100%;
}

See the Pen
Keeping aspect ratios on videos, old way
by Facundo Corradini (@facundocorradini)
on CodePen.

That’s great for a video and a 100% width container, but what if we wanted to keep an aspect ratio and have the container dynamically sized to its content? That’s where the hack escalates to disproportionate levels. If we used the absolute positioning technique, having too much content would put us at risk of awful overflows. If we wanted the container’s width to be automatically sized to the content, our absolute positioned element wouldn’t be able to make the container grow.

We made a custom demo for .
No really. Click here to check it out.

One solution to both of those is using the padding-top hack in a floated pseudo-element to control the aspect ratio, letting the container grow with our content if needed. The challenge then is getting the pseudo-element to render in a way that stays hidden from the user but still gets actual dimensions to affect the container.

This can be achieved in normal flow with a negative margin matching a 1px width as if we tried to use a zero-width element the browser would ignore it completely:

.container::before {
  content: "";
  float: left;
  padding-top: 75%; /* 4 : 3 ratio*/
  width: 1px;
  margin-left: -1px;
}
.container::after { /* classic clearfix */
  content: "";
  display: table;
  clear: both;
}

See the Pen
Aspect ratio with content, old way
by Facundo Corradini (@facundocorradini)
on CodePen.

In grid layout, we could potentially force the pseudo-element to share the column and row of the actual content:

.element{
  grid-column:1;
  grid-row: 1;
}

.container::before{
  content: "";
  padding-top: 100%; /* 1:1 aspect ratio*/
  grid-column:1; grid-row: 1;
}

See the Pen
Keeping circles circular, old(ish) way
by Facundo Corradini (@facundocorradini)
on CodePen.

The CSS aspect-ratio property

The new, simpler way doesn’t require any of those hacks. It allows us to explicitly declare the desired aspect-ratio and the browser will work its magic to keep it.

A square is simply aspect-ratio: 1 / 1, a 16:9 video gets aspect-ratio: 16 / 9, etc.

As long as at least one of the dimensions is auto, the browser will adjust the box size to keep the given aspect-ratio (if we specify both height and width, aspect-ratio will be ignored).

Better yet, an element with a declared aspect-ratio will try to keep it, but expand to prevent overflows if needed, which saves us from using absolute positioning and the pseudo-element hack.

This allows us to achieve the old tricks in a much simpler fashion but also opens the door to unlimited new possibilities.

aspect-ratio for art direction

One neat trick we can use with the new property is changing an image aspect-ratio in media queries. We may want an image to go 16:9 when in landscape mode, but keep a 1:1 format when it’s in portrait.

Of course, using a multiple image source set with the <picture> element would be the preferred way, but we don’t always have that luxury.

.ratiod-image {
  aspect-ratio: 1/1;
  object-fit: cover;
  width: 75vw;
}

@media (orientation: landscape) {  
  .ratiod-image {
    aspect-ratio: 16/9;
  }
}

See the Pen
Aspect ratio for art direction
by Facundo Corradini (@facundocorradini)
on CodePen.

A neat thing about this is that future implementations will even allow for interpolation of the aspect-ratio property, so it will be able to transition smoothly from one to the other!

Keeping the aspect-ratio of videos without an auxiliary container

One of my favorite applications for the new property is keeping the aspect ratio of embedded videos without having to rely on absolute positioning or the padding-top hack, therefore keeping the markup free of unnecessary containers.

To do so, we may simply declare the width as whatever we want (most likely, 100%), use the aspect-ratio property to keep the relation between width and height and set the height to unset to prevent the height stated in the HTML from taking over:

iframe[src*="https://www.youtube.com"] { /* targets any iframe from YouTube*/
  width: 100%; /* elongates the iframe to the container's width */
  height: unset; /* ignore the height attribute from the HTML */
  aspect-ratio: 16/9; /* adjust the height to keep aspect-ratio*/
}

See the Pen
Aspect-ratio on videos without container or hacks
by Facundo Corradini (@facundocorradini)
on CodePen.

Adjusting grid row height based on dynamic columns width

One of my favorite aspects of CSS grid is the autofit/minmax combo, which allows our grid to have as many columns of a determined minimum width as can fit in the container, but also have them evenly grow to distribute the extra space.

But usually, our rows would keep the predetermined height, turning what started as squares into rectangles. Now we can automatically adjust the row sizes according to the columns’ dynamic width!

We just have to define an aspect-ratio for the grid items and the browser will work its magic:

.grid{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.grid > *{
  aspect-ratio: 1 / 1;
}

See the Pen
Keeping aspect-ratio in a CSS Grid with dynamic-sized columns
by Facundo Corradini (@facundocorradini)
on CodePen.

Solving the old “CSS IS AWESOME” meme

We can argue that the issue in the meme is one of over-definition. The overflow is a design feature of CSS, not a bug, as it prioritizes preventing the loss of content. The supposed author has over-defined the interface, giving it a fixed width and a fixed height, and big letters that simply don’t fit, and that’s why it overflows.

There are several possible ways to fix it — for instance, we could let it grow wider by just changing from a fixed width to a min-content, let it grow taller if we hadn’t defined the height, allowing for scrollbars with overflow: auto or scroll, etc.

But I’ll assume the intention is to keep the square… square, and let the box grow as needed to fit whatever text we throw-in.

Well, with aspect-ratio, that’s easy:

.grid{
  display:grid;
/* minimum width is 120px, max autogrows */
  grid-template-columns: minmax(120px, auto);
/* square aspect ratio */
  aspect-ratio: 1 / 1;
  align-items: center;
}

See the Pen
CSS ASPECT-RATIO IS AWESOME
by Facundo Corradini (@facundocorradini)
on CodePen.

Conclusion

Even though at first glance, aspect-ratio can look like an alternate spelling for the old aspect-ratio hack, as we just saw it’s much more than that! It helps us simplify the HTML, get rid of containers, explicitly declare the intentions on the CSS making it easier to maintain, and achieve stuff that was extremely hard or even impossible before. A great new addition to our CSS toolbox!

Is your frontend hogging your users' CPU?

As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web apps — .

Facundo Corradini Frontend developer, CSS specialist, best cebador de mates ever.

Leave a Reply