How to keep wrapped flex-items the same width as the elements on the previous row?

CssFlexbox

Css Problem Overview


I have a <ul> that is a flex-box and a bunch of <li>s in it which are the flex-items.

I am trying to get the <li> to have a flexible width, that stays between two sizes using min-width and max-width. It works great as long as they are on the same line. However, when they wrap, the <li> s on the new line use as much space as they can. Which means they are small on the first line, and big on the second line when there are just a few of them.

Is there a way to tell flexbox to keep the items the width after wrapping, while keeping the flexible width?

wrapping demo:

enter image description here

My code looks like this:

<ul>
  <li>
    <figure>
      <img src="http://placekitten.com/g/250/250">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/100/100">
    </figure>
  </li>
  <!-- ... -->
</ul>

And the CSS:

ul {
  display: flex;
  flex-wrap: wrap;
}

  li {
    min-width: 40px;
    max-width: 100px;
    flex: 1 0 0;
  }

Here's a live example on codepen with some extra comments, resize your window to see it wrap.

Css Solutions


Solution 1 - Css

TL;DR

This is not something I'd call a solution per se, but it's a rather elegant workaround that only uses media queries, and more importantly no JavaScript!

Mixin (SCSS):

@mixin flex-wrap-fix($flex-basis, $max-viewport-width: 2000px) {
  flex-grow: 1;
  flex-basis: $flex-basis;
  max-width: 100%;

  $multiplier: 1;
  $current-width: 0px;

  @while $current-width < $max-viewport-width {
    $current-width: $current-width + $flex-basis;
    $multiplier: $multiplier + 1;

    @media(min-width: $flex-basis * $multiplier) {
      max-width: percentage(1/$multiplier);
    }
  }
}

Usage:

Apply the mixin to your flex item:

.flex-item {
  @include flex-wrap-fix(100px)
}

Update:

The above mixin should do the trick, as long as you your flex container width matches your viewport size, as is the case in OP's example. Media queries won't help you otherwise, because they're always based on the viewport width. However, you could use the [css-element-queries][1] library and its element queries instead of browser media queries. Here's a mixin that you can apply to the flex container:

@mixin flex-container-wrap-items($flex-basis, $max-expected-width: 2000px) {
  display: flex;
  flex-wrap: wrap;

  > * {
    max-width: 100%;
    flex-grow: 1;
    flex-basis: $flex-basis;
  }

  $multiplier: 1;
  $current-width: 0px;

  @while $current-width < $max-expected-width {
    $current-width: $current-width + $flex-basis;
    $multiplier: $multiplier + 1;

    &[min-width~="#{$flex-basis * $multiplier}"] > * {
      max-width: percentage(1/$multiplier);
    }
  }
}

Explanation:

Let's say, as per the OP's example, we want each item to have a maximum width of 100px, so we know that for a browser width of 100px we can fit one item per row, and so on:

| Viewport Width | Max Item Count Per Row | Item Width (min-max) |
|----------------|------------------------|----------------------|
| <= 100         | 1                      | 0px - 100px          |
| <= 200         | 2                      | 50px - 100px         |
| <= 300         | 3                      | 50px - 100px         |
| <= 400         | 4                      | 50px - 100px         |
| <= 500         | 5                      | 50px - 100px         |
| <= 600         | 6                      | 50px - 100px         |

We can write media queries to create the following rules:

| Viewport Width | Max Item Count Per Row | Item Max Width | Calculation |
|------------------------------------------------------------------------|
| <= 100px       | 1                      | 100%           | (100/1)     |
| <= 200px       | 2                      | 50%            | (100/2)     |
| <= 300px       | 3                      | 33.33333%      | (100/3)     |
| <= 400px       | 4                      | 25%            | (100/4)     |
| <= 500px       | 5                      | 20%            | (100/5)     |
| <= 600px       | 6                      | 16.66666%      | (100/6)     |

Like this:

li {
  flex: 1 0 0
  max-width: 100%;
}

@media(min-width: 200px) {
  li { max-width: 50%; }
}

@media(min-width: 300px) {
  li { max-width: 33.33333%; }
}

