Aspect Ratios for Grid Items

We've covered Aspect Ratio Boxes before. It involves trickery with padding such that an element's width and height are in proportion to your liking. It's not an ultra-common need, since fixing an element's height is asking for trouble, but it comes up.

One way to lower the risk is The Psuedo Element Tactic, in which a pseudo element pushes its parent element to the aspect ratio, but if the content inside pushes it taller, it will get taller, aspect ratio be damned.

You can use that technique in CSS grid with grid items! Although there are a couple of different ways to apply it that are worth thinking about.

#Remember that grid areas and the element that occupy them aren't necessarily the same size.

We just covered this. That article started as a section in this article but felt important enough to break off into its own concept.

Knowing this, it leads to: do you need the grid area to have an aspect ratio, and the element will stretch within? Or does the element just need an aspect ratio regardless of the grid area it is in?

#Scenario 1) Just the element inside needs to have an aspect ratio.

Cool. This is arguably easier. Make sure the element is 100% as wide as the grid area, then apply a pseudo element to handle the height-stretching aspect ratio.

<div class="grid">
  <div style="--aspect-ratio: 2/1;">2/1</div>
  <div style="--aspect-ratio: 3/1;">3/1</div>
  <div style="--aspect-ratio: 1/1;">1/1</div>
.grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  place-items: start;
.grid > * {
  background: orange;
  width: 100%;
.grid > [style^='--aspect-ratio']::before {
  content: "";
  display: inline-block;
  width: 1px;
  height: 0;
  padding-bottom: calc(100% / (var(--aspect-ratio)));

Which leads to this:


Note that you don't need to apply aspect ratios through custom properties necessarily. You can see where the padding-bottom is doing the heavy lifting and that value could be hard-coded or whatever else.

#Scenario 2) Span as many columns as needed for width

I bet it's more likely that what you are needing is a way to make a, say 2-to-1 aspect ratio element, to actually span two columns, not be trapped in one. Doing this is a lot like what we just did above, but adding in rules to do that column spanning.

[style="--aspect-ratio: 1/1;"] {
  grid-column: span 1;
[style="--aspect-ratio: 2/1;"] {
  grid-column: span 2;
[style="--aspect-ratio: 3/1;"] {
  grid-column: span 3;

If we toss grid-auto-flow: dense; in there too, we can get items with a variety of aspect ratios packing in nicely as they see fit.


Now is a good time to mention there little ways to screw up exact aspect ratios here. The line-height on some text might push a box taller than you want. If you want to use grid-gap, that might throw ratios out of whack. If you need to get super exact with the ratios, you might have more luck hard-coding values.

Doing column spanning also gets tricky if you're in a grid that doesn't have a set number of rows. Perhaps you're doing a repeat/auto-fill thing. You might end up in a scenario with unequal columns that won't be friendly to aspect ratios. Perhaps we can dive into that another time.

#Scenario 3) Force stuff

Grid has a two-dimensional layout capability and, if we like, we could get our aspect ratios just by forcing the grid areas to the height and width we want. For instance, nothing is stopping you from hard-coding heights and widths into columns and rows:

.grid {
  display: grid;
  grid-template-columns: 200px 100px 100px;
  grid-template-rows: 100px 200px 300px;

We normally don't think that way, because we want elements to be flexible and fluid, hence the percentage-based techniques used above for aspect ratios. Still, it's a thing you can do.



This example forces grid areas to be the size they are and the elements stretch to fill, but you could fix the element sizes as well.

#Real world example

Ben Goshow wrote to me trying to pull this off, which is what spurred this:


Part of the issue there was not only getting the boxes to have aspect ratios, but then having alignment ability inside. There are a couple of ways to approach that, but I'd argue the easiest way is nested grids. Make the grid element display: grid; and use the alignment capabilities of that new internal grid.


Note in this demo instead of spanning rows, the elements are explicitly placed (not required, an example alternative layout method).