Create Masonry Layout with CSS (Grid with variable-sized contents arranged in columns)

Create Masonry Layout with CSS (Grid with variable-sized contents arranged in columns)

Source code for examples in this article can be found at https://github.com/umaranis/css-masonry-layout

In Masonry layout, variable-sized items are arranged in columns. It is different from a regular grid in the sense that it doesn’t have fixed-sized rows.

Pinterest is a popular example of the masonry layout. The layout looks beautiful and makes efficient use of space.

In this post, I try to create Masonry layout in CSS using different features like flex, grid and column-count. Let’s see how close we can get to masonry layout with just CSS.

Here is the code of HTML page which I am going to try to turn into a masonry layout.

<html>
<head>
    <link rel="stylesheet" href="masonry.css">
</head>

<body>
    <div class="container">
        <div class="item" style="height: 100px;">1</div>
        <div class="item" style="height: 200px;">2</div>
        <div class="item" style="height: 250px;">3</div>
        <div class="item" style="height: 200px;">4</div>
        <div class="item" style="height: 100px;">5</div>
        <div class="item" style="height: 50px;">6</div>
        <div class="item" style="height: 30px;">7</div>
    </div>
</body>
</html>

The HTML will stay the same, I will only be changing the CSS in the following attempts.

1st Attempt: Using CSS Grid

CSS Grid is a powerful layout system with rows and columns.

Here is the CSS to create the desired layout using the grid-layout system:

.container {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    grid-auto-rows: auto;
    grid-gap: 2px;
    width: 480px;
}

.item {
    color: #ffffff;
    background-color: #ff00d9;    
    padding: 5px;
    width: 150px;    
    border-radius: 15px;        
    font-size: x-large;      
}

We have two CSS classes:

  • container – for the div that contains the items to be laid out
  • item – for items in the container

The important lines are the highlighted ones which set up the grid system.

Here is the output:

The grid system has fixed sized rows and each item should be placed in a specific row and column (though they may span multiple columns or rows). Therefore, CSS Grid cannot provide us with a masonry look, let’s try something us.

Multiple Items in a cell: I thought of having a grid with multiple columns and only 1 row, and then place multiple items in the same cell. Hoping that items would flow vertically one after the other but in grid items placed in the same cell overlap (see specs). z-index property can be used to control the ordering of overlapping items.

Use Case:
– When all items are of the same size.
– When item size is a multiple of a specific size. Items, then, can be spanned to multiple rows and columns based on their size.

2nd Attempt: Using Flex

Flex is a one-dimensional layout system with the ability to wrap items. This is in contrast to Grid which is two-dimensional.

Here is the CSS to create the desired layout using flex:

.container {
    display: flex;
    flex-wrap: wrap;
    flex-direction: column;
    align-content: flex-start;
    height: 500px;
}

.item {
    color: #ffffff;
    background-color: #ff00d9;
    padding: 5px;
    width: 150px;
    border-radius: 15px;
    font-size: x-large;
    margin: 1px;
}

The important lines are the highlighted ones which set up the flex system. Flex direction is set to column so that items are laid out vertically and wraps into the next column.

Here is the output:

It looks good but there are a few problems.

1- We have to explicitly set the height of the container so that the items wrap into the next column.

2- If there are a small number of items, they will fill up the first column, rather than filling up the top row of the container. Rather than creating a cool layout, this will look pretty odd.

3- The ordering of items doesn’t look right. Users expect the items to flow from left to right and then to the next row.

Use Case: This may work if the order of items doesn’t matter and the height of the container can be set to a specific value.

3rd Attempt: Using flex and order property

In this attempt, I am going to improve the previous solution by using flex’s order and page break properties to resolve the ordering issue.

.container {
    display: flex;
    flex-flow: column wrap;
    align-content: flex-start;                
}

.container > :nth-child(3n + 1) { order: 1; } /* 1st column */
.container > :nth-child(3n + 2) { order: 2; } /* 2nd column */
.container > :nth-child(3n + 3) { order: 3; } /* 3rd column */

.container > :nth-child(-n + 3) {
    page-break-before: always; /* CSS 2.1 syntax */
    break-before: always;  /* New syntax */
}


.item {
    color: #ffffff;
    background-color: #ff00d9;
    padding: 5px;
    width: 150px;
    border-radius: 15px;
    font-size: x-large;
    margin: 1px;
}

Here is the output:

It is perfect now but unfortunately, it only works in Firefox.

Two tricks which make it work (albeit only in Firefox) are the following:

order – In flex, irrespective of the order in which items appear in the HTML, they are laid out based on their order property. We are using nth-child CSS selector to set the right ordering for 3 column grid.

break-before – This is the property which forces flex to break into the next column after certain number of items. break-before is only supported in Firefox.

4th Attempt: Using column-count

column-count is a CSS property which specifies how many columns an item should be divided into. It is commonly used to layout text in multiple columns (along with column-width). Let’s try to create a masonry layout with it.

Here is the CSS:

.container {                
    column-count: 3;
    column-gap: 10px;
    width: 480px;
}        

.item {
    display: inline-block;
    color: #ffffff;
    background-color: #ff00d9;    
    padding: 5px;
    width: 150px;
    margin: 1px 0;
    border-radius: 15px;    
    font-size: x-large;   
}

display: inline-block property is important. Without it, the item may break and not display completely in one column.

Here is the output:

It looks perfect but has the same problem we had earlier with flex. The ordering of items doesn’t look right. Users expect the items to flow from left to right and then to the next row.

It is an improvement on flex through as we don’t have to set the height of the container and it uses all the available columns first, rather than filling up the first column.

Use Case: When the ordering of items is not important.

Closing

We came quite close to creating a Masonry layout with pure CSS. All these layouts will work for some scenarios, but none of them ticks all the boxes. The one (3rd attempt) which does tick all the boxes is only supported on Firefox (as of now).

So, in order to create a more sophisticated we would need JavaScript.
Here is a popular jQuery plugin for creating Masonry layout
Here is JS library by the same author which supports a number of layouts including Masonry.

Source code for examples in this article can be found at https://github.com/umaranis/css-masonry-layout

Leave a Reply

Your email address will not be published.