Illustration by

Quantity Queries for CSS

Don’t you just hate documentaries that don’t deliver? They have enticing names like In Search of the Giant Squid, and tease you with shots of murky underwater shapes and excited scientists pointing far out to sea. You settle down to watch, eyes narrowed with suspicion, thinking, “I better see some squid or I’m writing an angry letter to the network.”

Article Continues Below

Sure enough, 90 minutes of interviews with bored-looking fishermen later, the presenter is forced to conclude, “No… no, we didn’t find any big squids. But maybe one day [majestic orchestral flourish].” Great. You wanted Finding Nemo and got Not Finding Nemo instead.

I wouldn’t do that to you, friends. This is your guide to creating style breakpoints for quantities of HTML elements, much as you already do with @media queries for viewport dimensions. I’m not pointing at some blurry specification in the distance or a twinkle in an implementer’s eye. We’re going to do this today, with CSS that’s already available.

Dynamic content#section1

Responsive web design is primarily concerned with one variable: space. In testing responsive layouts, we take an amount of content and see which spaces it will successfully fit into. The content is deemed constant; the space, variable.

The @media query is the darling of responsive web design because it allows us to insert “breakpoints” wherever one layout strategy ceases to be viable and another should succeed it. However, it’s not just viewport dimensions, but the quantity of content that can put pressure on space.

Just as your end users are liable to operate devices with a multitude of different screen sizes, your content editors are liable to add and remove content. That’s what content management systems are for. This makes Photoshop mockups of web pages doubly obsolete: they are snapshots of just one viewport, with content in just one state.

In this article, I will be outlining a technique to make CSS quantity-aware using specially formed selectors. I will be applying these selectors to one classic problem in particular: how to alter the display of items in a horizontal navigation menu when there are too many to suit the initial layout mode. That is, I will demonstrate how to switch from a display: table-cell to a display: inline-block layout when the number of items in the menu becomes “more than or equal to 6.”

I will not be relying on any JavaScript or template logic, and the menu’s list markup will remain devoid of class attribution. By using CSS only, the technique honors the separation of concerns principle, according to which content (HTML) and presentation (CSS) have clearly defined roles. Layout is CSS’s job and, where possible, CSS’s only.

Comparing the initial menu bar layout for fewer than six items with the layout for six or more items

The demonstration is available on CodePen and will be referred to throughout the article.

To help me illustrate this qualification of quantity, I’ll be employing diagrams of squids in the article to represent HTML elements. Green squids with ticks represent elements that match the CSS selector in question, red squids with crosses are unselected elements, and grayed-out squids denote elements that don’t exist.

A key for the three squid symbols to be used in following diagrams. A green squid (for selected elements), a red squid (for unselected elements) and a grey squid for elements that don't exist

Counting#section2

The key to determining the quantity of elements in a given context is to count them. CSS doesn’t provide an explicit “counting API,” but we can solve the same problem with an inventive combination of selectors.

Counting to one#section3

The :only-child selector provides a means to style elements if they appear in isolation. Essentially, it lets us “style all the child elements of a particular element, if counting those children returns 1 as the total.” Aside from its stablemate :only-of-type, it is the only simple selector that can be described as quantity-based.

In the following example, I use :only-of-type to add a special style to any buttons that are the only elements of their element type among sibling elements. I give these lone buttons an increased font-size because singularity suggests importance.


button { 
	font-size: 1.25em;
}

button:only-of-type {
	font-size: 2em;
}

Here’s the crucial part. If I were to start out with one button, replete with a larger font size, and add buttons before or after it, each button would then adopt a smaller font size. The style of all the elements in the set is dependent on a quantity threshold of two: if there are “fewer than two” elements, the larger font size is honored. Take a look at that code again with the “fewer than two” notion in mind:


button {
	font-size: 1.25em;
}

button:only-of-type {
	font-size: 2em;
}

The fewer than two logic means one selected element (green squid) becomes two unselected elements (red squids) when an element is added

If it feels more natural, you can turn the CSS logic on its head using negation and make the condition “more than one.”


/* "More than one" results in a smaller font size */
button {
	font-size: 2em;
}

button:not(:only-of-type) {
	font-size: 1.25em;
}

The more than one logic means one unselected element (red squid) becomes two selected elements (green squids) when an element is added

Quantity n#section4

Styling elements based on the “more than one” and “fewer than two” thresholds is a neat trick, but a flexible “quantity query” interface would accept any quantity. That is, I should be able to style “more than or equal to n” for any value of n. Then I can style “more than or equal to 6” in our navigation menu.

