Should masonry be part of CSS grid?

Currently, the CSS Working Group (CSSWG) is discussing whether to include masonry as part of CSS grid, or as a new layout module?

In this article, I will share my thoughts and examples about whether it should be part of the CSS grid or not.

What is masonry layout?

First of all, a few details in case you are not aware of what masonry is.

A layout type is also called the “waterfall”, where elements with different widths or height stack horizontally or vertically.

Here is an example of vertically stacked elements. This has been known as the “Pinterest” layout for years.

In this article, I will go through examples that don’t look like the Pinterest layout. In particular, examples that might use CSS masonry on demand.

The debate on the syntax

Currently, there is a debate on whether to make masonry part of the CSS grid, or as a separate layout module. You can look at the following blog posts for more info.

The Name

I agree with this webkit article about the name.

In CSS, the names and keywords are simple and easy to read and write. Some options work better in my opinion. For example:

  • waterfall

  • packed

  • collapse (as suggested by the Webkit blog)

  • brick

.masonry {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-template-rows: collapse;
  gap: 1rem;
}

To me, packed or collapse work better.

Imagine if display: flexbox were named display: rubberband, or if CSS display: grid is named display: chessboard. That feels the same to me when I read display: masonry.

My Thoughts on debate

if the new syntax is supported now , how we should use it?

If we go with the new syntax (display: masonry), it will need a few years to become supported in all major browsers. Let’s be honest, a layout is a major part of a web page that makes it hard to apply progressive enhancement. You either have a masonry or not, right?

Making it part of CSS grid will at least guarantee that the layout will work, but without the masonry stuff.

Responsive Design

In practical CSS applications, it’s rare to find a grid like this:

.masonry {
  display: masonry;
  masonry-template-tracks: repeat(3, 1fr);
  masonry-direction: column-reverse;
}

It might be like this:

.masonry {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  padding: 1rem;

  @media (min-width: 700px) {
    display: masonry;
    masonry-template-tracks: repeat(3, 1fr);
    gap: 10px;
    padding: 0;

    &:has(.card-fixed) {
      display: grid;
    }
  }
}

At the end of the day, masonry is a grid. If we go with the new syntax, then changing a layout will require changing the whole layout module (grid, masonry, or flex). This is too much work.

Masonry is a grid

Masonry is a grid, right? Introducing a new layout module would require a significant learning curve to fully understand and memorize new properties.

Fun fact: I still can’t memorize some grid properties or values, let alone get a new layout module for masonry.

Consider the following example. We have a typical grid where all the grid items have the same size.

.layout {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 1rem;
}

If some of the grid items have a larger height than the others, the layout will look like this if we’re using CSS grid. Try to toggle between “Same size” and “Dynamic size”.

At this point, it doesn’t make sense to have a new layout module (i.e.: display: masonry) only because a few items have a larger height (At least for me).

In a real-life case, I might need:

  • have a normal grid on small viewport sizes, then switch to masonry on larger ones.

  • or apply masonry if there are 10+ items in the grid

  • or apply masonry if items have the class dynamic-size

  • and so on.. the options are endless

A masonry layout doesn’t necessarily imply that a layout is always masonry. It can be a mix of flexbox, normal grid, and masonry grid. It might be needed to be applied to an already defined grid.

Try to switch between the options below. Note: the masonry option works in Firefox and Safari Technology Preview at the time of writing this article.

Example of the new vs the grid-integrated syntax

We have a footer layout where sections are displayed next to each other. Here is the design:

When there is no enough space, the footer groups will wrap. This is a default behaviour in CSS grid.

How would you build this layout? Let’s assume that we’ll use a CSS grid.

.footer {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 1rem;
}

With that, the grid works fine. Try to resize it.

Let’s explore enhancing this layout by using masonry.

Grid-integrated option

In this solution, the grid is enhanced by adding masonry as a value for grid-template-rows.

.footer {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  grid-template-rows: masonry;
  gap: 1rem;
}

In the following demo, try:

  • Resizing the layout.

  • Toggle the checkbox to see the difference between the default grid and masonry.

Grid-independent option

If the grid-independent option is used, how will it work? I have a few thoughts:

  • This is a grid by default and I don’t know the exact number of items it will have.

  • Even if I know the number of footer groups, they will need to be arranged differently when the viewport/container width is resized.

  • If I need to change to a new layout module, how I can manage them in the default grid style (i.e: no)

Let’s explore using the grid-independent option. Please note that the following CSS still doesn’t work in any browser.

.footer {
  display: masonry;
  masonry-template-tracks: repeat(auto-fit, minmax(120px, 1fr));
  gap: 1rem;
}

As a developer, if I want masonry to be applied to a grid under certain cases, I will be forced to use display:masonry. For me, that doesn’t make sense because:

  • It will fail in browsers that don’t support display: masonry. I can write fallback, but why write more CSS when we can avoid it upfront by integrating it into the CSS grid?

  • It’s a duplication of the CSS grid syntax.

News Layout

In this example, we have a news section. It contains a featured news item on a large viewport and a grid with 3 columns.

Try to resize the layout and see how it behaves.

Here is the default grid CSS:

.layout {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1rem;
}

.card.featured {
  @container news (min-width: 600px) {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: max-content 1fr;
  }
}

If we want to enable masonry, we’ve two options.

Grid-integrated option

.layout {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  grid-template-rows: masonry;
  gap: 1rem;
}

.card.featured {
  @container news (min-width: 600px) {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: max-content 1fr;
  }
}

Grid-independent option

Take a look:

.layout {
  display: masonry;
  masonry-template-tracks: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1rem;
}

For the featured card, I want to place it from the start to the end column with 1 / -1. According to the css-grid-3 spec:

Item placement in the grid axis of a masonry container is established with the masonry-track-start and masonry-track-end properties (and their masonry-track shorthand), whose syntax and interpretation are analogous to the grid-column-start and grid-column-end properties (and their grid-column shorthand).

That means I need to replace grid-column with masonry-track.

.card.featured {
  @container news (min-width: 600px) {
    masonry-track: 1 / -1;
    display: grid;
    grid-template-columns: max-content 1fr;
  }
}

Let’s assume that I want to use display: masonry as an enhancement.

.layout {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1rem;
}

.card.featured {
  @container news (min-width: 600px) {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: max-content 1fr;
  }
}

@supports (display: masonry) {
  .layout {
    display: masonry;
    masonry-template-tracks: repeat(auto-fit, minmax(180px, 1fr));
    /*gap: 1rem;*/ /* gap is already there */
  }

  .card-featured {
    @container news (min-width: 600px) {
      /* Do we need to reset grid-column,
 or masonry-track will override it? */
      grid-column: initial;
      masonry-track: 1 / -1;
    }
  }
}

If we compare the above to the grid-integrated option, the most obvious difference is that grid-template-rows: masonry will work on supported browsers. If not, it will simply show the default behavior.

Another example that shows how masonry can be useful on top of CSS grid, not a separate layout module.

Section Layout

In this design, we have a section that consists of:

  • Headline and description

  • Gallery of images

  • Headline and description

  • Gallery of images

We need to build the image gallery dynamically:

  • On small sizes, the images will get a masonry style, but when the image container is wider (around tablet size), the images should be squared

  • If there are 5+ photos, the layout will change (e.g: square look).

See the following figure: