CSS Layouts: Maintain Element Proportions with aspect-ratio
Tags: css, webdev, tutorial, frontend
Published: 2023-08-01 16:00:00

* [Introduction](#intro)
* [Banner Example](#banner)
* [iframe Example](#iframe)
* [Images](#images)

## Introduction <a name="intro"></a>
There are times you'll want to keep an object on a web page the same proportion on all devices and screens, and this is possible now with `aspect-ratio`. This property is useful for elements that do not have inherit proportions, such as `<div>` and `<iframe>`.

I've mostly used `aspect-ratio` for decorative or elements with minimal text. Be careful about using it for items with long passages of text, because you have to consider what happens when text overflows. And of course if you're trying to fit it within a layout to align with other components, then you're better off using properties like grid or flex.

## Banner Example <a name="banner"></a>
Here's a simple banner example:

{% codepen https://codepen.io/pursuitofleisure/pen/poQZyWJ %}

```html
<!-- HTML -->
<div class="banner">
  <h1>Welcome to the Movies</h1>
  <div class="square-one"></div>
  <div class="square-two"></div>
</div>
```

The banner uses `aspect-ratio` to keep it the same proportion (21:9) on every screen size. The aspect-ratio uses `width / height` as values (height default value is `1` if not declared). The banner has two decorative elements that also utilize `aspect-ratio` to keep the shapes and sizings consistent. These items don't necessarily have to keep the same proportions (they may change for artistic reasons depending on the viewport), but it's easy to do so now with `aspect-ratio` and not need to set explicit heights per viewport.

```css
/* CSS */
.banner {
  position: relative;
  max-width: 1000px;
  width: 100%;
  margin: 1em auto;
  aspect-ratio: 21 / 9; /* Setting aspect-ratio */
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #00171f;
  overflow: hidden;
}

.square-one {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 30%;
  aspect-ratio: 5 / 4; /* Setting aspect-ratio */
  background-color: #007ea7;
}

.square-two {
  position: absolute;
  top: 0;
  right: 0;
  width: 80%;
  aspect-ratio: 7; /* Setting aspect-ratio */
  background-color: #003459;
}
```

The two "squares" are decorative background shapes that keep both a consistent percentage width of the banner, and a consistent proportion. Resize the Codepen window and you can see that the banner and the decorative elements inside maintain the same proportions on all screens.

You can see how simple it is to just set the `aspect-ratio` value once and not have to worry about it. In the past I've had to use multiple media queries for banners with background images because there is no inherent size with them, and relying on changing the vertical padding or font sizes to keep the same ratio was tedious and never an exact match.

## iframe Example <a name="iframe"></a>

For an `<iframe>` such as an embedded YouTube video, making it responsive while keeping the original proportion is much easier than it used to be.

Clicking on the YouTube "share" button will give you a code snippet like so:

```html
<!-- HTML -->
  <iframe 
    width="560" 
    height="315" 
    src=...>
  </iframe>
```

We can use the width and height properties as the `aspect-ratio` and this will allow the video to be responsive keeping its original proportion:

```css
/* CSS */
iframe {
  aspect-ratio: 560 / 315; /* width and height of video */
  max-width: 100%;
  height: auto;
}
```

And that's it! Previously you had to `position: absolute` the `<iframe>` in a container, and set its `padding-bottom: 56.25%` which is the video height divided by the width (315 / 560). I used to just copy this block over and over without thinking exactly what it was doing just because I knew it would work:

```css
/* Old CSS method */
.container {
  position: relative;
  padding-bottom: 56.25%;
}

iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
```

This code will still work, and might still be needed if you're using a non-evergreen browser, but otherwise `aspect-ratio` is [highly supported now](https://caniuse.com/?search=aspect-ratio).

If you only style the `<iframe>` without setting `aspect-ratio`, or without using the old container method, and you only set the `width` or `max-width` property on the `<iframe>`, you'll wind up with a more square-ish shape as the screen gets smaller because the height will not adjust automatically. The video will still play in the correct aspect-ratio, but you will get really thick letter-boxing on the top and bottom which will make it not look like a video while paused.

## Images <a name="images"></a>
For images, the proportion is inherently built in with the `width` and `height` attributes (which you should always specify), so you don't need `aspect-ratio`.

```html
<img 
  src="./image.jpg" 
  alt="" 
  width="300" 
  height="200"> 
```

The `width` and `height` are treated like an aspect-ratio that will keep the image the same proportion of 300 pixels wide by 200 pixels tall no matter how big or small the image gets. This also helps the browser determine how much space is needed before the image fully loads so you reduce any jarring content shifting AKA "cumulative layout shift" that affects site speed. So you _probably_ won't need `aspect-ratio` for images, however it _can_ be used in combination with `width` and `height` that you can read about [here](https://css-tricks.com/almanac/properties/a/aspect-ratio/).