Thinking

Aspect ratios in CSS

One of the hurdles of responsive web development is presenting content in a way that’s easy to read and digest across all devices from small mobile screens to huge desktop monitors. While text can wrap and layouts can stack; visual media needs to shrink to fit and maintaining the aspect ratios are key to keeping this content readable.

Tools and maps on a worktop

Photo by Fleur

Native aspect ratios

In the majority of cases, your media will take the form of basic image embedding using <img> or <picture> elements. The dimensions of regular embedded image content are read by browsers to automatically scale content with its width and preserving the aspect ratio.

However, to leverage this ability you need to allow the images to scale based on their parent elements which is not the default behaviour. This can be done with very little CSS allowing the image to constrain itself to the width of its parent element and set the height automatically.

img {
  max-width: 100%;
  height: auto;
}

Intrinsic aspect ratio

Sometimes you might find your designs call for a fixed aspect ratio for visual content, or you might be dealing with media that the browser can’t scale for you (like iFrames). This is where manually setting an intrinsic aspect ratio comes in handy.

By taking advantage of the properties of padding in CSS we can create a scaling area to lock our content to. When using a percentage based padding, that percentage is calculated based on the width of the element. So something like padding-top: 50%; would create a padded vertical area equivalent to half the width of the element.

Once you’ve created an area to work with, you can stretch your content within it from edge to edge.

.aspect-ratio-16-9 {
  width: 100%;
  padding-top: 56.25%;
  position: relative;
}

.aspect-ratio-16-9 > * {
  position: absolute;
  inset: 0;
}

Object-fit

As you can imagine, stretching media native elements like images and videos could cause them to warp if they aren’t the exact ratio you’re aiming for. Intrinsic aspect ratio locking works perfectly for media with no native dimensions like iFrames but modern CSS allows us to apply this approach to media with its own aspect ratio.

object-fit is a CSS property that allows you to modify how media inside <img>, <picture> and <video> elements fills the element itself. By default this will stretch to fit causing the tell-tale warping.
Using object-fit: cover; will neatly expand the media content to fill the element from edge to edge without any stretching.

.aspect-ratio-1-1 {
  width: 100%;
  padding-top: 100%;
  position: relative;
}

.aspect-ratio-1-1 > img,
.aspect-ratio-1-1 > video {
  position: absolute;
  inset: 0;
  object-fit: cover; /* This prevent stretching image & video media */
}

Maximising media

All of the methods discussed so far focus on maintaining aspect ratios relative to the width of the content. In the cases where you’re trying to make elements fullscreen this method breaks down as viewports can be any aspect ratio from traditional portrait mobile screens to landscape ultra-wide monitors.

In this case we need to be able to flip between basing the media dimensions on width or height as the viewport aspect ratio changes. By combining calc() and aspect ratio queries you can achieve this effect with very few lines of code:

.full-screen img {
  --percent-of-largest-edge: 80;
  --aspect-ratio-width: 16;
  --aspect-ratio-height: 9;

  height: calc(1vw * (var(--percent-of-largest-edge) * (var(--aspect-ratio-height) / var(--aspect-ratio-width))));
  width: calc(1vw * var(--percent-of-largest-edge));
}
  
@media (min-aspect-ratio: 16/9) {
  
  /* Flip the calculation if the screen is above 16:9 */
  .full-screen img {
    height: calc(1vh * var(--percent-of-largest-edge));
    width: calc(1vh * (var(--percent-of-largest-edge) * (var(--aspect-ratio-width) / var(--aspect-ratio-height))));
  }
}

Try resizing the page and watching the image in the demo below stretch in the screen based on width or height without warping at all:

The future of responsive content

Whilst these methods all involve leveraging the quirks and interactions of some CSS properties, they remain just hacks to cover a long needed feature of CSS.

There is some good news on the horizon as there is some support starting to come through for a built-in aspect-ratio property. This could unlock easy and readable methods to lock aspect ratios in layout.

As of time of writing, this property is only available as an experimental feature in Chrome 84 but hopefully adoption will roll out to other modern browsers over time.

This website uses cookies

This website uses cookies to improve your experience. By using PRISM⁵⁵, you accept our use of cookies.