Getting Started with CSS Counters

Use CSS to create list counters.

As web developers, proper representation of information is an important feature. One way to properly display information is by denoting hierarchy, answering the question of "what comes first?".

One sure way to denote hierarchy is by numbering objects. Apart from the ordered list, there is no other element that allows us to increment order in CSS. If we wanted to display numbers like we have above, we had to do some preprocessing. Keeping track of the index, making sure to auto-increment it etc. Something like this.

<ul class="numbered-list">
    <?php for ($i = 1; $i <= $posts->total_count; $i++): ?>
    <li class="numbered-list__item">
        <span class="numbered-list__counter">
            <?php echo $i ?>
        </span>
        <!-- Remaining markup -->
    </li>
    <?php endfor ?>
</ul>

This is good up to a point, but it has its limitations. What if we wanted to use alphabets, Roman numerals, Greek characters like etc. Implementing any of this requires the use of custom libraries.

Thankfully, with CSS we can keep track of things like this without breaking a sweat.

Counter Reset and Counter Increment

Before we start counting with CSS, you should know that counting involves two things: reset and Increment. Reset is where we reset our counter or give our counter a starting point. While Increment actually increments the counter. Let's quickly drop our markup.

All we need is a basic, yet simple to understand markup. In our css file, we need to define our reset and increments.

<ul class="numbered-list">
    <li class="numbered-list__item">
        <span class="numbered-list__counter"></span>
        <span class="numbered-list__text">Minion ipsum</span>
    </li>
</ul>

Let's set <li class="numbered-list"></li> as the reset point. To do that, we simply do.

.numbered-list {
    counter-reset: counter-name;
}

Note: counter-name can be anything you want it to be.

Since we set our reset point, we need to tell <span class="numbered-list__counter"></span> to increment its value. To do that, we use.

.numbered-list__counter {
    counter-increment: counter-name;
}

By referencing the above reset point, we tell the counter via counter-increment to start counting. In this case, our example won't work quite yet.

But since counter increments are generated content like :before and :after, we need to insert counters using the content property, either in :after or :before. Also, the counter css function lets us drop the counter value at the perfect point.

.numbered-list__counter:before {
    counter-increment: counter-name;
    content: counter(counter-name);
}

Note: I omitted the content styling to keep our CSS to a minimum.

Offsetting Incremental Index

The counter-increment can also take a value either positive or negative to change the number of steps used to increase the value.

counter-increment: counter-name 2;
/* counter-increment: counter-name +2; */

This will increase the increment with a multiple of 2. To reverse the increment, you can use a negative value to decrement the value by a multiple of the index provided.

counter-increment: counter-name -2;

Changing Counters Start Point

Passing an integer after the counter-reset declaration tells the browser to change the offset of the initial value.

.numbered-list {
    counter-reset: counter-name 2;
}

Setting this value to 2 starts the counter at 3 like this. You should also know that the default counter reset value is 0.

Reversing Counters

Reversing counters involves using a negative index on the counter-increment. But this can also get tricky pretty fast because if the list is dynamically generated, the user has to find a way to keep track of the index.

Personally, what I've have seen people do is something like this.

<ul class="numbered-list" style="counter-reset: name <?php echo $posts->total_count + 1 ?>;">
    <?php for ($i = 1; $i <= $posts->total_count; $i++): ?>
    <li class="numbered-list__item">
        <span class="numbered-list__counter">
            <?php echo $i ?>
        </span>
        <!-- Remaining markup -->
    </li>
    <?php endfor ?>
</ul>

The counter is reset inline using php to generate the value of counter-reset. In the css file we can then set the counter-increment to a negative value.

Using Other Formats

Apart from incrementing a counter with numbers, we can also increment them using alphabets, greek characters, roman numerals etc.

If you are a fan of roman numerals (like me), then to use roman numerals — all you need to do is simply pass a second parameter (lower-roman) to the counter css function.

content: content(counter-name, lower-roman);

Other options include decimal, decimal-leading-zero, lower-roman, upper-roman, lower-greek, lower-latin, upper-latin, armenian, georgian, lower-alpha, upper-alpha.

Browser Support for CSS Counters

css counters browser support

Like they say, "a picture is worth a thousand words". This chart from CanIUse illustrates how widely supported CSS Counters are. They are well supported in Internet Explorer and Safari, which is saying a lot. So you should not be worried about using CSS Counters, they have massive browser support.

Personally, I find CSS counters to be cool. To some people, counting with CSS is nondescript as they are fine with whatever method they have in place, and will continue to use it.

Samuel Oloruntoba

Self-proclaimed full-stack web developer and a quasi-academic. I work mostly on the backend (PHP and Node) with a recent enthusiasm for frontend development (React, SVG, HTML5 Canvas).