Equal Width Flex Items Even After They Wrap

Equal width flex items even after they wrap

Currently, flexbox offers no clean solution for aligning flexible items in the last row or column. It's beyond the scope of the current spec.

Here's more information and various solutions people have used to get around the problem:

  • Targeting flex items on the last row
  • Is it possible for flex items to align tightly to the items above them?

However, last-row alignment is not a problem with another CSS3 technology, Grid Layout. In fact, it's very simple with this method (and requires no changes to the HTML):

.container {  display: grid;  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));  grid-auto-rows: 20px;  grid-gap: 5px;}
.item { background: yellow; text-align: center; border: 1px solid red;}
<div class="container">  <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 class="item">7</div>  <div class="item">8</div>  <div class="item">9</div>  <div class="item">10</div></div>

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

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

equal width flex items

The problem you're having is mostly the third item on the second row.

An initial setting on flex items is min-width: auto, which means the item will have a minimum width based on its content. This could be a fixed-width element or, in your case, the longest word.

Since your content in that item has one long string of text with no space characters, it's forcing the item to expand, throwing off the column alignments.

Simply use a space character to break that string of text and the issue is resolved.

I would also set the item widths to 20% and override the min-width: auto
default for more stability.

More details here: Why doesn't flex item shrink past content size?

* {  box-sizing: border-box;}
.container { display: flex; flex-direction: column; text-align: center;}
.first,.second { display: flex; /* flex-wrap: wrap; <-- removed for demo */}
p { padding: 1em .5em; flex: 0 0 20%; /* adjustment */ overflow: hidden; /* adjustment */ border: 1px dashed green;}
p::before { content: '✔';}
<div class="container">  <div class="first">  <p>  Lorem ipsum dolor sit amet imsppmfmmfmfmfmfmfmfm</p>  <p>  Lorem ipsum dolor sit ametlorem lorem lorem lorem lorem lorem, </p>  <p>  Lorem ipsum dolor sit amet,</p>  <p>  Lorem ipsum dolor sit amet, lorem ipsum</p>  <p>  Lorem ipsum dolor sit amet,</p>  </div>    <div class="second">  <p>  Lorem ipsum dolor sit amet,</p>  <p>  Lorem ipsum dolor sit amet,</p>  <p>  Lorem ipsum dolor sit amet, jkdasnjdcmx,cnc,mncn,mn,mcmn</p>  <p>  Lorem ipsum dolor sit amet, </p>  <p>  Lorem ipsum dolor sit amet,dddddd </p>  </div></div>  

Equal size of flex item while breaking the last item to next line

You should set flex-grow:0 and should be calc(100% / 6) instead of calc(100% / 7). Also I added box-sizing:border-box; for borders to be included in width.

ul {  padding: 0;  margin: 0;  list-style-type: none;  display: flex;  flex-flow: row wrap;}
li { flex: 1; border: solid 1px; text-align: center; flex-basis: calc(100% / 6); box-sizing: border-box;}
.keep-next-line { flex-grow: 0;}
<ul>  <li>1</li>  <li>2</li>  <li>3</li>  <li>4</li>  <li>5</li>  <li>6</li>  <li class="keep-next-line">7</li>  <!--Kepp this next line, but size should be equal to other item--></ul>

How to make flexbox items the same size?

Set them so that their flex-basis is 0 (so all elements have the same starting point), and allow them to grow:

flex: 1 1 0px

Your IDE or linter might mention that the unit of measure 'px' is redundant. If you leave it out (like: flex: 1 1 0), IE will not render this correctly. So the px is required to support Internet Explorer, as mentioned in the comments by @fabb;

Flex: wrapped items with same width as the rest

Add a few extra flex items and make them "invisible" by setting their height/padding/border to 0/none.
Based on how many columns you'll need, it takes one less for it to work.

