Quantity queries and Flexbox part 2

A while ago, after reading Heydon Pickering's article on quantity queries, I had a go at it myself. I wrote a post on how we can use quantity queries in CSS to alter the width of items in a grid depending on their position in the grid. The idea is to fill the entire grid space, even if there isn't enough items to completely fill it (e.g. if you have 7 items in a 3 column grid, you'll get 2 empty spaces at the end, so we can use quantity queries to add new width styles to the last two items). I used Flexbox to make sure all of the items in the grid have equal heights.

Since writing that post I've realised that I didn't use Flexbox or nth-child to their full potential. The CSS can be written more efficiently.

In the previous post I looked at a three column grid where I didn't know how many items would eventually be in the grid. However, I wanted the grid to be balanced (no empty spaces).  I looked at all the possible total numbers of items which could lead to an unbalanced grid (2,5,8,11) and (1,4,7,10) and found the nth-child values which would target those numbers. Then I said if the last child is in one of the above sequences, target the first child and a sibling.


li:nth-last-child(3n-1):first-child,
li:nth-last-child(3n-1):first-child + li {
   width: 50%;
   color: pink;
}
li:nth-last-child(3n-2):first-child,
li:nth-last-child(3n-2):nth-child(4) {
   width: 66.6%;
   color: gold;
}

This works fine, but there's a more efficient way to do it. Thanks to Keith Clark's post, I've recently seen that we can target the entire last row of a grid to add new styles. The CSS looks like this:


li:nth-child(3n+1):nth-last-child(-n+3) {
   color: deeppink;
}

nth-child(3n+1) targets the first item in every row. nth-last-child counts backwards from the last item in the grid. Therefore nth-last-child(-n+3) will target items in position 3, 2 and 1 from the end of the grid. So, if we join the two together we have two conditions. Only target an item which is in position (3n+1) and in position 3,2 or 1 from the end of the grid. This results in the first item in the last row being selected, like this:

Target first item in last row

This is great. Now we can target the rest of the items in the last row using the general sibling selector in CSS ~ like this:


li:nth-child(3n+1):nth-last-child(-n+3),
li:nth-child(3n+1):nth-last-child(-n+3) ~ li {
    color: deeppink;
}

all-items-last-row.png#asset:472The great thing is that it doesn't matter how many items are in the last row. The general sibling selector will target all of the siblings regardless of how many there are.

So, now we've targeted all the items in the last row, we can use flexbox to style them. Flexbox can make items fill the width of their container, so we don't have to calculate all the widths ourselves (like I did before). We can apply flex-grow to the items so that they fill the entire space between them.


li:nth-child(3n+1):nth-last-child(-n+3),
li:nth-child(3n+1):nth-last-child(-n+3) ~ li {
    color: deeppink;
    flex-grow: 1;
}

I love that Flexbox does all the maths for us so we don't have to.

Here's how the grid looks when it is one item short of a balanced grid.

flex-grow-last-row.png#asset:473

Here's how it looks when the grid is two items short of a balanced grid:

flex-grow-last-row-1.png#asset:474The grid doesn't have to have three columns. You can rewrite it for four or five column grids as well. For a four column grid, we can target the first item in every row like this:


li:nth-child(4n+1):nth-last-child(-n+4),
li:nth-child(4n+1):nth-last-child(-n+4) ~ li {
    color: deeppink;
    flex-grow: 1;
}

And for a five column grid:


li:nth-child(5n+1):nth-last-child(-n+5),
li:nth-child(5n+1):nth-last-child(-n+5) ~ li {
    color: deeppink;
    flex-grow: 1;
}

You get the idea.

I love that the grid will always look balanced while accommodating any number of items, all with minimal effort from me.

The full example is on CodePen.