Blog

Ideas and insights from our team

Responsive layout with an unknown number of elements solved by Flexbox


The CSS3 Flexible Box, or Flexbox, is a layout mode providing for the arrangement of elements on a page such that the elements behave predictably when the page layout must accommodate different screen sizes and different display devices. For many applications, the flexible box model provides an improvement over the block model in that it does not use floats, nor do the flex container's margins collapse with the margins of its contents.

-- Mozilla Developer Network

Elements distributed through columns and equal height rows are a common UI pattern which can be easily solved using Flexbox. Although simple, you might face a few problems when approaching this issue. This blog post will explain one of them.

Let's suppose you have a responsive container with an unknown number of elements. The number of elements may vary and you wish to display them in N columns for large media devices. The items have a base width, but you want them to adjust to fill the space of the row. What this means is that the items can grow to fill in blank spaces, but they cannot shrink. This way, when resizing the window, the number of columns may change to fit the max number of elements respecting the base width. Now, a very important requirement is that, although growing, all items must have the same width.

To put this in picture, this is what the container would look like with 3 columns in a large device: large-device

In a medium device: medium-device

And in a small device: small-device

Using Flexbox, you can start solving this problem by wrapping the items in the container with flex-wrap, setting your base width with flex-basis and then filling the space with flex-grow. This way:

.container {
  display: flex;
  flex-wrap: wrap;
}

.item {
  flex-grow: 1;
  flex-basis: 300px;
  margin: 10px;
}

If the number of elements is a multiple of the number of columns, then this solution will work just fine. However, because we don't know the number of elements, we cannot guarantee this and we might end up with something like this: undesired-result The last element is left alone in the row and it grows to fill the container, because of the flex-grow property. Not quite what we want, because all items don't have the same width now.

So, how do we solve this? A workaround is to insert a number of extra ghost elements of zero height. To be more precise, the number of ghost elements would always be: max-#-of-columns - 1. This way, the empty columns are always filled.

.item:empty {
  height: 0;
}
<div class="container">
  <div class="item">A</div>
  <div class="item">B</div>
  <div class="item">C</div>
  <div class="item">D</div>  
  <div class="item"></div>   
  <div class="item"></div>  
</div>

Resulting in this: desired-result

There, a simple solution with a few lines of CSS/HTML. Although this does work and we were able to address the issue, it's important to point out the solution is controversial, because we are polluting the HTML with unnecessary elements. The truth is, there isn’t a better system – both flexbox and the CSS grid are good at different things and should be used together, not as alternatives to one another.

Have a better solution? I'd love to know. Please comment down below!

References:

About Lais Varejão

Software developer at Vinta Software.

Comments