Styling CSS List Counters
I recently worked on a project that used a list of product features in the design. I decided to use ordered lists to handle the automatic numbering for me and thought I would share some of the things I learned about it.
I’ve been pretty obsessed with using minimal HTML markup lately. I’ve found that the less my HTML file is littered with divs and the more my CSS is styling semantic HTML tags, then I spend a whole lot less time maintaining my HTML file. I try to write the HTML once and only modify the CSS from that point on. It’s not always perfect, but it’s a good ideal to strive for.
That’s why ordered lists worked in this situation. Rather than typing the list number for each item, I let the HTML do it for me. then, when content changes in the future, the CSS handles the automatic numbering for me. Maintainability for the win!
We’re all familiar with ordered lists:
- This is the first item
- This is the second item
- This is the third item
- This is the fourth item
This outputs something like this:
- This is the first item
- This is the second item
- This is the third item
- This is the fourth item
Using list-style-type to change numbers
So far, nothing fancy, right? Well, the first weapon we have is the ability to swap the numbering out for other styles. These include:
- disc (• • •)
- circle (○ ○ ○)
- square (▪ ▪ ▪)
- decimal (1 2 3)
- decimal-leading-zero (01, 02, 03)
- lower-roman (i ii iii)
- upper-roman (I II III)
- lower-greek (α β γ)
- lower-latin (a b c)
- upper-latin (A B C)
- armenian (Ա Բ Գ)
- georgian (ა ბ გ)
- lower-alpha (a b c)
- upper-alpha (A B C)
Still common knowledge, but now we’re getting warmer.
Creating a custom counter
Using list-style-type is great if we want the default numbering system, but what if we want to style the numbers and even add content to them? In the future, CSS3 might allow us to do this with li::marker which is awesome. There’s no support for li::marker at the time of this writing, so what can we do in the meantime?
Here’s the CSS:
ol { counter-reset: my-counter; list-style-type: none; } li { counter-increment: my-counter; &:before { content: "{ Part " counters(my-counter, ".") " } "; } }
First, we set the ol list-style-type to none. That removes the numbering from each list item, giving us a blank canvas to work with.
Second, we set counter-reset to “my-counter” which is a way to tell the CSS to start a fresh counter at each ordered list tag. The “my-counter” is just an arbitrary name for the counter we are creating. It could really be anything, but the important thing is to use it consistently in your CSS markup.
Next, we set the li counter-increment, which again is my-counter. This tells the CSS that each list item is an integer and to count each one sequentially.
Finally, we created a pseudo-element for our list items, using :before. By filling in the content attribute, we can tell the CSS which counter to use and whether we want any additional characters included with our numbers. In this case, we tell the CSS to use the my-counter increment and to format it so it displays like this: { Part 1 }.
What’s that crap after the comma in the content attribute? We use “.” to tell the CSS to put a period between our numbers when we are working with sub-lists. What, we can create sub-lists? Sure, let’s talk about that.
Making sub-lists
What if we need our items to support sub-items underneath them? Just nest another ordered list in the list item it corresponds to. Here’s our HTML:
- This is the first item
- This is the second item
- This is the first sub-item
- This is the second sub-item
- This is the third sub-item
- This is the third item
- This is the fourth item
Now, we have a list within a list.
Putting it all together
Now that our markup is in place, we just need to style it in the CSS. We have control over the ordered list, each list item, and the numbers before the list items. You can style these however you want, but here’s a basic example.
See the Pen CSS Counters by Geoff Graham (@geoffgraham) on CodePen.