Understanding CSS Counters and Their Use Cases
There are certain CSS features that we probably don’t use very often. I think CSS counters fall into that category for most of us, and more than likely for two reasons:
- They don’t seem to have many use cases.
- The code to write them is a bit complex.
I recently saw a practical use case for counters, so I thought I would give a brief crash course in how counters are written and share that use case here.
A Crash Course in CSS Counters
CSS counters allow you to number items in CSS using dynamic numbering, similar to how an ordered list works. But CSS counters are quite different. This feature uses a pseudo-element combined with some counter-specific CSS to append/prepend a dynamic “count” to a specified set of elements.
Here is a code example, similar to the one I’ll be using in the demo at the end of this article:
.container {
counter-reset: issues 0;
}
.issue:before {
counter-increment: issues 1;
content: "Issue " counter(issues, decimal);
display: block;
}
The first declaration block defines the scope of the count. What this means is my counter will increment only within elements that have a class of .container
. I’ve chosen a custom identifier called “issues”, which is required to link the container to the elements being counted.
The second declaration block uses the :before
pseudo-element (I could alternatively use :after
, but that would be rare in a counter) and the content
property to prepend the defined content.
As part of the content
property’s value, I’m using the counter()
function along with a string, similar to something that might be done in JavaScript or other more typical programming language. The counter() function takes two arguments: the counter identifier defined earlier (in this case, “issues”) and the counter style, which can be any value used for the list-style-type
property in ordered lists. The default is “decimal”, which I’m using in this example.
If you’re still confused how counters work, maybe this mini infographic will help:
And if that’s not enough, here are a few extra resources with more info, including my own article where I originally published a similar infographic:
- CSS Counters: counter-increment and Friends (Impressive Webs)
- How To Benefit From CSS Generated Content And Counters (Smashing Magazine)
- CSS Counter Styles Level 3 (W3C spec)
That last link is the editors draft of the counters spec, which introduces some brand new counter-related features that probably don’t have much, if any, browser support and which might be at risk in future versions of the spec. The features I’m using in this article, however, are cross-browser, going back to IE8.
Determining the Value of CSS Counters
At a superficial glance, two major problems arise when first thinking about implementing CSS counters:
- Ordered lists already do this, so why would I need a feature so complex just to number items?
- Generated content is not accessible and this is mixing content with presentation.
The first of those two problems is really not a problem at all. If you want consecutive items numbered then it would be appropriate to use an ordered list (i.e. an <ol>
with nested <li>
elements). But CSS counters are not for numbering consecutive items; they’re for numbering non-consecutive items, wherever they might be in the DOM, then being able to re-order them without the need to change the number prepended to each.
Additionally, although counters can be a little complex looking at first glance, once you grasp the concept of CSS’s pseudo-elements, they really aren’t that difficult to modify and maintain.
The other problem, related to accessibility, doesn’t seem to be as big a problem as it was in the past. An article by Léonie Watson concluded:
“[G]enerated content is accessibility supported in most browsers, and recognised by screen readers accordingly.”
Support, however, is not 100%, so I would say that if you’re going to use pseudo-elements to generate content, the “content” should be of decorative value and not crucial to the understanding or functionality of the website it’s included in. With that balanced approach in mind, it should be fine to use counters in certain cases like the one described below.
A Simple Use Case
Recently I was looking at the W3C’s Selectors Level 4 spec and noted that they have “issues” and “examples” interspersed within the content. I’ve seen this before but only then did I decide to investigate it a little bit. These elements are numbered, so I wondered how they were able to add and remove them without needing to re-number the whole set each time. I figured it was maybe a script, or else generated on the back-end or something like that.
Nope. They’re using CSS counters, as indicated in the screenshot below:
In their page, they have numbered issues (the red boxes) and numbered examples (the yellow boxes). Using a custom identifier for each counted set, they can easily add, remove, or even re-order the items and the CSS will automatically add the correct numbers to the items.
In addition, these spec pages also include an Issues Index at the end of the page, which uses the same counter identifier as the issues, but in a new scope so it starts the count fresh. The duplicate issues list does create a little more work to maintain the issues but, once again, the order is generally easy to maintain as long as it matches what’s been changed in the content body.
A Drag-and-drop Demo
I’ve recreated the W3C’s example so you can fiddle around with how counters work, but also to see how easy it is to create dynamic lists in this way from non-adjacent items with just HTML and CSS.
See the Pen 20fe8f19ae48c210da6c5df78c0cf6f8 by SitePoint (@SitePoint) on CodePen.
In the demo, I’m using jQueryUI’s Sortable feature to let you drag and re-order any of the paragraphs on the page, including the “issue” and “example” boxes. Note how the numbers change depending on where the items are dragged (although the behavior of the number seems to be a bit quirky while in the process of dragging). I’ve also included a duplicate “Issues List” at the bottom, like on the W3C’s pages.
Conclusion
This should make it a little more clear what CSS counters can be used for. Think of any document that gets edited often, maybe even something user generated where the items can be re-sorted or re-ordered. And, as mentioned, these kinds of counters are most likely to be useful when the numbering isn’t vital to the understanding of the content and when the items are non-adjacent within the source.
If you’ve seen CSS counters used in some other useful way, let us know in the comments.