* {  box-sizing: border-box;}
.wrapper { border: 1px solid red; width: 100%; max-width: 800px;}
ul { list-style: none; margin: 0; padding: 0;
display: flex; flex-wrap: wrap; margin: -1em -1em 0 0;}
li { border: 1px solid black; padding: 1em; margin: 1em 1em 0 0; flex: 1 1 10em;}
li:nth-last-child(-n+3) { height: 0; border: none; padding: 0; margin: 0 1em 0 0;}
<!DOCTYPE html><html>
<head> <link rel="stylesheet" href="style.css"> <script src="script.js"></script> </head>
<body> <div class="wrapper"> <ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> <li>Item 4</li> <li>Item 5</li> <li>Item 6</li> <li>Item 7</li> <li>Item 8</li> <li></li> <li></li> <li></li> </ul> </div> </body>
</html>

My 25% width flex item is taking 100% width after wrapping

You have the items set to flex: 1 0 25%.

This breaks down to:

  • flex-grow: 1
  • flex-shrink: 0
  • flex-basis: 25%

Remove flex-grow: 1. It's telling the items to consume free space.

Try this: flex: 0 0 25%

For more details and other options, see these posts:

  • Equal width flex items even after they wrap
  • Flexbox: 4 items per row

Flex-wrap with equal width

You can use map() and get() to return array of widths and then with Math.max you can find maximum value in that array and apply it as width on each item

var w = $('.narrow > div').map(function() {  return $(this).width();}).get();
var max = Math.max.apply(null, w);$('.narrow > div').width(max);
.wrap {  width: 300px;  background: #fff;}.narrow {  display: flex;  flex-wrap: wrap;}.narrow >* {  background: #ccc;  border: 2px solid #fff;  padding: 10px;  color: #fff;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><div class="wrap">  <div class="narrow">    <div class="item1">      Longest sentence.    </div>    <div class="item2">      Short text.    </div>    <div class="item3">      Another text.    </div>    <div class="item1">      Longest sentence.    </div>    <div class="item2">      Short text.    </div>    <div class="item3">      Another text.    </div>  </div></div>

Make flex items have equal width in a row

Flexbox method

In order to make the text items (.section-child) equal width, you need to use flex: 1 1 0, which you have done. This is the same as saying flex: 1.

However, this by itself doesn't achieve the goal for two reasons:

  1. The parent of .section-child, a flex container, but also a flex item in a larger container, is limited to the width of its content, by default. So it won't expand and the text can overflow the container. You need to apply flex: 1 to .section, as well.

  2. A flex item cannot be smaller than the size of its content, by default. The initial setting is min-width: auto. So flex: 1 cannot work to equally distribute container space, because a flex item cannot shrink past the longest item. You need to override this behavior with min-width: 0.

.top-level {  display: flex;  flex-flow: row wrap;}
.section { display: flex; flex-flow: row nowrap; border: 1px solid; margin-right: 12px; margin-top: 12px; flex: 1; min-width: 0;}
.section-child { display: flex; flex-flow: column nowrap; align-items: center; flex: 1; min-width: 0;}
.child-title { white-space: nowrap;}
.vertical-separator { width: 1px; background-color: rgba(0, 0, 0, 0.3); margin: 8px;}
<div class="top-level">  <section class="section">    <div class="section-child">      <h4 class="child-title">Title</h4>      <!--A lot more content here-->    </div>    <div class="vertical-separator"></div>    <div class="section-child">      <h4 class="child-title">Longer title</h4>      <!--A lot more content here-->    </div>    <div class="vertical-separator"></div>    <div class="section-child">      <h4 class="child-title">Much much longer title</h4>      <!--A lot more content here-->    </div>  </section>  <section class="section">    <div class="section-child">      <h4 class="child-title">Title</h4>      <!--A lot more content here-->    </div>    <div class="vertical-separator"></div>    <div class="section-child">      <h4 class="child-title">Longer title</h4>      <!--A lot more content here-->    </div>    <div class="vertical-separator"></div>    <div class="section-child">      <h4 class="child-title">Much much longer title</h4>      <!--A lot more content here-->    </div>  </section>  <section class="section">    <div class="section-child">      <h4 class="child-title">Title</h4>      <!--A lot more content here-->    </div>    <div class="vertical-separator"></div>    <div class="section-child">      <h4 class="child-title">Longer title</h4>      <!--A lot more content here-->    </div>    <div class="vertical-separator"></div>    <div class="section-child">      <h4 class="child-title">Much much longer title</h4>      <!--A lot more content here-->    </div>  </section></div>


Related Topics



Leave a reply



Submit