@media(min-width: 400px) {
  li { max-width: 25%; }
}

@media(min-width: 500px) {
  li { max-width: 20%; }
}

@media(min-width: 600px) {
  li { max-width: 16.66666%; }
}

Of course, that's repetitive, but most likely you're using some sort of preprocessor, which can take care of the repetitiveness for you. That's precisely what the mixin in the above TL;DR section does.

All we have to do now is to specify 100px as our flex-basis, and optionally the maximum browser window width (defaults to 2000px) to create the media queries for:

@include flex-wrap-fix(100px)

Example

Finally, a forked version of the original CodePen example with the desired output, using the above mixin:

http://codepen.io/anon/pen/aNVzoJ

[![demo of the solution][2]][2]

[1]: https://github.com/marcj/css-element-queries "css-element-queries" [2]: http://i.stack.imgur.com/1qthz.gif

Solution 2 - Css

Alternative - grid

It's not the answer to what you want, but it nice to use:

display: grid;
grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));

https://codepen.io/omergal/pen/povzJrz

ul {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
}

li {
  min-width: 40px;
  max-width: 100px;
}

ul,
li {
  margin: 0;
  padding: 0;
  list-style: none;
}
ul {
  background-color: tomato;
}

li {
  margin: 0.5em;
  background-color: darkgreen;
}

img {
  width: 100%;
  opacity: 0.5;
}
figure,
img {
  margin: 0;
  padding: 0;
}

<ul>
      <li>
        <figure>
          <img src="http://placekitten.com/g/250/250">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/101/101">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/201/201">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/150/150">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/80/80">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/111/111">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/40/40">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/110/110">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/75/75">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/89/89">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/150/150">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/32/32">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/61/61">
        </figure>
      </li>
      <li>
        <figure>
          <img src="http://placekitten.com/g/320/320">
        </figure>
      </li>
    </ul>

Solution 3 - Css

I have been experimenting with your codepen and I set flex: 0 1 10%

Of course you can set the flex-basis whatever you want, I just wanted to prove the point, that if you set flex basis and allow it to shrink, it will behave much nicer in this case.

I think this is what you needed. Here's the preview: http://codepen.io/anon/pen/bwqNEZ

Cheers

Solution 4 - Css

I think I got it...but it's hacky. The result is the 14 images each scaling from 100px wide down to 40px, even on multiple rows.

From 107px to 40px Width

  • Added a duplicate set of the 14 original <li>s and added them to the flex <ul>.

  • Next, the second set was rendered invisible:

     li:nth-of-type(n + 15) {
       visibility: hidden;
     }
    
  • In order to maintain uniform flexibility the following changes to each <li>:

     li {
       min-width: 40px;
       max-width: 10%;
       flex: 1 0 100px;
     }
    

Please review the CodePen

and/or the Snippet in Full Page mode.

ul {
  display: flex;
  flex-wrap: wrap;
  max-height: 258px;
}
li {
  min-width: 40px;
  max-width: 10%;
  flex: 1 0 100px;
}
ul,
li {
  margin: 0;
  padding: 0;
  list-style: none;
}
ul {
  background-color: tomato;
}
li {
  margin: 0.5em;
  background-color: darkgreen;
}
li img {
  width: 100%;
  opacity: 0.5;
}
li figure,
li img {
  margin: 0;
  padding: 0;
}
li:nth-of-type(n + 15) {
  visibility: hidden;
}

<ul>
  <li>
    <figure>
      <img src="http://placekitten.com/g/250/250">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/101/101">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/201/201">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/150/150">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/80/80">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/111/111">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/40/40">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/110/110">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/75/75">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/89/89">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/150/150">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/32/32">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/61/61">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/320/320">
    </figure>
  </li>



  <li>
    <figure>
      <img src="http://placekitten.com/g/250/250">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/101/101">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/201/201">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/150/150">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/80/80">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/111/111">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/40/40">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/110/110">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/75/75">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/89/89">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/150/150">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/32/32">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/61/61">
    </figure>
  </li>
  <li>
    <figure>
      <img src="http://placekitten.com/g/320/320">
    </figure>
  </li>
</ul>

