Masonry with CSS grid
Tags: css, tutorial, layout
Published: 2020-02-17 11:23:20

A month ago [codepen](https://codepen.io/) published a challenge to create [a photo gallery](https://codepen.io/challenges/2020/january/3). Oh, I know photo gallery, I've made a gallery or two, I thought. So I decided to give it a go.

What you see in the cover photo is [my submission](https://codepen.io/jenning/pen/LYEqgjy) for the challenge. Thanks `thecatapi.com` for the amusing cat photos.

Masonry layout can be unimaginative when it comes to photo gallery ([what's masonry layout?](https://masonry.desandro.com/)), but I had an idea to build it with CSS grid. [CSS grid](https://css-tricks.com/snippets/css/complete-guide-grid/) is powerful and you should pick it up if you haven't already. It allows you to effectively layout a webpage, but using it to build masonry layout can still be considered fairly novel. What's the secret?

There are a few key points that I'll cover:
* square grid layout
* varying item sizes and packing them together
* content that doesn't disrupt the container size

###**1. Square grids**

We need square grids to allow the photos to be laid out in a way that makes sense. The square grids will also need to be responsive where it will take up available space. To do that I applied a [technique to size an element to specific aspect ratio](https://www.geeksforgeeks.org/maintain-the-aspect-ratio-of-a-div-with-css/). The trick is sizing an element vertically with `padding` as opposed to `height`. Percentage `padding`value is calculated based on the element's `width`. i.e. equal % of `width` and `padding-bottom` will give you a square.

We don't have to apply this to every grid item (the children of a grid container). This is only applied to 1 element, the `:before` pseudo element of the grid container, like so:

```css
.container:before {
  content: '';
  height: 0;
  width: 100%;
  padding-bottom: 100%;
}
```

This squared `:before` pseudo element will serve as the blueprint for all other grid item. However, this itself is not enough to generate a square grid layout yet. We will need to couple it with CSS grid's property `grid-auto-rows: 1fr` and `grid-template-columns: repeat(var(--number-of-columns), 1fr)` to form the layout.

* `grid-template-columns: repeat(var(--number-of-columns), 1fr)` to setup the number of columns for your layout
* `grid-auto-rows: 1fr` will force each row to be of equal size.

```css
.container {
  display: grid;
  grid-auto-rows: 1fr;
  grid-template-columns: repeat(6, 1fr);
}
```

With this the grid items will be sized into squares, and will be laid out in 6 columns. This is still not the end of square grids. You'll notice that there is an empty gap at the first grid. That is the `:before` pseudo element taking up space. To seal this up, move **both** the pseudo element and the first grid item to the first block.

```css
.container:before {
  content: '';
  height: 0;
  width: 100%;
  padding-bottom: 100%;
  grid-column: 1 / 1;
  grid-row: 1 / 1;
}

.grid-item:first-child {
  grid-column: 1 / 1;
  grid-row: 1 / 1;
}
```

Now your square grid layout is ready.

###**2. Varying item sizes and packing them together**

To create the varying sizes of a masonry block, we size the grid items using `grid-column: span` and `grid-row: span`. Then we use `:nth-child` to simulate the randomness of a masonry layout. In my pen, I only size items up to 2 blocks, so the code looks like:

```css
/**
  every 3rd item takes up 2 blocks horizontally (2 x 1)
**/
.grid-item:nth-child(3n) {
  grid-row: span 2;
}

/**
  every 4th and 7th item takes up only 1 block (1 x 1),
  to overwrite the previous 3n rule
**/
.grid-item:nth-child(4n),
.grid-item:nth-child(7n) {
  grid-column: span 1;
  grid-row: span 1;
}

/**
  every 5th item takes up 2 blocks vertically (1 x 2),
  also every 15th block will be a big one (2 x 2)
**/
.grid-item:nth-child(5n) {
  grid-column: span 2;
}
```

There are some numbers you can tweak to get the gallery to cut off perfectly, (i.e. column count vs item count), and you can tweak the numbers to get a layout that you like. There are no hard rules here. This happen to be the layout that I'm happy with.

The grid items are now seemingly sized randomly like a masonry, but there are still empty blocks scattered in between, due to the grid item not able to fit in after sizing up.

To fix that we apply `grid-auto-flow: dense` to pack them up. This will force the single block item squeeze into the empty blocks.

Now you'll see a respectable but empty gallery.

###**2. Content that doesn't disrupt the container size**

To maintain the perfectly squared grid item, the content (the photos) cannot be pulling or pushing the container (the grid item itself), as that'll cause the square to collapse at the next row. We can do this by inline-styling the image url with `background-image` property, but that would be terrible for accessibility. So we'll do it the hard way (just kidding), by using the `<img>` tag and styling it with CSS.

First we want the image to not affect the container. To do that we apply `position: absolute` to the image and that will take it out of the document flow. 

```css
.grid-item {
  position: relative;
}

.photo {
  position: absolute;
}
```

Next we want to size the image so that it'll fill up the container of varying sizes. To do this we apply

```css
.photo {
  position: absolute;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
```

Now you get a beautifully filled up photo gallery!

{% codepen https://codepen.io/jenning/pen/LYEqgjy %}

###**Conclusion**

Originally I only wanted to highlight the square grid layout technique, but figured a tutorial to build a full feldged photo gallery with css might be more interesting. There are other designs where I've applied the square grid technique too, where you need responsive squares or circles, i.e. the Olympic logo. Be creative! I hope you'll add this to your arsenal of CSS techniques!

Some related helpful resources:
* [Grid Garden](https://cssgridgarden.com/) for CSS grid practices
* [Every layout](https://every-layout.dev/)
* [Grid examples](https://gridbyexample.com/examples/)
* [Maintaining aspect ratio with CSS](https://www.geeksforgeeks.org/maintain-the-aspect-ratio-of-a-div-with-css/)