With a view to achieving this final goal, what if I were able to style discrete quantities like “exactly 6 in total” or “exactly 745”? How would I go about that? I would need to use a selector that allowed me to traverse sets of elements of any quantity numerically.

Fortunately, the :nth-last-child(n) selector accepts the number “n”, enabling me to count sets of elements from the end of the set toward the beginning. For example, :nth-last-child(6) matches the element that is sixth from last among sibling elements.

Things get interesting when concatenating :nth-last-child(6) with :first-child, introducing a second condition. In this case, I am looking for any element that is both the sixth element from the end and the first element.


li:nth-last-child(6):first-child {
	/* green squid styling */
}

If this element exists, the set of elements must be exactly six in quantity. Somewhat radically, I have written CSS that tells me how many elements I am looking at.

Of six squids, the first is green and the rest red. The first is subject to the nth-last-child(6) selector as well as the first-child selector

All that remains is to leverage this key element to style the remaining elements in the set. For this, I employ the general sibling combinator.

Six green squids because the first green squid is combined with the general sibling combinator to make all the red squids that follow green

If you’re not familiar with the general sibling combinator, the ~ li in li:nth-last-child(6):first-child ~ li means “any li elements that occur after li:nth-last-child(6):first-child.” In the following example, the elements each adopt a green font color if there are precisely six of them in total.


li:nth-last-child(6):first-child, 
li:nth-last-child(6):first-child ~ li {
	color: green;
}

More than or equal to 6#section5

Targeting a discrete quantity—whether it’s 6, 19, or 653—is not especially useful because it pertains to such a specific situation. Using discrete widths rather than min-width or max-width in our @media queries would be similarly unhelpful:


@media screen and (width: 500px) {
	/* styles for exactly 500px wide viewports */
}

In the navigation menu, I really want to switch layouts at a threshold: a quantity watershed. I want to switch at six or more items—not exactly six items. When I reach that threshold, I would like to change from a distributed table layout to a simpler, wrappable inline-block configuration. Importantly, I would like to retain that switched configuration as the number of items further increases.

The question is, how does one begin to construct such a selector? It’s a question of offsets.

The n+6 argument#section6

Another arithmetical argument adoptable by the :nth-child() selector takes the form “n + [integer]”. For example, :nth-child(n+6) styles all the elements in a set starting from the sixth.

A set of red squids that become green at the sixth squid for the remainder of the set (which can be of any size), counting upwards.

Though this has conceivable applications all its own, it’s not a “quantity-aware” selection method as such: we’re not styling anything because there are six elements or more in total; we’re just styling the ones that happen to enumerate higher than five.

To begin solving the problem properly, what we really need is to create a set of elements that excludes the last five items. Using the opposite of :nth-child(n+6):nth-last-child(n+6)—I can apply the switched layout properties to all “last elements” starting from the sixth, counting back toward the beginning of the set.


li:nth-last-child(n+6) {
	/* properties here */
}

This omits the last five items from a set of any length, meaning that when you reduce the length of the set below six, you cease to see any selected items. It’s a sort of “sliding doors” effect.

A set of green squids (to the left) and red squids (to the right) become a set of just red squids when the set becomes fewer than six in number

If, indeed, the set is greater than or equal to six in total, then all that remains is to style those last five items as well. This is easy: where there are more than six items, one or more items that “return true” (in JavaScript-speak) for the nth-last-child(n+6) condition must exist. Any and all of these extant elements can be combined with “~” to affect all items (including the last five) that follow it.

When a set of red squids has squids added to it, the squids to the right of the set become green and can be used to make the rest of the red squids green too (with the general sibling combinator)

The surprisingly terse solution to our problem is this:


li:nth-last-child(n+6),
li:nth-last-child(n+6) ~ li {
	/* properties here */
}

Naturally, 6 can be replaced with any positive integer, even 653,279.

Fewer than or equal to n#section7

As in the earlier :only-of-type example, you can turn the logic on its head, switching from “more than or equal to n” to “fewer than or equal to n.” Which brand of logic you use depends on which state you consider the more natural default state. “Fewer than or equal to n” is possible by negating n and reinstating the :first-child condition.


li:nth-last-child(-n+6):first-child,
li:nth-last-child(-n+6):first-child ~ li {
	/* properties here */
}

In effect, the use of “-” switches the direction of the selection: instead of pointing toward the start from the sixth, it points toward the end from the sixth. In each case, the selector is inclusive of the sixth item.

nth-child versus nth-of-type#section8

