0

I'm trying to create a masonry layout that can also span an item over 2 columns. So far I have made what I feel is a great start. Have a look:

.wrapper {
  display: grid;
  grid-gap: 20px;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  grid-auto-rows: 5px;
  max-width: 600px;
  margin: 0 auto;
}
  
.wrapper  div {
  background: red;
}  

.two {
  grid-column-end: span 2;
}
<div class="wrapper">
  <div class="one" style="grid-row-end: span 3"></div>
  <div class="one" style="grid-row-end: span 3"></div>
  <div class="one" style="grid-row-end: span 3"></div>
  <div class="two" style="grid-row-end: span 4; background-color: green;"></div>
  <div class="one" style="grid-row-end: span 4"></div>
  <div class="one" style="grid-row-end: span 2"></div>
  <div class="one" style="grid-row-end: span 3"></div>
  <div class="one" style="grid-row-end: span 2"></div>
  <div class="one" style="grid-row-end: span 5"></div>
</div>

Here is a JsFiddle if you want to play with it: http://jsfiddle.net/9o4de38u/2/

Now for this to work, I also have some javascript which determines how many rows the item needs to span. I don't think its relevant so I left it out and hard-coded values in the html for purposes of this demo.

My problem here is if you take a closer look at my demo, the green box spans over 2 columns, however the space before the green is empty. Because the green had to take up 2 spaces, the previous element only took one leaving a big gap. I want to fill this gap with the element that comes after the green. However, I'm not certain what would be the best way to do this.

My idea is to simply swap the order of elements in the html. If I swap green and the previous red then it will fill out nicely. The trouble is how to do this. My current thoughts are to use Javascript and use start position of the element to determine if a swap is needed. For Example if green box has a same starting position as the red box before it, swap them.

But I don't know how much I can rely on this, what if a browser renders it off different and its off by a pixel, it will have no effect.

My question is, what would be in your opinion the best way for me to accomplish what I want?

If there is a library out there that does what I want, I'll accept that as an answer as well. I just couldn't find any that can allow an item to span multiple columns.

Bagzli
  • 6,254
  • 17
  • 80
  • 163
  • @Paulie_D I was just about to post an answer about that. Did you want to turn your comment into an answer? – Steven Lambert Jul 12 '18 at 16:00
  • @Paulie_D the inline style force the item to be certain height. I have to do this in order to achieve masonry layout. – Bagzli Jul 12 '18 at 16:05
  • @Paulie_D sure, but height doesn't work with this. Give it a try, if you can make it work, please share the fiddle. – Bagzli Jul 12 '18 at 16:08
  • @Paulie_D I am not using masonry.js. I'm just trying to replicate that sort of layout. I set rows to 5px to reduce the vertical gap that gets created between elements. – Bagzli Jul 12 '18 at 16:12
  • Here - https://stackoverflow.com/questions/44377343/css-only-masonry-layout-but-with-elements-ordered-horizontally I suspected this was a dupe and it certainly seems to be but I have a casting vote and wanted to hold off. – Paulie_D Jul 12 '18 at 16:16

1 Answers1

3

Rafaela Ferro wrote an article on medium that goes into amazing detail on creating masonry style layouts using CSS grid that allow for great flexibility.

As @Paulie_D recommended, using grid-auto-flow: dense will tell the grid to fill in any holes produced by non-uniform layouts. Due to how you're setting up your heights though, some gaps will still occur.

.wrapper {
  display: grid;
  grid-gap: 20px;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  grid-auto-rows: 5px;
  max-width: 600px;
  margin: 0 auto;
  grid-auto-flow: dense;
}
  
.wrapper  div {
  background: red;
}  

.two {
  grid-column-end: span 2;
}
<div class="wrapper">
  <div class="one" style="grid-row-end: span 3"></div>
  <div class="one" style="grid-row-end: span 3"></div>
  <div class="one" style="grid-row-end: span 3"></div>
  <div class="two" style="grid-row-end: span 4; background-color: green;"></div>
  <div class="one" style="grid-row-end: span 4"></div>
  <div class="one" style="grid-row-end: span 2"></div>
  <div class="one" style="grid-row-end: span 3"></div>
  <div class="one" style="grid-row-end: span 2"></div>
  <div class="one" style="grid-row-end: span 5"></div>
</div>
Steven Lambert
  • 5,571
  • 2
  • 29
  • 46
  • Steven, could you create a working example of what you mean? If you wish to switch to using heights to make it work, please do. I'm not set on having it in any specific way. This was all me trying to figure out a way to make what I want work. – Bagzli Jul 12 '18 at 16:09
  • 1
    CSS grid can't produce true masonry layouts since the grid rows have to be uniform. Depending on what you want, you might be fine to have a good enough masonry layout by using grid items of same heights. Or you could vary the heights / widths like the article does to create an interesting grid layout, but still not true masonry. That or you have to guarantee that for every `span 3` grid item, you have a # of grid items that equal `span 3` in the end (so one `span 2` and one `span 1` or three `span 1`). That way all the empty spaces will get filled in. – Steven Lambert Jul 12 '18 at 16:13
  • Adding `grid-auto-flow: dense;` to my project seems to produce results exactly what I am looking for. However, now I am wondering about the heights. I use `grid-row-end` to make the column span a certain height, but is there a way to do without it? See, my heights are dynamic so I don't know how tall each block will end up being. This is why I have javascript to calculate it for me and assign it the right span size. How can I do without that? – Bagzli Jul 12 '18 at 16:25
  • Ah, never mind, i was wrong. The moment it goes to 4 or 5 rows it starts having gaps again. – Bagzli Jul 12 '18 at 16:34