Responsive layout with an unknown number of elements solved by Flexbox

Lais Varejão
April 19, 2017
<blockquote>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</blockquote><p>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.</p><p>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.</p><p>To put this in picture, this is what the container would look like with 3 columns in a large device:<br></p><figure class="kg-card kg-image-card"><img src="https://vinta-cms.s3.amazonaws.com/media/filer_public/0a/68/0a680226-748d-445c-9040-4dd209ef68c0/lg-device.png" class="kg-image" alt="large-device"></figure><p>In a medium device:<br></p><figure class="kg-card kg-image-card"><img src="https://vinta-cms.s3.amazonaws.com/media/filer_public/3c/7a/3c7ab37f-c8e2-40de-b799-4625d872c662/md-device.png" class="kg-image" alt="medium-device"></figure><p>And in a small device:<br></p><figure class="kg-card kg-image-card"><img src="https://vinta-cms.s3.amazonaws.com/media/filer_public/7f/20/7f20ddbb-12f8-4031-9214-cf89dfd9e4a7/sm-device.png" class="kg-image" alt="small-device"></figure><p>Using Flexbox, you can start solving this problem by wrapping the items in the container with <code>flex-wrap</code>, setting your base width with <code>flex-basis</code> and then filling the space with <code>flex-grow</code>. This way:</p><pre><code class="language-css">.container { display: flex; flex-wrap: wrap; } .item { flex-grow: 1; flex-basis: 300px; margin: 10px; } </code></pre><p>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:<br></p><figure class="kg-card kg-image-card"><img src="https://vinta-cms.s3.amazonaws.com/media/filer_public/ab/e4/abe498e9-f27f-44f0-b040-2aeb7afbd2c2/undesired-result.png" class="kg-image" alt="undesired-result"></figure><p><br>The last element is left alone in the row and it grows to fill the container, because of the <code>flex-grow</code> property. Not quite what we want, because all items don't have the same width now.</p><p>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: <code>max-#-of-columns - 1</code>. This way, the empty columns are always filled.</p><pre><code class="language-css">.item:empty { height: 0; } </code></pre><pre><code class="language-html">&lt;div class="container"&gt; &lt;div class="item"&gt;A&lt;/div&gt; &lt;div class="item"&gt;B&lt;/div&gt; &lt;div class="item"&gt;C&lt;/div&gt; &lt;div class="item"&gt;D&lt;/div&gt; &lt;div class="item"&gt;&lt;/div&gt; &lt;div class="item"&gt;&lt;/div&gt; &lt;/div&gt; </code></pre><p>Resulting in this:<br></p><figure class="kg-card kg-image-card"><img src="https://vinta-cms.s3.amazonaws.com/media/filer_public/f4/dc/f4dc140e-d19b-49a2-bb5f-4812daebf6d7/desired-result.png" class="kg-image" alt="desired-result"></figure><p>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.</p><p>Have a better solution? I'd love to know. Please comment down below!</p><p>References:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Using_CSS_flexible_boxes">https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Using_CSS_flexible_boxes</a></li><li><a href="https://www.codeschool.com/blog/2016/10/12/why-flexbox/">https://www.codeschool.com/blog/2016/10/12/why-flexbox/</a></li><li><a href="https://css-tricks.com/filling-space-last-row-flexbox/">https://css-tricks.com/filling-space-last-row-flexbox/</a></li><li><a href="https://www.toptal.com/front-end/how-to-build-css-only-smart-layouts-with-flexbox">https://www.toptal.com/front-end/how-to-build-css-only-smart-layouts-with-flexbox</a></li><li><a href="https://philipwalton.github.io/solved-by-flexbox/">https://philipwalton.github.io/solved-by-flexbox/</a></li><li><a href="http://tutorialzine.com/2017/03/css-grid-vs-flexbox/">http://tutorialzine.com/2017/03/css-grid-vs-flexbox/</a></li></ul>