Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Masonry layout with css3 flex

Tags:

html

css

flexbox

I'm trying to make kind of masonry layout of items using display: flex;

It's running good until my items has different sizes:

here is example: http://jsfiddle.net/2pL3j07x/1/

I want small items below big items to wrap it around (2 items in a 'row' below big item should easly fit but only 1 goes there)

Is it possible with css flex to do so?

like image 950
Adam Pietrasiak Avatar asked Oct 18 '14 01:10

Adam Pietrasiak


People also ask

How do you make a masonry layout in CSS flexbox?

CSS masonry with flexbox, :nth-child(), and order. On the surface it seems fairly easy to create a masonry layout with flexbox; all you need to do is set flex-flow to column wrap and voilà, you have a masonry layout.

How do you add a masonry layout in CSS?

To use masonry layout, one of your grid axes needs to have the value masonry . This will then be referred to as the masonry axis, the other axis will have rows or column tracks defined as normal, this will be the grid axis. The CSS below creates a four-column grid, with the rows set to masonry .

What is masonry layout?

Masonry layout is a layout method where one axis uses a typical strict grid layout, most often columns, and the other a masonry layout. On the masonry axis, rather than sticking to a strict grid with gaps being left after shorter items, the items in the following row rise up to completely fill the gaps.


2 Answers

I know you don't want to use transforms, but this works exactly as described and actually has better browser support than flexbox does (although as far as the regularly updating browsers that's a less compelling argument than normal).

Regardless:

Here's the fiddle

What we're doing here:

.flex-c {
    transform: rotate(90deg) scaleY(-1);
    transform-origin: left top;
}

Setting the direction – We're twisting it to go up to down by moving the left side of the container to the top, a one-quarter turn clockwise.

Flipping the whole she-bang – We're flipping the container on the Y-axis, so that the orientation is correct (apparent as ltr in this case)

.flex-c:after {
    content: '';
    display: table;
    clear: both;
}

Fixing the float clearing stuff – Good resource on that here

.flex-i {
    transform: rotate(90deg) scaleY(-1);
    height: 100px;
    width: 100px;
    background-color: gray;
    margin: 0 10px 10px 0;
    float: left;
}

Reverting the flipping and twisting for individual items – We want these to display correctly, so we untwist them with the transform rule to head the .flex-i rule.

So let's take a look at what's going on with the final result:

  1. The big item is at the top left.
  2. Items are displayed in columns.
  3. The columns are filled from top to bottom.
  4. The growth is infinitely expansible.

Some downsides:

  1. Your vertices are flipped for layout on this container. You can mitigate this somewhat by keeping it in a third container for wrap if you don't mind extra markup (I personally do, but to each their own).
  2. This is going to fail miserably on older browsers – but so is your flexbox solution, so kind of a wash.

Hope this helps!

Edit:

So the comments raised the rather obvious question "What if we want more than one big element in this layout?"

Perfectly reasonable, and the solution as presented is going to leave some rather ugly gaps, granted. Unfortunately, if you need a pure content fill for those, you'll need to get much more creative and use either a JavaScript layout system (boo, slow, nasty, hard-to-maintain) or do something that several other masonry layouts have had to do before: tuckpointing

We're going to shim our empty spaces with dummy items. These can be pre-selected images, tiles, anything that's going to add a splash of flair in place of actual content without distracting from the content.

Here's how we do it:

Fiddle

Update the .flex-c container to hide its overflow

.flex-c {
    transform: rotate(90deg) scaleY(-1);
    transform-origin: left top;
    overflow: hidden;
}

Add an easily disguised shim pseudo-element

.flex-i:not(.big):after {
    content: '';
    height: 100px;
    width: 100px;
    left: 110px;
    top: 0;
    position: absolute;
    background-color: pink;
}

ALTERNATIVELY: You can do this in a way that's less CSS3-fancy but has better support

.flex-i:after {
    content: '';
    height: 100px;
    width: 100px;
    left: 110px;
    top: 0;
    position: absolute;
    background-color: pink;
}
.flex-i.big:after {
    display: none;
}

What this does in terms of the fiddle is creates a little pink box that pops out of the right side of each one of those content items. If there's a box there already, it gets covered up. If it's missing, we show the dummy box. Super simple.

like image 84
Josh Burgess Avatar answered Sep 22 '22 18:09

Josh Burgess


I think for what you are tryig to do, it would be easier using float:left; instead of the flex property. I made an updated

JSFiddle here

.flex-c {
    display: block;
    height: 450px;
    width: auto;
}

.flex-i {
    height: 100px;
    width: 100px;
    float:left;
    background: gray;
    margin: 0 10px 10px 0;
}

.big {
    width: 210px;
    height: 210px;
    float:left;
}

.wrap {
    display: inline-block;
}
<div class="wrap">
    <div class="flex-c">
        <div class="flex-i big"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
        <div class="flex-i"></div>
    </div>
</div>
like image 40
Drazzah Avatar answered Sep 23 '22 18:09

Drazzah