Solution 5 - Css

Setting the min-width and max-width as percentages has allowed all images to stay the same width when they break onto the next line. It is not ideal, but closer to what you are after?

li {
  min-width: 15%;
  max-width: 15%;  
}

Solution 6 - Css

I came here looking to solve the same problem for a flex box I have. I've since realised that this is now much easier with display: grid as detailed here and here.

Solution 7 - Css

Its not working very well, but you can achieve something in this direction with columns.

column-gap: 10px;
column-width: 150px;

https://codepen.io/hobbeshunter/pen/OgByNX

Solution 8 - Css

For Stylus you can use this mixin: https://codepen.io/anon/pen/Epjwjq</a>


flex-wrap-fix($basis, $max) {
flex-grow: 1
flex-basis: $basis
max-width: 100%



 $multiplier = ceil($max / $basis)
 
 for i in (1..$multiplier) {
   @media(min-width: ($basis * i)) {
     max-width: percentage(1/i)
   }
 }




}

}

Solution 9 - Css

This isn't perfect but worth try. http://codepen.io/anon/pen/MKmXpV

The changes are

li
  width: 7.1428%
  flex: 0 0 auto

figure
  margin: 0 auto
  background-color: darkgreen
  min-width: 40px
  max-width: 100px
  • changed li flex to "0 0 auto" and the li width to a percentage based on the number of lis.
  • moved the min and max widths from the li to the figure
  • moved the green background to the figure and centered the figures in the lis

Solution 10 - Css

My solution is not ideal, as it leans on JQuery: http://codepen.io/BigWillie/pen/WwyEXX

CSS

ul {
  display: flex;
  flex-wrap: wrap;
}

li {
  min-width: 40px;
  max-width: 100px;
  flex: 1;
}

JavaScript

var eqRowFlexItem = function(elem) {
    var w;
    var $elem = $(elem);
    // Clear out max-width on elements
    $elem.css('max-width', '');
    w = $(elem).eq(0).width();
    $(elem).css('max-width', w);
}

eqRowFlexItem('li');

$(window).resize(function() {
    eqRowFlexItem('li');
});

I'm not entirely sure if it answers the problem. I found this post, as the title matched the problem I was having. I was after an effect whereby I would have a greater number of elements per row on larger screens - and fewer elements per row on smaller screens. In both cases, these elements also needed to scale to fit... across devices.

However, those stray elements on the last row would always expand to fill the remaining space.

The JQuery gets the width of the first element - and applies it as a max-width to every element. The OP wanted the blocks to fall within a min-width / max-width rage. Setting the max-width in CSS seems to work. We clear the max-width off the element, so it defaults to the max-width in the CSS... then gets the actual width.

Solution 11 - Css

I had the same issue, so I used a very tiny bit of Jquery to control the width of the boxes and make them all the same width even once they wrap.

My demo is on CodePen Flex wrap, with equal width on new row

function resize(){
  var colWidth;
  var screen = $(window).width(); 
  
  if(screen < 576){
    colWidth = Math.round(screen / 2) - 31;
    $(".item").width(colWidth);
  } 
  if(screen > 575 && screen < 768){
    colWidth = Math.round(screen / 3) - 31;
    $(".item").width(colWidth);
  } 
  else if (screen > 767 && screen < 992){
      colWidth = Math.round(screen / 4) -31;
      $(".item").width(colWidth);
  }
  else if (screen > 991 ){
      colWidth = Math.round(screen / 6) -31;
      $(".item").width(colWidth);
  }
}
  
window.onresize = function() {
  resize();
}

resize();

