DEV Community

Cover image for Styling numbered lists with CSS counters
Brian Neville-O'Neill
Brian Neville-O'Neill

Posted on • Originally published at blog.logrocket.com on

Styling numbered lists with CSS counters

Written by Supun Kavinda✏️

In web design, it’s important to represent data in an organized way so that the user can easily understand the structure of the website or content. The simplest way to do this is to use ordered lists.

If you need more control over the appearance of the numbers, you might assume that you’d need to add more elements to the DOM via HTML or JavaScript and style them. Fortunately, CSS counters save you much of that trouble.

In this tutorial, we’ll demonstrate how to get started with CSS counters and go over some use cases.

The problem with ordered lists

When you write an ordered list like the one below, the browser automatically renders the numbers for you.

<ol>
  <li>My First Item</li>
  <li>My Second Item</li>
  <li>My Third Item</li>
</ol>
Enter fullscreen mode Exit fullscreen mode

Ordered List With No Style

This is great, but it doesn’t allow you to style the numbers. For example, let’s say you need to place the number inside a circle. How would you do that?

One way is to get rid of the list altogether and manually add numbers yourself.

<div>
  <span>1</span> My First Item
</div>
<div>
  <span>2</span> My Second Item
</div>
<div>
  <span>3</span> My Third Item
</div>


div {
  margin-bottom:10px;
}
div span {
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:25px;
  height:25px;
  border-radius:50%;
  background-color:#000;
  color:#fff;
}
Enter fullscreen mode Exit fullscreen mode

Ordered List With Numbers in Circles

This does what we need it to do, but there are some drawbacks. For one thing, it’s hard to write the numbers by hand. And what if you need to change a number? You’d have to change them all one by one. You could add the <span> element dynamically using JavaScript to address these problems, but this would add more nodes to the DOM, which leads to heavy memory usage.

In most cases, it’s better to use CSS counters. Let’s examine why.

LogRocket Free Trial Banner

Introduction to CSS counters

CSS counters are webpage-scope variables whose values can be changed using CSS rules.

First, set a counter using the counter-reset property. list-number is the variable name to use here.

div.list {
  counter-reset: list-number;
}
Enter fullscreen mode Exit fullscreen mode

Next, use the counter-increment property to increase the value of the counter.

div.list div {
  counter-increment: list-number;
}
Enter fullscreen mode Exit fullscreen mode

Now each time a div.list div element appears, the list-number variable increases by one.

Finally, use the :before pseudo-element with the content property and counter() function to display the number.

div.list div:before {
  content: counter(list-number);
}
Enter fullscreen mode Exit fullscreen mode

Here’s the full code:

<div class="list">
  <div>My first item</div>
  <div>My second item</div>
  <div>My third item</div>
</div>


div.list {
  counter-reset: list-number;
}
/** Note that we can use counter-increment in :before psuedo element **/
div.list div:before {
  counter-increment: list-number;
  content: counter(list-number);
}
Enter fullscreen mode Exit fullscreen mode

The output would look like this:

We’re not quite there yet. Let’s style the :before pseudo-element to make it look better.

div.list div:before {
  counter-increment: list-number;
  content: counter(list-number);

  margin-right: 10px;
  margin-bottom:10px;
  width:35px;
  height:35px;
  display:inline-flex;
  align-items:center;
  justify-content: center;
  font-size:16px;
  background-color:#d7385e;
  border-radius:50%;
  color:#fff;
}
Enter fullscreen mode Exit fullscreen mode

Changing the starting point

By default, counter-reset sets the counter to 0. It starts from 1 after the first counter-increment call. Set the initial value by passing an integer as the second parameter to the counter-reset function.

div.list {
  counter-reset: list-number 1;
}
Enter fullscreen mode Exit fullscreen mode

Ordered List Styled With counter-reset

If you want to start from 0, set the initial value to -1.

div.list {
  counter-reset: list-number -1;
}
Enter fullscreen mode Exit fullscreen mode

Ordered List With counter-reset From Zero

Changing incremental values

By default, counter-increment increases the value of the counter by one. Just like counter-reset, you can define an offset for the counter-increment property.