Note that I am using :nth-child() and :nth-last-child() in the preceding examples, not :nth-of-type() and :nth-last-of-type(). Because I am dealing in <li> elements and <li>s are the only legitimate children of <ul>s, :last-child() and :last-of-type() would both work here.

The :nth-child() and :nth-of-type() families of selectors have different advantages depending on what you are trying to achieve. Because :nth-child() is element agnostic, you could apply the described technique across different element type siblings:


<div class="container">

	<p>...</p>

	<p>...</p>

	<blockquote>...</blockquote>

	<figure>...</figure>

	<p>...</p>

	<p>...</p>

</div>


.container > :nth-last-child(n+3),
.container > :nth-last-child(n+3) ~ * {
	/* properties here */
}

(Note how I am using the universal selector to maintain element agnosticism here. :last-child(n+3) ~ * means “any element of any type following :last-child(n+3).”)

The advantage of :nth-last-of-type(), on the other hand, is that you are able to target groups of like elements where other siblings of different types are present. For example, you could target the quantity of paragraphs in the following snippet, despite them being bookended by a <div> and a <blockquote>.


<div class="container">

	<div>...</div>

	<p>...</p>

	<p>...</p>

	<p>...</p>

	<p>...</p>

	<p>...</p>

	<p>...</p>

	<p>...</p>

	<blockquote>...</blockquote>

</div>


p:nth-last-of-type(n+6),
p:nth-last-of-type(n+6) ~ p {
	/* properties here */
}

Selector support#section9

All of the CSS2.1 and CSS3 selectors used in this article are supported in Internet Explorer 9 and above, including all reasonably recent mobile/handheld stock browsers.

Internet Explorer 8 support is good for most selector types, but technically partial, so you might want to consider a JavaScript polyfill. Alternately, you could pair the selectors for the “safer” of the layout strategies with IE9-specific classes. In the case of the navigation menu, the safer option is the one catering to more items, using inline-block. The declaration block would look something like this:


nav li:nth-last-child(n+6),
nav li:nth-last-child(n+6) ~ li, 

.lt-ie9 nav li {
	display: inline-block;
	/* etc */
}

In the real world#section10

Suppose our navigation menu belongs to a content-managed site. Depending on who is administering the theme, it will be populated with a greater or fewer number of options. Some authors will keep things simple with just “Home” and “About” links provided, while others will cram their menu full of custom page and category options.

By providing alternative layouts depending on the number of menu items present, we increase the elegance with which we tolerate different implementations of the theme: we address variable content as we might variable screen dimensions.

Comparing the initial menu bar layout for fewer than six items with the layout for six or more items

So, there you have it: squid ahoy! You can now add quantity as a styling condition to your repertoire.

Content-independent design#section11

Responsive web design solves an important problem: it makes the same content comfortably digestible between different devices. For folks to receive different content just because they have different devices would be unacceptable. Similarly, it’s unacceptable for a design to dictate the nature of the content. We wouldn’t tell an editor, “Lose that, will you? It makes the design look wrong.”

But what form the content takes, and how much of it there is at any one time, is frequently indeterminate—another unknown. And we can’t always rely on text wrapping and truncation scripts. To get a real handle on content independence, we need to develop new tools and techniques. Quantity queries are just one idea.

Web design is about mutability, difference, uncertainty. It’s about not knowing. Uniquely, it is a mode of visual design not about manifesting a form, but about anticipating the different forms something might take. To some it is unbearably perplexing, but to you and me it is a challenge to relish. Like the elusive giant squid, it is a seriously slippery customer.

About the Author

Heydon Pickering

Heydon Pickering is a designer and interface developer from Norwich in the UK. He is lead designer at Neontribe and the accessibility editor for Smashing Magazine, where his book Apps For All is also available.

