44

I want to create this layout:

enter image description here

When an item doesn't fit in the container, we can move to the next line:

enter image description here

When the container is tiny than the wider item, we can wrap the content in multilines

enter image description here

It's very easy with Javascript, here is the example https://jsfiddle.net/oucxsep4/.

var choices = document.querySelectorAll('li');
var maxWidth = 0;
// read
for (i = 0; i < choices.length; ++i) {
    maxWidth = Math.max(maxWidth, choices[i].offsetWidth)
};

// write
for (i = 0; i < choices.length; ++i) {
    choices[i].style.width = maxWidth + "px";
};
ul{
    margin: 0;
    padding: 0;
    list-style: none;
}
li{
    background: red;
    float: left;
    margin: 5px;
}
<ul>
    <li>first choice</li>
    <li>choice</li>
    <li>This is the wider choice</li>
    <li>other choice</li>
</ul>

Is it possible to do it without using Javascript, only CSS? I have tried with flexbox without success.

Hidden Hobbes
  • 13,893
  • 3
  • 38
  • 64
Carlos
  • 589
  • 1
  • 5
  • 9
  • 1
    Flexbox has no grid notion. Items at different flex lines behave independently, so I don't think you can achieve this with flexbox. – Oriol Jul 02 '15 at 16:12

3 Answers3

16

It is possible using simple css:

.list-container {
  display: inline-flex;
  flex-direction: row;
  justify-content: center;
}

.list {
  display: flex;
  flex-direction: column;
}

.list-item {
  text-transform: capitalize;
  background-color: rgb(200, 30, 40);
  font-size: 1.3em;
  text-align: left;
  padding: 10px;
  margin: 1px;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-start;
}
<!DOCTYPE html>

<div class="list-container">
  <div class="list">
    <div class="list-item">fresh figs</div>
    <div class="list-item">pine nuts</div>
    <div class="list-item">honey</div>
    <div class="list-item">balsamic vinegar</div>
  </div>
</div>
<div class="list-container">
  <div class="list">
    <div class="list-item">fresh figs</div>
    <div class="list-item">pine nuts</div>
    <div class="list-item">honey</div>
    <div class="list-item">balsamic vinegar</div>
  </div>
</div>
GKFX
  • 1,386
  • 1
  • 11
  • 30
Ericky
  • 187
  • 1
  • 5
  • It does, you just need to have an extra column like this. (Look at the editted version) Each list takes the width of the largest element – Ericky Mar 08 '17 at 11:54
  • Any way to make this dynamic in terms of the number of columns? The provided solution is not overly useful in any sort of dynamic environment. I was hoping I could just put everything in one list and set a maximum height on the list to have it wrap to multiple columns but then the sizes of everything would need to adjust automatically so they are uniform. – Newclique Mar 07 '18 at 17:52
  • 2
    @Ericky I think what Carlos is trying to show in that screenshot is that the original layout displays the 4 items in 2 columns, but if any one of the items are too large, then all items resize and display as 1 column. – Lani Jul 03 '18 at 00:16
  • The answers here are hacks and/or obsolete. Closed and re-directed to a modern Grid solution. – Michael Benjamin Jul 03 '22 at 15:24
4

It is not yet possible with CSS alone to match all sibling elements widths to the widest one. However, you can achieve most of your desired layout with CSS by giving your list items a width of 50% to create the two column structure (width: calc(50% - 10px /* subtracts margins */);) and then also give them a minimum width (min-width:153px; in this example).

If you are not able to manually set a minimum width in the CSS then you will likely have to supplement your CSS with some javascript to set the minimum width for those sibling elements similar to your example.

ul{
    margin: 0;
    padding: 5px;
    list-style: none;
    width:50%;
    background-color: #eee;
}

ul::after {
    clear: both;
    content: "";
    display: block;
}

li {
    background: red none repeat scroll 0 0;
    display: block;
    float: left;
    margin: 5px;
    min-width: 153px;
    width: calc(50% - 10px);
}
<ul>
    <li>first choice</li>
    <li>choice</li>
    <li>This is the wider choice</li>
    <li>other choice</li>
</ul>
IMI
  • 2,461
  • 1
  • 14
  • 20
  • 2
    Thanks for the answer. Is similar to the desire layout but not exactly. The number of columns should be dynamic. – Carlos Jul 09 '15 at 13:14
2

I have an ugly solution

https://jsfiddle.net/y5x3znqo/2/

body {
  background: #ccc
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

li {
  background: red;
  float: left;
  margin: 5px;
}

.label {
  position: absolute;
}

.hide {
  visibility: hidden;
}
<ul>
  <li>
    <span class="label">first choice</span>
    <span class="hide">This is the wider choice</span>
  </li>
  <li>
    <span class="label">choice</span>
    <span class="hide">This is the wider choice</span>
  </li>
  <li>
    <span>This is the wider choice</span>
  </li>
  <li>
    <span class="label">other choice</span>
    <span class="hide">This is the wider choice</span>
  </li>
</ul>

The idea is put the widest item in every choice with visibility:hidden. This requires to precalculate the widest item, for example in the backend.

GKFX
  • 1,386
  • 1
  • 11
  • 30
Carlos
  • 589
  • 1
  • 5
  • 9