CSS Flex dynamic grid with multiple sizes

Question

I have a problem with flex.

I have a wrapper where a minimum of 1 and maximum of 9 squares can be shown. Squares can have multiple sizes, based on the number of squares in grid. I've got all required cases working except for one, as seen in this picture: enter image description here

My styles are:

.grid {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
    align-content: space-between;
    width: 300px;
    height: 300px;
    position: relative;
}

Plus. the images have sized based on the overall number of them and their position in a list.

So the problem is in situation when I have 1 big square (takes position of 4 small squares) and 5 small squares around him from right and bottom.

The big one is first as he should be.

Next to him (top right corner) is second one, that's also correct.

The third one is in bottom left corner, and it should be in the second line and on the far right. Because of this one, all the others are in wrong position, so the last one is overflowing.

I've tried a lot of value combinations for justify-content, align-content, align-items and align-self but nothing have worked.

I'll go back to ton of classes and position absolute solution, if there is no flex solution for this. But I don't like it. It's too much styles and it doesn't look good.

Any advice would be greatly appreciated.


Show source
| css   | flexbox   2017-01-03 14:01 2 Answers

Answers ( 2 )

  1. 2017-01-03 16:01

    I think float is a better option for you, check out this snippet:

    .grid {
      width: 300px;
    }
    .box {
      background: orange;
      width: 90px;
      height: 90px;
      margin: 5px;
      float: left;
    }
    .big {
      width: 190px;
      height: 190px;
    }
    <div class="grid">
      <div class="box big"></div>
      <div class="box"></div>
      <div class="box"></div>
      <div class="box"></div>
      <div class="box"></div>
      <div class="box"></div>
    </div>

    Flex is still trying to make complete rows of elements, so your big square and your little square are part of one row; there's no support for stacking beyond that.

    Float on the other hand tries to stuff elements wherever it can fit them.

  2. 2017-01-06 10:01

    There is no way to handle this layout with flex in a single container.

    You need to do a little trick to achieve it.

    The easier one would be to take the third item out of the flex layout, positioning it absolute:

    .grid {
        display: flex;
        flex-wrap: wrap;
        justify-content: flex-start;
        align-content: space-between;
        width: 300px;
        height: 300px;
        position: relative;
    }
    
    .item {
        background-color: lightblue;
        width: 100px;
        height: 100px;
        margin: 0px;
        border: transparent solid 5px;
        box-sizing: border-box;
        background-clip: content-box;
    }
    
    .item:first-child {
        width: 200px;
        height: 200px;
    }
    
    .item:nth-child(2) {
        background-color: blue;
        position: absolute;
        top: 100px;
        right: 0px;
    }
    <div class="grid">
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    </div>

    Another posibility, may be more in the flex idea, but also tricky

    Set the big element with a margin-bottom negative, that makes it occupy only 1 row (being the height of a row the size of the small boxes).

    Now be have a layout with 3 rows. The problem will be that the 3rd box will be under the first, big box. To solve this, we are setting a pseudo element (I have styled the snippet to make it visible, in production just set it to height 0 and it will disappear) with the same properties of width and margin of the first element.

    .grid {
        display: flex;
        flex-wrap: wrap;
        justify-content: flex-start;
        align-content: space-between;
        width: 300px;
        height: 300px;
        position: relative;
    }
    
    .grid:after {
       content: "";
      order: 3;
      background-color: red;
      width: 190px;
      height: 10px;
      margin: 5px;
    }
    
    .item {
        background-color: lightblue;
        width: 90px;
        height: 90px;
        margin: 5px;
    }
    
    .item:first-child {
        width: 190px;
        height: 190px;
      margin-bottom: -100px;
      order: 1;
      opacity: 0.5;
    }
    
    .item:nth-child(2) {
      order: 2;
    }
    
    .item:nth-child(n+3) {
      order: 4;  
    }
    <div class="grid">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
    <div class="item">6</div>
    </div>

◀ Go back