5

I have a list of items of variable width inside of a wrappable flex box.

I want all items to have the same width, so I applied the following CSS attributes: flex-basis: 0 and flex-grow: 1. With that, each item in every column should have had the same width, independently from its original width.

This was the expected result, which can be achieved by using flex-basis: 30%.

This

Unfortunately, this won't work in all cases (if the width of each item is small, there will be a lot of empty space).

Using a fixed width does not solve the issue, because each item can occupy different amounts of space.

This can help illustrate de problem, the "large items" create 5 rows (width of 33% per item) while the "small items" create only 3 rows (width of 20% per item).

This

I'm also not allowed to use JavaScript.

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

li {
    white-space: nowrap;
    flex-basis: 0;
    flex-grow: 1;
}
<ul>
    <li>Short name</li>
    <li>Short name</li>
    <li>Short name</li>
    <li>Short name</li>
    <li>Short name</li>
    <li>Short name</li>
    <li>Short name</li>
    <li>Short name</li>
    <li>Long long long name</li>
    <li>Short name</li>
    <li>Short name</li>
    <li>Long long long name</li>
    <li>Long long long name</li>
    <li>Long long long name</li>
    <li>Long long long name</li>
    <li>Short name</li>
    <li>Short name</li>
    <li>Long long long name</li>
    <li>Long long long name</li>
    <li>Short name</li>
    <li>Short name</li>
</ul>

Edit: I had a misconception about the way flexboxes work. The solution here is to use an appropriate value for flex-basis.

Sandro Marques
  • 89
  • 1
  • 2
  • 10
  • You gonna need `inline-flex`, `column` direction and fixed height on the parent to accomplish that: https://jsfiddle.net/qLzca963/ – Asons Mar 12 '18 at 19:03
  • 1
    It partially solves the problem, @LGSon. But what if I don't know the height beforehand? It just overflows if there are a lot of items. – Sandro Marques Mar 12 '18 at 19:09
  • Then the only way is with script. Do note, there is no other way to allow for a column to size by its smallest item, unless you wrap them in 3 columns by changing the markup. – Asons Mar 12 '18 at 19:10

2 Answers2

9

I want all items to have the same width, so I applied the following CSS attributes: flex-basis: 0 and flex-grow: 1.

That's a wrong approach. With flex-grow you are distributing free space in the container. Since the length of the content is different in each item, the free space will be different in each line. Hence, the columns won't align across rows, despite each item having flex-basis: 0.

With that, each item in every column should have had the same width, independently from its original width.

Actually, per the info I posted above, "With that, each item in every row should have had the same width, independently from its original width."

ul {
  display: flex;
  flex-wrap: wrap;
  margin: 0;
  padding: 0;
}

li {
  flex-basis: 0;
  flex-grow: 1;
  white-space: nowrap;
  list-style-type: none;
  border: 1px dashed red;
}
<ul>
  <li>Long name</li>
  <li>Long name</li>
  <li>Short name</li>
  <li>Short name</li>
  <li>Short name</li>
  <li>Short name</li>
  <li>Short name</li>
  <li>Long name</li>
  <li>Long long long name</li>
  <li>Long name</li>
  <li>Long name</li>
  <li>Long long long name</li>
  <li>Long long long name</li>
  <li>Long long long name</li>
  <li>Long long long name</li>
  <li>Short name</li>
  <li>Long name</li>
  <li>Long long long name</li>
  <li>Long long long name</li>
  <li>Long name</li>
  <li>Short name</li>
</ul>

You also need to keep in mind that a flex item, by default, cannot shrink past the size of its content, despite flex-basis: 0. See here: Why don't flex items shrink past content size?

Here's a solution that may be useful:

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

li {
  flex: 1 0 26%; /* see note below */
  white-space: nowrap;
}
<ul>
  <li>Long name</li>
  <li>Long name</li>
  <li>Short name</li>
  <li>Short name</li>
  <li>Short name</li>
  <li>Short name</li>
  <li>Short name</li>
  <li>Long name</li>
  <li>Long long long name</li>
  <li>Long name</li>
  <li>Long name</li>
  <li>Long long long name</li>
  <li>Long long long name</li>
  <li>Long long long name</li>
  <li>Long long long name</li>
  <li>Short name</li>
  <li>Long name</li>
  <li>Long long long name</li>
  <li>Long long long name</li>
  <li>Long name</li>
  <li>Short name</li>
</ul>

Note regarding flex-basis: 26%:

With flex-grow: 1 defined in the flex shorthand (see li), there's no need for flex-basis / width to be 33.33%, which could result in two items per row if you decide to add vertical margins.

Since flex-grow will consume free space on the row, flex-basis only needs to be large enough to enforce a wrap. In this case, with flex-basis: 26%, there's plenty of space for margins, the row is filled precisely, and there is not enough space for a fourth item.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
0

You can use specific width:

li{
   flex: 0 0 20%;
}

Or

li{
   flex: 1 0 0;
}
RedRex
  • 311
  • 1
  • 5