CSS Grid, maintaining aspect ratio and managing overflow

Donnie D'Amato
Jul 11, 2018 · 5 min read

We’ve recently deprecated IE support for our product and this has opened up a world of new possibilities when it comes to development. While I know CSS Grid had partial support in IE (Microsoft created the original spec), the full implementation is much more magical. Patience is a virtue.

Speaking of changes, we’re in the middle of a product redesign and some of the new looks call for some cards on summary pages. These cards have a few requirements:

  1. The number of cards must act responsively, filling the width of the container in some percentage until hitting a threshold where a greater or fewer amount of cards can fit.
  2. Each card will have a main image on the top, which must maintain a 4:3 aspect ratio no matter the width of the card (this means the width will affect the height).
  3. There should only ever be one row of cards.

So let’s get started!

Responsive cards using CSS Grid

A much better solution would be to use CSS Grid with just a few lines of code:

:root {
--spacing: 24px;
--min-card-width: 250px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--min-card-width), 1fr));
grid-column-gap: var(--spacing);
}

The variables are there so that I can easily tweak the values. This will solve everything in the first requirement nicely.

Aspect ratio using padding percentage

:root {
--ratio-percent: 75%;
}
.aspect-ratio {
padding-top: var(--ratio-percent);
}
.absolute-fill {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

The width of these items is determined by the grid; and I’m setting the padding to a variable at the top of the file just in case we’d need to change from a 4:3 to some other ratio easily. Then each child of the grid item will have a fill on it. Notice I haven’t set position: relative; on the .aspect-ratio class in order for the fill to affect the children properly; we’ll need to do something about that in a bit.

So all of this is good, but we have one final requirement we need to meet.

Hiding overflow using… a lot of stuff

What we really want to do is make the grid container as tall as a single card. However, the size of the grid is based on the number of items within it. Even if we could set the height, the height of the card changes based on the width of the cards, and the width of a card changes based on the width of the container. Yeesh! How do we manage all of this?

First, let me provide some markup. It should give you a bit of a hint.

<section class="grid">
<aside class="aspect-ratio"></aside>
<article>
<ul class="grid absolute-fill">
<li class="aspect-ratio">
<div class="absolute-fill">content 1</div>
</li>
<li class="aspect-ratio">
<div class="absolute-fill">content 2</div>
</li>
<li class="aspect-ratio">
<div class="absolute-fill">content 3</div>
</li>
<li class="aspect-ratio">
<div class="absolute-fill">content 4</div>
</li>
<li class="aspect-ratio">
<div class="absolute-fill">content 5</div>
</li>
</ul>
</article>
</section>

If you look closely, you’ll see there are two uses of the .grid class. One is on a parent container which holds two items: a <aside class="aspect-ratio"/>, and a wrapper for another .grid element. The idea is this:

Both .grid elements will have the same settings from the class above, meaning their children will act just the same under the grid-template-columns rule we set earlier. The <aside class="aspect-ratio"/> element will act like a card but is empty. The purpose of this is to determine the height of its sibling; the <article/> element. We can fill the entire grid with this item by setting grid-row and grid-column to 1 / -1; for both. The <article/> element can then provide a height to its children, specifically the nested <ul class="grid absolute-fill"/> inside of it. This will become the dimensions of the parent, and the height of that dimension was based off of a card.

Adding it all together, including some of the necessary position: relative; rules in the right places, we should get a solid result. You can see the full styles below:

*, *:before, *:after {
box-sizing: border-box;
}
:root {
/* variables */
--spacing: 24px;
--min-card-width: 250px;
--ratio-percent: 75%;
--addl-height: 100px;
}
body {
margin: 0;
}
/* just for the purposes of the demo */
header, footer, section, div {
border: 1px solid blue;
padding: var(--spacing);
}
section {
/* hide all the overflowing cards */
overflow: hidden;
}
article {
grid-row: 1 / -1;
grid-column: 1 / -1;
position: relative;
}
ul {
/* clear ul styles */
list-style: none;
margin: 0;
padding: 0;

/* additional gap */
grid-row-gap: var(--spacing);
}
li {
/* set up aspect ratio hack */
position: relative;
}
.absolute-fill {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--min-card-width), 1fr));
grid-column-gap: var(--spacing);
}
.aspect-ratio {
padding-top: var(--ratio-percent);
}
.aspect-ratio:after {
content:"";
height: var(--addl-height);
display: block;
border-top: 1px solid blue;
}
The final result (with a header and footer)

We’re using the :after element just to show what card content below the card might look like but this could easily be another element. The catch is that the size of that content must be included in the spacer element in the top-level grid (the <aside/> element in this case). That element’s height dictates the height of the grid container, not the content cards themselves.

I’m pretty sure you could use a pseudo element instead of the <aside/>, but I wanted to show the relationship between it and the cards in the example.

You can see this in action here when you resize the browser.

Compass True North

Compass Engineering & Product Blog — An inside glimpse at…

Compass True North

Compass Engineering & Product Blog — An inside glimpse at our technology and tools, brought to you by the engineers of the game-changing real estate platform, Compass. Hiring at https://www.compass.com/careers/

Donnie D'Amato

Written by

brb, Architecting Design Systems

Compass True North

Compass Engineering & Product Blog — An inside glimpse at our technology and tools, brought to you by the engineers of the game-changing real estate platform, Compass. Hiring at https://www.compass.com/careers/

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store