Simple Masonry layouts with CSS Flexbox

A tutorial guide to show how to create CSS Flexbox-based Masonry layouts and improve them further with tiny bits of JavaScript.

Simple Masonry layouts with CSS Flexbox
See the Demo

CSS Flexbox module allows you to layout things more easily. In this post, I’m going to share an easy way to do a masonry layout with Flexbox.

I have already posted about CSS-only masonry earlier before; but this one is little bit different from that covering the modern approach. So without much ado, lets start.

Flexbox module provides a set of different properties that you may set to lay and align the boxes. If you don’t know much about Flexbox, this guide is a recommended read.

Concept(s)

1. Horizontal Masonry

The first concept is to give all the masonry bricks or cells or flex container’s child items flex: auto. The children then will be sized according to their width and height properties, but will also grow to absorb any extra free space in the masonry, and shrink to their minimum sizes to fit the masonry.

We have to give each masonry brick an equal height to arrange all of them in a horizontal masonry fashion.

.masonry { 
  display: flex;
  flex-flow: row wrap;
  margin-left: -8px; /* Adjustment for the gutter */
}

.masonry-brick {
  flex: auto;
  height: 250px;
  min-width: 150px;
  margin: 0 8px 8px 0; /* Some gutter */
}

We will also be giving different widths to the .masonry-brick items that will make the items arranged in a masonry fashion.

.masonry-brick {
  &:nth-child(4n+1){
     width: 250px;
  }
  &:nth-child(4n+2){
     width: 325px;
  }
  &:nth-child(4n+3){
     width: 180px;
  }
  &:nth-child(4n+4){
     width: 380px;
  }
}

Demo the Horizontal Masonry

2. Vertical Masonry

The second concept is to give our masonry container (.masonry) a fixed height and then set the flex-direction to column. This technique is not so handy, as you will have to tweak up the height of the container everytime you add new items to the masonry.

.masonry { 
  display: flex;
  flex-flow: column wrap;
  max-height: 800px;
  margin-left: -8px; /* Adjustment for the gutter */
}

.masonry-brick {
  margin: 0 8px 8px 0; /* Some gutter */
}

Demo the Vertical Masonry

The vertical masonry scene is quite some work, as you would require to modify the masonry height each time you add a new brick to it. How about a little piece of JavaScript do that task for us?

Veritcal Masonry with JavaScript

Below JavaScript function is for the calculation of the collective height of all the bricks, and then to provide the masonry container a calculated height based on the masonry gutter, and the number of columns provided for different screen breakpoints.

<script>
function masonry(grid, gridCell, gridGutter, dGridCol, tGridCol, mGridCol) {
  var g = document.querySelector(grid),
      gc = document.querySelectorAll(gridCell),
      gcLength = gc.length,
      gHeight = 0,
      i;
  
  for(i=0; i<gcLength; ++i) {
    gHeight+=gc[i].offsetHeight+parseInt(gridGutter);
  }
  
  if(window.screen.width >= 1024)
    g.style.height = gHeight/dGridCol + gHeight/(gcLength+1) + "px";
  else if(window.screen.width < 1024 && window.screen.width >= 768)
    g.style.height = gHeight/tGridCol + gHeight/(gcLength+1) + "px";
  else
    g.style.height = gHeight/mGridCol + gHeight/(gcLength+1) + "px";
}
</script>

Now, we need to bind the above function with “load” and “resize” events for the browser window, so that it could be executed everytime the document is loaded or resized. For the proper functionality with images, I’m using ImagesLoaded to detect if all images are loaded, and then I’m firing the function inside it for the perfect calculation of the height of each brick.

<script>
["resize", "load"].forEach(function(event) {
  window.addEventListener(event, function() {
    imagesLoaded( document.querySelector('.masonry'), function() {
      // A maonsry grid with 8px gutter, with 3 columns on desktop, 2 on tablet, and 1 column on mobile devices.
      masonry(".masonry", ".masonry-brick", 8, 3, 2, 1);
    });
});
</script>

Fancy Preloading

In order to add fancy preloading text to the vertical masonry, you may consider using something like below. It’s about nothing but adding an adjacent HTML element to our masonry container, which we have to hide as soon as the all the images finish loading. A toggle in display on certain events is what we want here.

<script>
var masonryElem = document.querySelector('.masonry');
masonryElem.insertAdjacentHTML("afterend", "
Loading...
"); var masonryPreloader = document.querySelector('.masonry-preloader'); ["resize", "load"].forEach(function(event) { // Hiding the preloader masonryElem.style.display="none"; window.addEventListener(event, function() { imagesLoaded( document.querySelector('.masonry'), function() { masonryElem.style.display="flex"; masonryPreloader.style.display="none"; // A masonry grid with 8px gutter, with 3 columns on desktop, 2 on tablet, and 1 column on mobile devices. masonry(".masonry", ".masonry-brick", 8, 3, 2, 1); console.log("Loaded"); }); }, false); }); </script>

Demo the Flexbox Masonry with JS

Pretty lightweight, I guess! Please share your thoughts in the comments section.

And this is it. Currently, I’m working on the JS part to make it ordered from left-to-right. It is kinda tricky though, not sure where I’m gonna end up with it. Stay tuned, and don’t forget to share the post if you found it cool. Thanks for your time :)