Jumping through hoops with the golden flexbox hammer

May 16, 2016

I’ve been a big proponent of using Flexbox for a while, especially since hearing Zoe Mickley Gillenwater speaking about it at Smashing Conference Oxford 2014.

In particular, I use justify-content: space-between a lot. But one issue with it is what happens in the last row. If the number of child items doesn’t divide nicely into the number of items per row, there will be a big gap between them, as you can see from this Codepen example:

See the Pen space-between by malcomio (@malcomio) on CodePen.

It can look pretty ugly, especially if the parent element is wide. One possible solution is to have the items in the last row fill the available space. But for the tiles layout on the Gallery Guide, that wouldn’t work - it would make the last row items much too big. Ideally, the last row would be given a different behaviour - perhaps using a different justify-content value, perhaps using floats, but as far as I’m aware, there isn’t a nice CSS way to achieve this.

The suggestion I found on StackOverflow is to add extra elements. Given that the rows are being generated by a Drupal view, we can achieve this using a preprocess function, adding dummy rows, which don’t affect small screens because their height is set to zero.

Here’s a Codepen example showing the idea:

See the Pen space-between with dummy rows by malcomio (@malcomio) on CodePen.

The relevant views all use the unformatted list format, so in the implementation of template_preprocess_views_view_unformatted we add a variable to say how many extra rows are needed to make it fit nicely:

define('GALL_VIEWS_ITEMS_PER_ROW', 4);

/**
 * Implements template_preprocess_views_view_unformatted().
 */
function gall_preprocess_views_view_unformatted(&$variables) {
  // Add dummy rows so that flexbox looks nice.
  $view_id = $variables['view']->id();
  $tiles_views = _gall_tiles_views();
  if (in_array($view_id, $tiles_views)) {
    $remainder = count($variables['view']->result) % GALL_VIEWS_ITEMS_PER_ROW;
    $rows_to_add = GALL_VIEWS_ITEMS_PER_ROW - $remainder;
    if ($remainder && $rows_to_add) {
      $variables['extra_rows'] = $rows_to_add;
    }
  }
}

Once we’ve added this counter, we can use it to create a loop in our views-view-unformatted.html.twig template:

{% if extra_rows %}
  {% for i in 1..extra_rows %}
    <div class="views-row dummy-row"></div>
  {% endfor %}
{% endif %}

And, as if by magic, the view rows are aligned left. Problem solved.

But maybe the problem was one of my own making. Even before I’d finished building this, I was realising that maybe it would have been easier to just use floats. To paraphrase Abraham Maslow, or perhaps Abraham Kaplan, someone who has just discovered a hammer will always be looking for nails. As always, there’s another way I could have solved this, and the new way isn’t always better than the old way. Having said that, I do like the way that flexbox helps to keep my margins tidy…