CSS

CSS Only Masonry Layouts with Grid

Sponsor

Masonry seems to be one of these things that for years the web has struggled with but has found no real meaningful solution. When I speak about Masonry, I am talking about the type of layout which is most famously used on Pinterest, where elements 'fill' the gaps underneath them. This differs quite a lot from flex or grid, which have fixed row width.

This idea has been popular for a while, and there is even a hugely popular Javascript Masonry plugin. It always would baffle me why such a large library was required for what seems like it should perhaps be the default behaviour of divs on a page. A truly native solution has always been lacking.

Image of Masonry vs Grid layout

Enter: CSS Masonry

The CSS Working Group has now created a proposal for masonry with just CSS, using just a few lines of code, which will work both on the horizontal and vertical axis.

css Copy
.container { display: grid; grid-template-columns: 20% 20% 20% 20%; grid-template-rows: masonry; }

Simple, right? Why hasn't anyone done this before? Well, apparently it's pretty hard to standardize, which is probably why a native solution has taken so long to get to us. Should items auto fill gaps? How do you manage order? Or what order should a screen reader read the boxes in?

Because of all these uncertainties, support remains thin on the ground and in fact Firefox is the only browser with experimental support. That means, realistically, we can't use the CSS only version anywhere today.

Should this really be a grid feature?

Since CSS native masonry is pretty much off the table for now, we have to make do with some other solutions. I would question at this stage if the grid specification is the right place to put masonry at all, since grids by their very nature are grids. They have a fixed structure.

Consider a grid, which has rows, which items can be assigned to. Now, if we decide to turn that into masonry, items can now overlap rows and be assigned to different rows at the same time. This logical conundrum makes it very difficult to understand how a masonry style grid layout would work.

In my view, masonry fits much more easily into the CSS flexbox specification, and logically this seems to be the direction that makes sense. In any case, I hope we end up with some implementation, since the current options are severely lacking. The real risk is that it is realised that this should be a flex feature too late, and we waste time waiting for the feature to be added.

Flexbox even sounds right - a box that flexes could flex both vertically and horizontally. By shoehorning this functionality into CSS grid, we might end up making this whole endeavour far more complicated than it really needs to be.

Implementing Masonry Today

The disconnect for me is that most people want a very basic functionality when it comes to masonry - to fill in the gaps. Most plugins, however, provide very advanced functionality for edge cases most people are not concerned about, i.e. when you have really abnormally shaped items.

As such, I have produced my own version below which does exactly what you'd expect masonry to do:

  • Fill in the gaps
  • Adjust automtically to CSS changes
  • Create a container of the correct height

Although this is a static implementation, you could re-run the Javascript to update should more items be added.

It is possible to do this with just CSS as shown here, although if you are trying to use masonry, the chances are you will be using Javascript too, so why keep it CSS only?

Using just a bit of Javascript (roughly 35 lines with comments), we can recursively align all divs within an element with masonry. We can also identify which columns items fall into, and set the max height of the container.

A full demo can be viewed below, and for your viewing pleasure, a Code Pen Demo with Source Code can be found here.

javascript Copy
let mainId = 'masonry-effect'; let itemIdentifier = '#masonry-effect .item'; document.addEventListener('DOMContentLoaded', function(e) { // Programmatically get the column width let item = document.querySelector(itemIdentifier); let parentWidth = item.parentNode.getBoundingClientRect().width; let itemWidth = item.getBoundingClientRect().width + parseFloat(getComputedStyle(item).marginLeft) + parseFloat(getComputedStyle(item).marginRight); let columnWidth = Math.round((1 / (itemWidth / parentWidth))); // We need this line since JS nodes are dumb let arrayOfItems = Array.prototype.slice.call( document.querySelectorAll(itemIdentifier) ); let trackHeights = {}; arrayOfItems.forEach(function(item) { // Get index of item let thisIndex = arrayOfItems.indexOf(item); // Get column this and set width let thisColumn = thisIndex % columnWidth; if(typeof trackHeights[thisColumn] == "undefined") { trackHeights[thisColumn] = 0; } trackHeights[thisColumn] += item.getBoundingClientRect().height + parseFloat(getComputedStyle(item).marginBottom); // If the item has an item above it, then move it to fill the gap if(thisIndex - columnWidth >= 0) { let getItemAbove = document.querySelector(`${itemIdentifier}:nth-of-type(${thisIndex - columnWidth + 1})`); let previousBottom = getItemAbove.getBoundingClientRect().bottom; let currentTop = item.getBoundingClientRect().top - parseFloat(getComputedStyle(item).marginBottom); item.style.top = `-${currentTop - previousBottom}px`; } }); let max = Math.max(...Object.values(trackHeights)); document.getElementById(mainId).style.height = `${max}px`; });
css Copy
#masonry-effect { display: flex; flex-direction: row; flex-wrap: wrap; } .item { flex-direction: column; margin-right: 1rem; margin-bottom: 1rem; position: relative; width: calc(33.3% - 1rem); background: linear-gradient(45deg, #281dd4, #a42bff); border-radius: 10px; padding: 1rem; font-size: 1.25rem; box-sizing: border-box; font-variation-settings: 'wght' 600; }

Simple Masonry Demo

Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12

Conclusion

In conclusion, today, masonry can be achieved with some very lightweight Javascript, and in a few years we may have a native CSS solution. I hope you find this page useful!

What do you think of masonry in CSS? Let me know your thoughts on twitter.

Last Updated Monday, 29 March 2021

Subscribe to Newsletter

Subscribe to stay up to date with our latest web development and software engineering posts via email. You can opt out at any time.

Not a valid email