Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

css

flexbox

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.

like image 853
midu Avatar asked Apr 24 '14 16:04

midu


People also ask

How do you align Flex items in a row?

By default items start from the left if flex-direction is row , and from the top if flex-direction is column . You can change this behavior using justify-content to change the horizontal alignment, and align-items to change the vertical alignment.

How do I make my flex item not stretch width?

By default, the child elements of a flexbox container will stretch vertically to fill the height of the container. This can be prevented by using the align-self property on the child element that you do not want to stretch. This Pen is owned by dev on CodePen.

How do you set the width of a flex item?

A flexbox item can be set to a fixed width by setting 3 CSS properties — flex-basis, flex-grow & flex-shrink. flex-basis : This property specifies the initial length of the flex item. flex-grow : This property specifies how much the flex item will grow relative to the rest of the flex items.


2 Answers

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 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

like image 77
Merott Avatar answered Oct 17 '22 02:10

Merott


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>
like image 33
Omer Avatar answered Oct 17 '22 01:10

Omer