In this example, counter-reset sets list-number to 0. Each time the counter-increment is called, the value of list-number increases by 2, so, you’ll see numbers as 2, 4, and 6.

div.list {
  counter-reset: list-number;
}
div.list div:before {
  counter-increment: list-number 2;
  // other styles
}
Enter fullscreen mode Exit fullscreen mode

Styled Ordered List With counter-increment

Counter formats

The counter() function can have two parameters: counter-name and counter-format. For the second parameter, you can use any valid list-style-type value, including:

  • decimal (e.g., 1, 2, 3…)
  • lower-latin (e.g., a, b, c…)
  • lower-roman (e.g., i, ii, iii…)

The default value is decimal.

For example, if you love science like me, you can use lower-greek for alpha-beta value numbering.

div.list div:before {
  counter-increment: list-number;
  content: counter(list-number, lower-greek);
  // ... other styles
}
Enter fullscreen mode Exit fullscreen mode

Ordered List With Greek Characters Styled With CSS Counters

Nested counters

When using nested ordered lists, numbering is always shown in this format:

If you need numbers for child list items (e.g.,1.1), you can use CSS counters with the counters() function.

<ol>
  <li>
     My First Item
    <ol>
      <li>My Nested First Item</li>
      <li>My Nested Second Item</li>
    </ol>
  </li>
  <li>My Second Item</li>
</ol>


ol {
  list-style-type:none;
  counter-reset:list;
}
ol li:before {
    counter-increment:list;
    content: counters(list, ".") ". ";
}
Enter fullscreen mode Exit fullscreen mode

Note that we’re using the counters() function, not counter().

The second parameter of the counters() function is the connection string. It can also have a third parameter to set the format (e.g., Greek or Roman).

Nested counters with headings

Elements such as <h1>, <h2> are not nested in a document. They appear as distinct elements but still represent a sort of hierarchy. Here’s how to prepend nested numbers to headings:

body {
  counter-reset:h1;
}
h1 {
  counter-reset:h2;
}
h1:before {
  counter-increment: h1;
  content: counter(h1) ". ";
}
h2:before {
  counter-increment:h2;
  content: counter(h1) "." counter(h2) ". ";
}
Enter fullscreen mode Exit fullscreen mode

The h2 counter resets every time an h1 is found. Each <h2> in the document gets a number like x.y. relative to the <h1>.

Browser support

Thankfully, CSS counters are widely supported by browsers since they were introduced with CSS2. While using the counter() function in properties other than content is still experimental, you can do all the exercises we covered in this tutorial without any hesitation.

Below are browser support details from Can I use.

Browser Support for CSS Counters

A simple challenge

Are you ready for a simple challenge involving CSS counters?

Display 1 through 1000, along with their Roman characters, in 10 lines of code using CSS counters.

If you’re stumped, here’s how you do it:

To create 1,000 div elements, use the following.

for (var i = 0; i < 1000; i++) {
  document.body.appendChild( document.createElement("div") );  
}
Enter fullscreen mode Exit fullscreen mode

CSS counters:

body {
  counter-reset:number;
}
div:before {
  counter-increment:number;
  content: counter(number) " => " counter(number, lower-roman);
}
Enter fullscreen mode Exit fullscreen mode

What did you come up with?

Conclusion

CSS counters are a lesser-known feature in CSS, but you’d be surprised how often they come in handy. In this tutorial, we covered how and when to use CSS counters and went over some examples.

Below is a list of the properties we used.

Property Usage
counter-reset Reset (or create) a counter to given value (default 0)
counter-increment Increase a given counter by given offset (default 1)
counter(counter-name, counter-format) Get the value of the counter from the given format
counters(counter-name, counter-string, counter-format) Get value of nested counters from the given format

Of course, CSS counters are cool. But one thing I’m concerned about is that all counters are global. If you use many of them on a large project that has many CSS files, you may not be able to find where they are created, reset, and incremented. Don’t overuse them if you can help it, and if you must, be sure to use descriptive names for counters to avoid conflicts.


Is your frontend hogging your users' CPU?

As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.

Alt Text

LogRocket is like a DVR for web apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web apps — Start monitoring for free.


The post Styling numbered lists with CSS counters appeared first on LogRocket Blog.

Top comments (0)