CSS Grid - Cards Layout & Aspect Ratio

Dec 30, 20193mins read

Card-based layouts have been around in web design for longer than you may think and grown in popularity over the years. A lot of platforms have been using card based layouts for years now. Cue meme

Alt Text

There are many ways to implement card based layouts using flexbox, css grid or good ol' float☠. But we'll be using CSS Grid to make your life easy as a front-end developer.

Markup

Let's use un-ordered list for our card based layout but you can also use div as well.

<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>

Styles

Our goal is to make a layout that is structured in a grid of cards. Let's introduce CSS Grid to the mix. We'll have to define container ul as grid based container to do that we'll be adding the display property to it and setting it to the value of grid.

ul {
display: grid;
}

With CSS Grid you can set how many columns you want in the layout using property grid-template-columns.

ul {
grid-template-columns: 1fr 1fr 1fr;
}

This will create a 3 column layout, here fr represents a fraction of the total width the container spans. We can also use values in px, em, rem and percentages instead of fr. We set each column to 1fr and repeating it 3 times creates 3 columns for us but in case where you want to build equal width column layouts you can use repeat() to avoid repeating yourself😉

ul {
grid-template-columns: repeat(3, 1fr);
}

Replace the first argument that repeat takes with number of columns you want. Second argument is the width each column will have. Now let's introduce some gutter in case you want some. To do that we can use grid-gap property.

ul {
grid-gap: 16px;
}

This will add gutters of 16px wide to both columns and rows. To set different gutter to columns and rows you can either use grid-row-gap and grid-column-gap or set grid-gap to <grid-row-gap> <grid-column-gap> to assign different gutter sizes.

ul {
grid-gap: 16px 12px;
}

or

ul {
grid-row-gap: 16px;
grid-column-gap: 12px;
}

But what if you want the layout to have as many number of columns that can fit the window and wrap on browser resize?

Let's welcome auto-fill and auto-fit to the game.

What's auto-fill?

Alt Text

auto-fill will simply fill the row with as many cards it can and will create empty columns if the number of cards are less than the number of columns.

ul {
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr);
}

minmax() takes two arguments - 1. First - Minimum Width 2. Second - Maximum Width

The cards will expand as much as maximum width they have in a row until the space for new card becomes available in the row.

What's auto-fit

Alt Text

auto-fit will fill the row with as many cards as it can fit with maximum width. If a row has space to fill 10 cards of maximum width of 100px, it will fit all of them and the cards will wrap to new row if the row doesn't have space to fit a card with it's minimum width.

For more detailed difference between auto-fill and auto-fit refer to this article by @SaraSoueidan

Now for the purpose of this articles we'll be using auto-fill. With this included our css for the container has following properties.

ul {
display: grid;
grid-gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr);
}

Now let's style our cards a bit. We'll begin by adding background color to the body tag and some padding too.

body {
padding: 16px;
background: #19191C;
}

For our cards represented by li tags, let's style 'em.

li {
height: 240px;
background: #303035;
border-radius: 8px;
}

Our layout so far look's like this.

Alt Text

Aspect Ratio

On some screens the layout might look good with fixed height but as the screen size increases or for wide devices the cards will stretch horizontally but the height will be not. To avoid this we can setup aspect ratio for our cards that'll keep them in same ratio no matter how wide or how small a screen is. Now to simulate aspect ratio on our cards we can set the height property to auto or simply remove it.

We'll be using padding-top or padding-bottom to simulate a particular aspect ratio. Below are the values that we can use to achieve different aspect ratios.

  • 1:1 - 100%
  • 16:9 - 56.25%
  • 4:3 - 75%
  • 3:2 - 66.66%
  • 8:5 - 62.5%

Let's make our cards fit to 16:9 ratio.

li {
padding-top: 56.25%;
background: #303035;
border-radius: 8px;
}

With this added we'll have our cards at 16:9 ratio no matter the size of the screen. But any content within the cards would now have been pushed down by the padding we added. To solve this we can wrap the content of the cards into a container and set it to position: absolute. With this we can position our content at the top of the card.

li {
padding-top: 56.25%;
background: #303035;
border-radius: 8px;
position: relative;
}
li > img {
position: absolute;
width: 100%;
}

All styles together

body {
padding: 16px;
background: #19191C;
}
ul {
display: grid;
grid-gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr);
}
li {
padding-top: 56.25%;
background: #303035;
border-radius: 8px;
position: relative;
}
li > img {
position: absolute;
width: 100%;
}

CSS Grid pretty much handles the responsive part for us which I see as absolute win but since we set minimum width for our cards to 320px which means if the window's size is less than that the cards will overflow. So we'll have to use media queries nonetheless🤦🏼‍♂️

@media screen and (max-width: 320px) {
ul {
grid-template-columns: repeat(auto-fill, minmax(100%, 1fr));
}
}

Codepen