CSS Grid - Cards Layout & Aspect Ratio
Tags: css, grid, cards, layout
Published: 2019-12-30 13:04:57

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](https://thepracticaldev.s3.amazonaws.com/i/e3npcpyfttoe0ft5dcam.jpg)

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.

```html
<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`.

```css
ul {
   display: grid;
}
```

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

```css
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😉

```css
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.

```css
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.

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

or

```css
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](https://thepracticaldev.s3.amazonaws.com/i/r0def5tuyc7rdejoejlx.gif)

`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.

```css
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](https://thepracticaldev.s3.amazonaws.com/i/pcrfy3lio1e0lrupirnt.gif)

`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](https://css-tricks.com/auto-sizing-columns-css-grid-auto-fill-vs-auto-fit/) by [@SaraSoueidan](https://twitter.com/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.

```css
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.

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

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

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

Our layout so far look's like this.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/gqohrhhaq1hp60v6rx7l.png)


### 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:1 - 100%
2. 16:9 - 56.25%
3. 4:3 - 75%
4. 3:2 - 66.66%
5. 8:5 - 62.5%

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

```css
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.


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

li > img {
  position: absolute;
  width: 100%;
}
```

### All styles together

```css
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🤦🏼‍♂️

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

## Codepen

{% codepen https://codepen.io/prvnbist/pen/povrxBa %}