Search code examples
htmlcssflexboxcss-grid

Photo collage of up to 6 photos using flexbox


I'm new to flexbox, and trying to use it to lay out a photo collage of up to 6 photos, like so: correct

I want the photos to be laid out as numbered, so that when there are fewer than 6 photos, the position of each photo above remains the same, just with an empty space for the missing photos.

Here's what I've got:

<html>
<head>
    <style type="text/css">
        .container {
            width: 600px;
            height: 600px;
            background-color: blue;
            display: flex;
            flex-direction: column;
            flex-wrap: wrap;
        }
        .photo {
            background-color: red;
        }
        .large {
            width: 400px;
            height: 400px;
        }
        .small {
            width: 200px;
            height: 200px;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="photo large">Photo 1</div>
        <div class="photo small">Photo 2</div>
        <div class="photo small">Photo 3</div>
        <div class="photo small">Photo 4</div>
        <div class="photo small">Photo 5</div>
        <div class="photo small">Photo 6</div>
    </div>
</body>
</html>

But this is producing this layout:

incorrect

For some reason, flex-wrap is not putting Photo 3 where I want it, even though there is room for it. I've tried fiddling with justify-content and align-items, but can't seem to get it to work.

Is there anyway to do this without having to introduce more <div>s?


Solution

  • CSS Grid Solution

    Since you have no restrictions regarding CSS Grid, have a look at what it can do for you when laying out items in both x and y directions. The grid-template-area property makes handling this type of layout trivial. You define the template areas in the parent element and then place the children in whichever area(s) you'd like. You can imagine this really opens up what you can do in different responsive/adaptive contexts.

    .container {
      width: 600px;
      height: 600px;
      background-color: blue;
      display: grid;
      grid-template-areas: "largetop largetop smalltop1" 
                           "largetop largetop smalltop2" 
                           "smallbottom1 smallbottom2 smallbottom3";
    }
    
    .photo-1 {
      grid-area: largetop;
    }
    
    .photo-2 {
      grid-area: smallbottom1;
    }
    
    .photo-3 {
      grid-area: smallbottom2;
    }
    
    .photo-4 {
      grid-area: smalltop1;
    }
    
    .photo-5 {
      grid-area: smalltop2;
    }
    
    .photo-6 {
      grid-area: smallbottom3;
    }
    
    .photo {
      background-color: red;
    }
    
    .large {
      width: 400px;
      height: 400px;
    }
    
    .small {
      width: 200px;
      height: 200px;
    }
    <div class="container">
      <div class="photo photo-1 large">Photo 1</div>
      <div class="photo photo-2 small">Photo 2</div>
      <div class="photo photo-3 small">Photo 3</div>
      <div class="photo photo-4 small">Photo 4</div>
      <div class="photo photo-5 small">Photo 5</div>
      <div class="photo photo-6 small">Photo 6</div>
    </div>

    jsFiddle

    Flexbox Solution

    Once the large photo takes up the available container space, the following two items wrap to the next row via flex-wrap: wrap.

    .container {
      width: 600px;
      height: 600px;
      background-color: blue;
      display: flex;
      /* row direction  */
    }
    
    .left {
      display: flex;
      flex-wrap: wrap;
      flex-basis: 400px;
    }
    
    .photo {
      background-color: red;
    }
    
    .large {
      width: 400px;
      height: 400px;
    }
    
    .small {
      width: 200px;
      height: 200px;
    }
    <div class="container">
      <div class="left">
        <div class="photo large">Photo 1</div>
        <div class="photo small">Photo 2</div>
        <div class="photo small">Photo 3</div>
      </div>
      <div class="right">
        <div class="photo small">Photo 4</div>
        <div class="photo small">Photo 5</div>
        <div class="photo small">Photo 6</div>
      </div>
    </div>