html, body{ padding:0; margin:0; } .flex-container { padding: 0; margin: 0; list-style: none; -ms-box-orient: horizontal; display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -moz-flex; display: -webkit-flex; display: flex; justify-content: left; background-color:#ddd; }

.wrap    { 
  -webkit-flex-wrap: wrap;
  flex-wrap: wrap;
}  

.item {
  background: tomato;
  height: 100px;
  margin:0 15px;
  line-height: 100px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
  margin-top:10px;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="flex-container wrap">
  <li class="item">1</li>
  <li class="item">2</li>
  <li class="item">3</li>
  <li class="item">4</li>
  <li class="item">5</li>
  <li class="item">6</li>
  <li class="item">7</li>
  <li class="item">8</li>
  <li class="item">9</li>
</ul>

Solution 12 - Css

if the layout is fixed, you could simply add hidden items and toggle their visibility using css breakpoints for the pixels where wrapping happens.

for example, in the following scenario .columned-list has 3 .columned-list-item elements, and for the pixels within which the 3rd .columned-list-item element wraps to the next row, I just make the 4th .columned-list-item element .hidden-item - which is normally invisible - visible :

.columned-list {
            display: flex;
            flex-wrap: wrap;
            margin-left: 35px;

          
            justify-content: flex-start;

           

            .hidden-item {
                display: none !important;

                @media only screen and (max-width: 1375px) {
                    flex: 1 0 auto !important;
                    min-width: 410px !important;
                    width: 30% !important;
                    margin: 0 37px 37px 0 !important;
                    max-width: 100% !important;
                    display: flex !important;
                    background-color: #fff !important;
                }

                @media only screen and (max-width: 928px) {
                    display: none !important;
                }
            }

            .columned-list-item {
                flex: 1 0 auto;
                min-width: 410px;
                min-height: 1px !important; // <3 IE.
                width: 30%;
                margin: 0 37px 37px 0;
                max-width: 100%;
                display: flex;
                flex-direction: column;
                align-items: center;
                color: #676562;
                background-color: #f2f3f5;

                a:nth-of-type(1) {
                    position: relative;
                    min-height: 84px;
                    width: 100%;

                    img {
                        width: 100%;
                        height: auto;
                    }

                    div {
                        min-height: 88px;
                        max-width: 203px;
                        padding: 0 15px;
                        top: 50%;
                        transform: translateY(-50%);
                        position: absolute;
                        display: flex;
                        justify-content: center;
                        align-items: center;

                        background-color: #fff;
                        opacity: 0.6;
                        span {
                            height: 55px;
                            font-family: 'Trade Gothic Next LT W04 Rg', sans-serif;
                            font-size: 28px;
                            font-weight: bold;
                            font-stretch: normal;
                            font-style: normal;
                            line-height: 1;
                            letter-spacing: 0.7px;
                            text-align: left;
                            color: #676562;
                        }

                    }

                }

                a:nth-of-type(2) {

                    padding: 10px 35px;
                    display: flex;
                    flex-direction: column;
                    min-height: 161px;
                    max-width: 390px;

                    strong {
                        font-family: 'Trade Gothic Next LT W04 Bold', sans-serif;
                        font-weight: bold;
                        text-align: center;
                    }
                    span {

                        font-family: 'Trade Gothic Next LT W04 Light', sans-serif;
                        font-size: 15px;
                        font-weight: normal;
                        font-stretch: normal;
                        font-style: normal;
                        line-height: 1.4;
                        letter-spacing: normal;
                        text-align: center;
                        color: #676562;

                    }
                }

                .buttons-block {
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    width: 298px;
                    max-width: 100%;
                    margin: 10px 0 23px 0;

                    .button-a {
                        width: 114px;
                        height: 22px;
                        min-height: 1px;
                        box-shadow: 0px 1px 0 0 rgba(0, 0, 0, 0.16);
                        background-color: #f28d4e;
                        display: flex;
                        justify-content: center;
                        align-items: center;

                        span {
                            font-family: 'Trade Gothic Next LT W04 Rg', sans-serif;
                            font-size: 15px;
                            font-weight: bold;
                            font-stretch: normal;
                            font-style: normal;
                            line-height: 1.4;
                            letter-spacing: normal;
                            text-align: center;
                            color: #ffffff;
                        }
                    }

                    .button-b {
                        margin-bottom: 0;
                        padding: 0;
                    }

                }

                @media only screen and (max-width: 760px) {
                    margin: 0 0 20px 0;
                }
                @media only screen and (max-width: 410px) {

                    min-width: auto;
                    width: 100%;
                }
            }

            @media only screen and (max-width: 760px) {
                display: flex !important;
                flex-direction: column;
                align-items: center;
                margin-left: 0;
                padding-top: 10px;
            }

            
        }

Solution 13 - Css

This is a limitation of flexbox. The best thing you can do is set a max-width which the wrapping (growing) item will respect. You need CSS Grid for this.

Solution 14 - Css

I am new to frontend and learning flex box, I have attached an example of the image gallery I was trying to build see if it helps.

.main-container {
  display: flex;
  flex-wrap: wrap;
  flex-grow: 1 ;
  justify-content: space-around;
}
h1 {
  text-align: center;
}

img{
  border: 4px solid black;
}

div.gallery:hover {
  border: 10px solid rgb(97, 94, 94);
}

div.desc {
  padding: 15px;
  text-align: center;
}

<!DOCTYPE html>
<html>
  <head>
    <link href="styles.css" rel="stylesheet">
  </head>
  <body>
    <h1>My Image Gallery</h2>
    <div class="main-container">
      <div class="gallery">
        <a target="_blank" href="./assets/cat1.jpg">
          <img src="./assets/cat1.jpg" alt="Image 1" width="400" height="400">
        </a>
        <div class="desc">Add a description of the image here</div>
      </div>


      <div class="gallery">
        <a target="_blank" href="./assets/cat2.jpg">
          <img src="./assets/cat2.jpg" alt="Image 2" width="400" height="400">
        </a>
        <div class="desc">Add a description of the image here</div>
      </div>

      <div class="gallery">
        <a target="_blank" href="./assets/cat3.jpg">
          <img src="./assets/cat3.jpg" alt="Image 3" width="400" height="400">
        </a>
        <div class="desc">Add a description of the image here</div>
      </div>

      <div class="gallery">
        <a target="_blank" href="./assets/cat4.jpg">
          <img src="./assets/cat4.jpg" alt="Image 4" width="400" height="400">
        </a>
        <div class="desc">Add a description of the image here</div>
      </div>

      <div class="main-container">
        <div class="gallery">
          <a target="_blank" href="./assets/cat1.jpg">
            <img src="./assets/cat1.jpg" alt="Image 1" width="400" height="400">
          </a>
          <div class="desc">Add a description of the image here</div>
        </div>
  
  
        <div class="gallery">
          <a target="_blank" href="./assets/cat2.jpg">
            <img src="./assets/cat2.jpg" alt="Image 2" width="400" height="400">
          </a>
          <div class="desc">Add a description of the image here</div>
        </div>
  
        <div class="gallery">
          <a target="_blank" href="./assets/cat3.jpg">
            <img src="./assets/cat3.jpg" alt="Image 3" width="400" height="400">
          </a>
          <div class="desc">Add a description of the image here</div>
        </div>
  
        <div class="gallery">
          <a target="_blank" href="./assets/cat4.jpg">
            <img src="./assets/cat4.jpg" alt="Image 4" width="400" height="400">
          </a>
          <div class="desc">Add a description of the image here</div>
        </div>
    </div>
  </body>
</html>

enter code here

Solution 15 - Css

use max-width: 40px not max-width: 100px

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmiduView Question on Stackoverflow
Solution 1 - CssMerottView Answer on Stackoverflow
Solution 2 - CssOmerView Answer on Stackoverflow
Solution 3 - CssPavellozView Answer on Stackoverflow
Solution 4 - Csszer00neView Answer on Stackoverflow
Solution 5 - Csslili2311View Answer on Stackoverflow
Solution 6 - CssjdnzView Answer on Stackoverflow
Solution 7 - CsshobbeshunterView Answer on Stackoverflow
Solution 8 - CssDaniel DView Answer on Stackoverflow
Solution 9 - CssMiriam SalzerView Answer on Stackoverflow
Solution 10 - CssRobView Answer on Stackoverflow
Solution 11 - CssT.HallamView Answer on Stackoverflow
Solution 12 - CssRon16View Answer on Stackoverflow
Solution 13 - CssAndy CoupeView Answer on Stackoverflow
Solution 14 - CssSumeet RainaView Answer on Stackoverflow
Solution 15 - CssMMJView Answer on Stackoverflow