55 Reader Comments

  1. @Martin Gustav

    No, this has not been written about before.

    Lea’s article is apparently similar up to a point, but she does not go any further than the discrete quantities that I cover about half way through. These, as I explain, have few applications and also do not solve the problem at hand.

    The solution to the problem requires offsets (n+6 or -n+6) to create the necessary “thresholds”. I’m not aware of anyone who has written about this.

    I feel as though you only read up to the point where you recognised the similarity with Lea’s CSS and stopped?

  2. A move toward more content aware CSS would be a Designer’s dream come true. It would allow for much more flexibility in navigation structures and content layout templates.

    I’m curious about this statement though…

    “This makes Photoshop mockups of web pages doubly obsolete.”

    What then would be the author’s recommendation for a non-Photoshop-based design solution?

  3. @New Leaf Interactive: I would suggest building in HTML and CSS from the outset. Not full page designs to begin with, but a “living” styleguide divided into HTML components. This encourages you to write accessible code and gives you flexibility by not tying you to a concrete layout too early.

  4. Cool technique Heydon.

    @Torben Nice effect, but your css only applies to the grids of a certain size; if I add another row with 5 divs, the first one is grey and the last 4 are yellow.

    If you update your css with n+4, you support formatting 4 divs and greater: http://codepen.io/anon/pen/EapXar

  5. @pete
    You’re right. It will not work with widths of 1/3, 2/3 etc in a grid system.
    But I’m not trying to make a grid system. I only use it to demonstrate that you can apply a specific set of styles – in this case a background color – to a group of child-elements based on their number. The same as Heydon 🙂

  6. Very useful article, thanks. It felt a bit fiddly at first, but the more I read on, the more it made sense.
    I can think of a ‘Show more’ type thing using this technique!

  7. A useful technique, very well explained. Great article @Heydon Pickering!

    I have created a set of simple mixins for Sass that help with the selector construction. There’s also a `between()` mixin that generates a query for a range of items (e.g. ≥ 4 and ≤ 6 items). It hasn’t been discussed in the article but it’s easily feasible by combining the “fewer or equal than” and “more or equal than” techniques.

    Check out the quantity queries Sass mixins: https://github.com/danielguillan/quantity-queries

    Codepen demo: http://codepen.io/danielguillan/pen/GgBOxm

  8. Great article. Very inventive uses of pseudo-selectors. I really enjoy articles like this to help me think about CSS problems from a different perspective. Keep up the good work!

  9. Nice writeup of this!
    Yes, I hadn’t mentioned ranges in my article back then, but I showed them plenty of times in my CSS Secrets talks in 2011 🙂
    This technique (including the ranges) is also one of the “secrets” in my CSS Secrets book.
    But still, just because it’s been written and talked about before doesn’t mean it’s not super useful to have an extensive, illustrated, polished writeup like this one!

  10. This is really cool.
    But I can’t stop thinking that this relatively complex logic is a result of absence of a seriously “counting” script in CSS architecture.
    Still – I definitely like this post.

    By the way : the number “653,279” is not an integer ;]

  11. @New Leaf Interactive: I would suggest building in HTML and CSS from the outset. Not full page designs to begin with, but a “living” styleguide divided into HTML components. This encourages you to write accessible code and gives you flexibility by not tying you to a concrete layout too early.

    ————————————

    Sadly this isn’t a feasible approach when working within an Interactive Agency that services large brands.

    Website design for large corporate clients is always highly customized and very creatively detailed, is guided by demanding global brand visuals and standards, is shaped by a large User Experience team, and is visually designed by a team of Web Designers who are not the ones actually building the website.

    I’m one of the rare Designers who knows HTML and CSS (15 years ago we had to know HTML), and who enjoys using both on occasion. Yet I still find myself extremely limited creatively and visually when I “design” within an HTML framework.

    I can only hope the Design Community will someday receive a tool as powerful as Photoshop that allows responsive-friendly “living design. Adobe Muse seems to be headed in the right direction, but at this point horribly fails the responsive coding test. Perhaps in the next iteration.

  12. These selectors are among the slowest possible. ~500 slower than something wild like “div.box:not(:empty):last-of-type .title”. Test page http://jsbin.com/gozula/1/quiet

    That said, selector speed is rarely a concern, but if this selector ends up in a dynamic webapp where DOM changes are very common, it could have a large effect. So, good for many use cases but keep in mind it might become a perf bottleneck as the app matures. Something to profile at that point. Cheers

  13. @Lea I guess I hadn’t seen that particular talk. Perhaps only the “more secrets” one? Will definitely watch it now.

    In any case, I’m not surprised you had already experimented with the ranges concept! I was frustrated that some readers had not picked up on it and had assumed I was only covering the “discrete quantities” idea.

    Hopefully this article has leant some more context to ranges/thresholds regarding quantity. Glad you liked it.

  14. @Paul Irish: The slowness that you’re seeing with those selectors appears to me to be a flaw in webkit rather than an inherent problem with the selectors themselves. I just tried your test page with Firefox and an old copy of Opera (using the Presto engine) and neither of them seemed to have any problem with nth-last-child test case.

    Of course it’s an issue developers should be aware of, given webkit’s prevalence, but I would also think it’s something the webkit devs would be keen to fix if those results are really as bad as they appear (although for me webkit’s nth-last-child performance was about 50 times slower than the others, not 500).

  15. Maybe I misunderstood something, but doesn’t it depend on how large the navigation labels are, instead of the quantity of items? If “blog” gets replaced with “endorsements”, then on narrower screens it starts to overflow and the CSS would need to be updated with a smaller “breakpoint” number. See this pen: http://codepen.io/simurai/pen/MYBZJg

    Maybe a better use case is for girds where all items have the same width and are more predictable.

  16. @◖(•_•)◗ (Simurai)

    The length of the text in the navigation labels is _also_ a problem but not one I address here.

    There’s no reason why the labels in the table layout can’t wrap internally or be set to hyphenate. The technique I’m describing is addressing the point where wrapping becomes more likely and less desirable.

    The navigation menu example is really only one idea, though. The grid application is a neat one, I think.

    For example, for fewer than, say, 4 content items you could display the items in one column, with titles and descriptions showing. 4 or more items and you go into two columns and reduce the font-size a little. 10 or more items and you could switch to a 4/4 grid and hide the description to show just titles.

    This way, an overview of the listed content is responsive to how much content is showing, say on a blog’s front page.

    Thanks for stopping by!

    EDIT: I’ve added a long label in the demo to avoid confusion.

  17. @Paul Irish 1

    Thanks for the info, Paul.

    I’m frequently told that certain CSS selectors are really slow with the addendum that “it doesn’t really matter except in these extreme circumstances.”

    This seems weird to me. It’s like saying “this car will not go ANYWHERE… unless it has wheels, which it has.”

    Maybe I’m just being unimaginative or naive, but I can’t help but think “why mention it, then?” I mean, I know selector performance is measurable, but as you said, it would only take effect where DOM changes are very common. Do you mean concurrent, animated DOM changes? Lots of them? Because that’s starting to sound less like a usable app and more like some sort of diabolical migraine simulator 🙂

    Do you have an example of an app where optimizing the selectors has had a noticeable effect on performance? (I mean a working application, not a test case or demo.)

  18. Awesome! This is a problem that I’ve thought a lot about lately. In my mind, I’ve called it “content awareness”. User generated content can be very diverse and designing for every situation isn’t a luxury many can afford.

  19. This is a great idea, but it must be said that the use case of a CMS would generally involve some amount of control of the backend (e.g., a WordPress theme). Therefore, I would be inclined to use the backend to simply set a class that indicates quantity on the parent element over a more obscure / potentially less performant pure CSS solution. But in the very narrow set of cases where the developer is dealing with an indeterminate number of items AND has no control / competence to edit the backend templates, this would prove very useful indeed. Good writeup!

  20. Interesting read Heydon, the application for this sort of selection could be incredibly useful with dynamic content.

    I find it so strange how we often have to get somewhere by hacking with css before it’s actually considered to be implemented as a natural part of css. This concept obviously works, but wouldn’t it be nice if we could write articles about use-case scenarios rather than just explaining how to things working? Rhetorical question, just felt like ranting.

    Thanks for the read,

  21. This is so clever. I was with you till halfway through the article and then I started to struggle with the logic, but I can see what your doing. Going to re read this until it clicks because it’s so powerful 🙂

    Thanks

  22. I want you to thank for your time of this wonderful read!!! I definately enjoy every little bit of it and I have you bookmarked to check out new stuff of your blog a must read blog!!

  23. My heart started beating faster when I read: “I’m not pointing at some blurry specification in the distance or a twinkle in an implementer’s eye. We’re going to do this today, with CSS that’s already available.”

  24. @Heydon,

    Two years later, these clever techniques are still grossly underused or unknown. I don’t consider any of it to be a hack, either. Your article is great and I tell people who doubt the power of CSS to read it. Thanks again.

  25. A little late but I just found in this article the basis I needed for solving my problem and I wanted to thank you and also share my approach.

    I needed to change the width based on the number of elements to try to keep a series of inline-block elements with equal (or almost equal) quantity of elements in each row.

    Based on your solution I wrote something like this:

    .some-class:nth-last-child(3n):first-child, .some-class:nth-last-child(3n):first-child ~ .some-class {
    width: 33%;
    }
    .some-class:nth-last-child(4n):first-child, .some-class:nth-last-child(4n):first-child ~ .some-class {
    width: 25%;
    }
    .some-class:nth-last-child(5n):first-child, .some-class:nth-last-child(5n):first-child ~ .some-class {
    width: 20%;
    }
    .some-class:nth-last-child(6n):first-child, .some-class:nth-last-child(6n):first-child ~ .some-class {
    width: 16.66%;
    }

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA