CSS: Counters

Jeremy Wood
4 min readFeb 5, 2022


Several clocks on a wall
Photo by Jon Tyson on Unsplash

I was recently watching some Kevin Powell videos on CSS, and I learned about an interesting feature of the CSS library. It turns out CSS provides us with counters, which are just variables that can be incremented, decremented, or reset.

These counters can be handy for a number of things. If you’re making a list, this makes it relatively easy to make custom numbers for all the items in that list. You can make lists that count down. If you were to make an index page that lists chapters, and that also lists numbered sections within each chapter, you could have separate counters running for each type of list. Let’s take a look at that last example.

Here is the basic HTML. We have a list with a class of “index”, this list contains chapters, and each chapter contains a nested list of sections within that chapter. Each of our lists is going to be numbered out of the gate, but we want to use custom numbers for all of our lists. We will have to change our list-style to none on our lists.

Now we can make our counters and add them to our lists. One nice thing about counters is that you use the same command to both instantiate them, and to reset them. Therefore, the counter should be created where you want to reset it as well if possible. This is done with the counter-reset: ; property.

In our example we want one counter to track our chapters, and one to track our sections, so we will instantiate two counters. You simply pass the counter name of your choice to the counter-reset property.

We just instantiated two new counters. Since we created the chapter-counter on our index class, the counter will also reset every time we encounter a new element with a class of index. Likewise our section-counter will reset at any new element with the sections class. This is great, because it makes the same counter reusable across different lists of the same class.

Now that we have our counters, we just need to attach them to our list items. An easy was to do this is with the ::before pseudo element. We can just select every item in the list, and append the pseudo element to it with our current counter number inside of it. Then we just increment the counter after every element.

For every line-item that is a direct child of our index class, we are adding a ::before pseudo element. We can use the content property to display the current value of our counter to the element. We then use the counter-increment property to increment the counter by one.

Keep a couple things in mind with counter-increment. If we wanted to, we could change the increment amount by just passing in a number after our counter; counter-increment: chapter-counter 5; would cause our counter to increment by 5 every time, counter-increment: chapter-counter -3; would decrement our counter by 3, and so on. Also note that it doesn’t matter if we call counter-increment: before or after we display it in our content property. Either way it will display the newly incremented value.

We then do the same thing with our section lists. Before each line item we append a pseudo element containing the current value of our section-counter, then increment it.

The difference here is that we’re also grabbing the current count of our chapter-counter as well. This way we can concatenate the two for our custom bullet points. Our numbers increment for each section, and we’re done with that chapter. When we move on to the next chapter, we hit a new section. Once we hit the new section, counter-reset fires on our section-counter again, and the count starts over again for the new section.

Once we run through all of our lists we end up with this:

What’s more, since each of these numbers is a pseudo element, we can style each one accordingly. Now instead of just a plain old number, you can give it a background, a border, space it out to where you want it, add text to it, whatever you want! Pretty cool!

Unfortunately, as with many things on the web, there are some drawbacks to this system. Since the numbers are displayed in a content property, the numbers themselves cannot be selected via highlighting. This in turn means the contents of the pseudo element cannot be accessed by accessibility tools. Which means it’s important to think about where you use a system such as this. If it’s just for design purposes, then you’ll likely be fine. But if these elements are an integral part of understanding and navigating your site, you’ll likely want to consider other options.

There are more fun things you can do with counters. For instance, there’s a counter-style property which can turn your counter into roman numerals, alpha characters, and decimal lead numbers. There’s a counters() property that allows you to run, or nest, multiple instances of the same counter at once.

One of the coolest parts about the web is all these interesting tools that you might not use very often, but may end up saving your butt someday. There seems to be no end to these strange and exciting tools, libraries, and APIs in the web development space.



Jeremy Wood

I’m a full stack engineer who loves to learn, solve problems, and fix things! When I’m not working on my code, you’ll usually find me working on my motorcycles.