title: Using Quantity Queries to write content-aware CSS
url: http://www.tomango.co.uk/thinks/using-quantity-queries-to-write-content-aware-css/
hash_url: b70f75a844
The dawn of HTML5 and CSS3 provided a wave of new technologies promising to change the way we build web applications. Web developer reactions to most features were similar, an initial buzz of excitement while we pondered the possibilities brought forth by these shiny new techniques followed by a crushing sense of disappointment when we realised just how long it would be before we could start using them.
Some crest-of-the-wave developers renounced any form of support to older browsers and pressed on with the new features. However, many developers did not have the option to blindly assume that all users ran the latest cutting edge browser, if we were to do that, we’d segregate a significant portion of our users. For this reason, it’s usually a game of patience when it comes to using new frontend features in the wild.
This is why it’s so exciting to discover a feature that is both very useful and can also be used on a live website without large spoonfuls of graceful degradation.
During the brilliant Responsive Day Out 2015, there was a stand-out talk that brought to light a concept that opened many possibilities for modular, content-aware design. In his talk on ‘Solving problems with selectors‘, Heydon Pickering demonstrated Quantity Queries.
Quantity Queries are a way of reacting to differing levels of content with pure vanilla, well supported CSS. They are comprised of the following:
:nth-last-child
selector:first-child
selector~ *
selectorTo get the most out of Quantity Queries, you have to view the CSS selector as an IF statement that will ask a question of the markup and only run if all conditions are met. This more logical view of the selector helps with your decision-making when it comes to laying out specific elements.
The :nth-last-child
selector was added in CSS3 but it hasn’t had a great deal of publicity in comparison to its cool-kid siblings nth-child
and :first-of-type
. Their benefits are immediately obvious, hence the amount of air-time they received. :nth-last-child
has a more subtle set of benefits that have taken longer to discover.
This selector will select the element(s) that meet the criteria passed into the brackets of the selector, working from the bottom of the element stack and counting up.
In its simplest form, a single number can be passed into the brackets to ask the question, ‘Is this element the nth-last element that I’m working on?’. The :nth-last-child(2)
selector will select the penultimate element, the :nth-last-child(4)
selector will select the fourth-last element. Although this is handy, it’s not going to be useful on every project. That is, until it is teamed with a number of other selectors.
We all know and love the :first-child
selector. It’s great for making our opening paragraphs a little larger or hiding borders for the first element in a list. However, this quaint selector becomes a pretty powerful anchor when combined with :nth-last-child
. Remember, our selectors are IF statements and by using :first-child
as the first selector (imagine this as the first parameter in an IF statement), we’re able to filter out all elements that are not the first element.
Now our humble :nth-last-child(4)
selector can be combined to create :first-child:nth-last-child(4)
to create a much more specific selector asking the question ‘Is this element the first-child and the fourth-last element that I’m working on?’. This is getting pretty close to something that could be very useful but it needs one final ingredient to take it to the next level.
This rather bizarre selector uses two less common elements in CSS: the general sibling combinator and the universal selector. It asks the question ‘Does this element come after the current selected element’, effectively selecting all elements after the current element without traversing up an element level, something not currently possible in CSS – very cool! Combined with the previous code, we can write the following:
<div class="code-embed">
<pre class="language-css"><code>:first-child:nth-last-child(4),
:first-child:nth-last-child(4) ~ * {
/* Styles here */
}
</div>
This asks the question 'Is this element the first-child and the fourth-last element that I'm working on OR does this element come after the first-child and the fourth-last element that I'm working on?'.
We have a CSS selector that can check if a container holds exactly 4 elements!
In essence, we have created a content-aware stylesheet rule. That's pretty neat for a simple declarative language like CSS.
As the :nth-last-child
selector works in a similar way to the famous :nth-child
selector, you can pass more than just a single number into the selector. For example, if you pass in n+5, you are able to test whether the container holds five or more elements and style all of the elements accordingly!
The reason I got so excited about this concept was because I could immediately see how useful it could be on the websites we develop. Being able to count the number elements in a container used to be a privilege only extended to JavaScript and server-side languages. The former meant waiting till the page had finished loading before counting the elements and the latter only worked on page load and wouldn't react to client-side changes. Using CSS to count solves both of these issues; it is both dynamic and instantaneous.
In a world of Content Management Systems, the challenge to web developers is to ensure that content looks great on every device, regardless of the type and quantity of content that's been provided. Quantity Queries help towards this reactive goal by helping you to tidy up the page if too much or too little content has been added to a container.
I used Quantity Queries on a recent build to change the way that tags would display depending on the total number. This subtle enhancement only kicked in if there was five or more tags and it neatened up what would've been a long list. Using the following simple Quantity Query, I was able to swap from a list to a brickwork-like structure with ease:
<div class="code-embed">
<pre class="language-css"><code>.tag {
display: block;
}
.tag:first-child:nth-last-child(n+5), .tag:first-child:nth-last-child(n+5) ~ * {
display: inline-block;
}
</div>
The finished result
I put Quantity Queries to use on a template that required a flexible grid system to display a list of projects and news items. My aim to was to ensure that no orphaned elements would be left at the end of the list, regardless of how many items were in the system. Using a more complex set of selectors, I was able to create an effective grid system.
<div class="code-embed">
<pre class="language-css"><code>.panel:nth-child(3n),
.panel:nth-child(3n+1):last-child, .panel:nth-child(6n+1):nth-last-child(4), .panel:nth-child(6n+1):nth-last-child(4) ~ .panel, .panel:nth-child(6n+4):nth-last-child(4), .panel:nth-child(6n+4):nth-last-child(4) ~ .panel {
width: 25%;
}
.panel:nth-child(6n+1), .panel:nth-child(6n+6), .panel:nth-child(6n+2):last-child, .panel:nth-child(6n+4):nth-last-child(2), .panel:nth-child(6n+4):nth-last-child(2) ~ .panel {
width: 50%;
}
</div>
Working with Quantity Queries is a lot of fun. They give new possibilities for modular element design and they do a brilliant job of tidying up content extremes making your projects more flexible and future-proof.