Last year, many CSS experts discussed potential syntaxes for a new masonry layout feature. There were two main groups and a third group that balanced the two:
1. Use `display: masonry`
2. Use `grid-template-rows: masonry`
3. Use `item-pack: collapse`
A resolution hasn’t been reached yet. However, Firefox already supports the masonry layout with the second syntax, and Chrome is testing it with the first syntax. While it’s exciting to see native support for CSS Masonry evolving, it’s not feasible to use it in production if other browsers don’t support the same implementation.
Instead of joining one of these groups, I explored how to make masonry work with other browsers today. I found a way to do it with only 66 lines of JavaScript.
In this article, I’ll show you how it works. Here’s a demo to prove I’m not exaggerating. Note that there will be a slight delay since we’re waiting for an image to load first. If you’re placing a masonry at the top fold, consider skipping images due to this!
Here’s the demo:
### What in the magic is this?!
I’ve included a lot in this demo, even though there are only 66 lines of JavaScript:
– You can define the masonry with any number of columns.
– Each item can span multiple columns.
– We wait for media to load before calculating each item’s size.
– We made it responsive by listening to changes with the `ResizeObserver`.
These make my implementation robust and ready for production use, while also more flexible than many Flexbox masonry knockoffs on the internet.
**Now, a hot tip.** If you combine this with Tailwind’s responsive variants and arbitrary values, you can include even more flexibility into this masonry grid without writing more CSS.
Before you get too excited, let’s return to the main question: How does this work?
### Let’s start with a polyfill
Firefox supports masonry layouts via the second group’s syntax. Here’s the CSS you need to create a CSS masonry grid layout in Firefox.
“`css
.masonry {
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(min(var(–item-width, 200px), 100%), 1fr)
);
grid-template-rows: masonry;
grid-auto-flow: dense; /* Optional, but recommended */
}
“`
Since Firefox has native masonry support, we shouldn’t interfere with it. The best way to check if masonry is supported by default is to check if `grid-template-rows` can hold the `masonry` value.
“`javascript
function isMasonrySupported(container) {
return getComputedStyle(container).gridTemplateRows === ‘masonry’
}
“`
If masonry is supported, we’ll skip our implementation. Otherwise, we’ll take action.
“`javascript
const containers = document.querySelectorAll(‘.masonry’)
containers.forEach(async container => {
if (isMasonrySupported(container)) return
})
“`
### Masonry layout made simple
I didn’t invent this technique. I discovered it while searching for ways to implement a masonry grid today. Kudos to the unknown developer who first developed the idea — and to me for understanding, converting, and using it.
The technique involves:
1. Setting `grid-auto-rows` to `0px`.
2. Setting `row-gap` to `1px`.
3. Getting the item’s height through `getBoundingClientRect`.
4. Sizing the item’s “row allocation” by adding the `height` and `column-gap` value together.
This is unintuitive if you’ve been using CSS Grid the standard way. But once you understand it, you can grasp how it works!
We’ll take things step-by-step to show how this evolves into the final output.
### Step by step
First, set `grid-auto-rows` to `0px`. This is unusual because every grid item will effectively have “zero height”. Yet, CSS Grid maintains the order of the columns and rows!
“`javascript
containers.forEach(async container => {
// …
container.style.gridAutoRows = ‘0px’
})
“`
Second, set `row-gap` to `1px`. You’ll notice an initial stacking of the rows, each one pixel below the previous one.
“`javascript
containers.forEach(async container => {
// …
container.style.gridAutoRows = ‘0px’
container.style.setProperty(‘row-gap’, ‘1px’, ‘important’)
})
“`
Third, assuming no images or media elements in the grid items, get the height of each grid item with `getBoundingClientRect`.
Restore the “height” of the grid item in CSS Grid by substituting `grow-row-end` with the `height` value. This works because each `row-gap` is now `1px` tall.
“`javascript
containers.forEach(async container => {
// …
let items
Discover more from WIREDGORILLA
Subscribe to get the latest posts sent to your email.