How to Create CSS Counters to Automatically Number Lists

How to Create CSS Counters to Automatically Number Lists

One powerful tool that web developers often overlook are CSS counters. These counters allow you to automatically number elements on your webpage, saving you the hassle of manually having to update numbers when the structure of a page changes.

If you're tired of hardcoding numbers for ordered lists or sections in your layout, CSS counters offer an elegant and simple solution. This guide will walk you through how to use them effectively, whether you’re building a blog, an e-commerce site, or a complex web app.

What Are CSS Counters?

Let's start things off with a formal definition. CSS counters are variables maintained by CSS that you can increment and display within your content. The primary benefit of counters is their ability to dynamically handle sequential numbering (1, 2, 3, 4, etc), which is particularly useful in cases like the following:

- Automatically numbering ordered lists
- Creating section numbering in documents
- Adding step-by-step processes to tutorials
- Auto-numbering tables or figures in reports

Counters are particularly useful when the content on a page is changing frequently. Using CSS counters ensures that your numbering stays consistent without manual updates.

Key CSS Properties for Counters

Before diving into examples, it’s important to understand the CSS properties used to create these counters. Here are the three main properties you’ll use:

counter-reset: Initializes or resets a counter.
counter-increment: Increments a counter’s value.
content: Displays the counter in the generated content.

At a high level, CSS counters work by setting up a counter, incrementing its value for specific elements, and then inserting the counter’s value into the content using the content property (often in conjunction with ::before or ::after pseudo-elements).

Here’s a step-by-step explanation with examples.

Step 1: Setting Up the Counter

The first step is to create and initialize a counter using the counter-reset property. The syntax looks like this:

body {
    counter-reset: section; /* Initializes the "section" counter */
}

This example sets up a counter named "section." It starts with a value of 0 by default.

Step 2: Incrementing the Counter

Once the counter is set up, you can increment it for specific elements. This is where the counter-increment property comes into play. For example, let’s say you want to increment the counter for each h2 heading in a document:

h2 {
    counter-increment: section; /* Increments the "section" counter for each h2 */
}

Every time the browser encounters an h2, the "section" counter (created in Step 1) will increment by 1.

Step 3: Displaying the Counter

Finally, you need to display the counter value in your content. The content property allows you to insert the value of the counter, typically in the ::before or ::after pseudo-elements of an HTML tag.

h2::before {
    content: "Section " counter(section) ". "; /* Displays the section number */
}

This CSS will automatically display “Section 1. ” before the first h2, “Section 2. ” before the second, and so on.

Example: Numbering Sections in a Document

Let’s bring everything together with a practical example. Here, we’ll use CSS counters to number sections in a document.

HTML:

<body>
    <h2>Introduction</h2>
    <p>Welcome to the document.</p>
    
    <h2>Getting Started</h2>
    <p>Here's how to get started...</p>
    
    <h2>Advanced Techniques</h2>
    <p>Once you're comfortable, try these...</p>
</body>

CSS:

body {
    counter-reset: section; /* Initialize counter */
}

h2 {
    counter-increment: section; /* Increment counter for each h2 */
}

h2::before {
    content: "Section " counter(section) ". "; /* Display counter */
    font-weight: bold;
}

With this setup, your headings will look like:

Section 1. Introduction

Section 2. Getting Started 

Section 3. Advanced Techniques

No matter how you rearrange or add sections, the numbering will always update automatically.

Nested Numbering with CSS Counters

CSS counters are not limited to simple top-level sections like the example above. You can also create nested counters for subsections. To do this, you will need to setup multiple counters.

Here’s an example where we number both sections and subsections:

HTML

<body>
    <h2>Introduction</h2>
    <h3>Background</h3>
    <p>Here's the background...</p>
    
    <h2>Main Topics</h2>
    <h3>Topic One</h3>
    <p>Details on topic one...</p>
    
    <h3>Topic Two</h3>
    <p>Details on topic two...</p>
</body>
body {
    counter-reset: section; /* Initialize main section counter */
}

h2 {
    counter-increment: section; /* Increment section counter for each h2 */
}

h2::before {
    content: "Section " counter(section) ". ";
    font-weight: bold;
}

h3 {
    counter-reset: subsection; /* Reset subsection counter for each new section */
}

h3 {
    counter-increment: subsection; /* Increment subsection counter */
}

h3::before {
    content: counter(section) "." counter(subsection) " "; /* Display section.subsection */
}

Now, your h2 elements will have top-level section numbering (1, 2, 3, etc.), while your h3 elements will display nested numbering (1.1, 1.2, 2.1, 2.2, etc.).

Automatic Numbering for Ordered Lists

While HTML has built-in ordered lists (<ol>), CSS counters give you more control over the style and structure. Here’s an example of how to create a fully customized ordered list using counters:

HTML:

<ol class="custom-list">
    <li>First item</li>
    <li>Second item</li>
    <li>Third item</li>
</ol>

CSS:

.custom-list {
    list-style-type: none;
    counter-reset: list-item; /* Initialize list-item counter */
}

.custom-list li {
    counter-increment: list-item; /* Increment list-item counter */
}

.custom-list li::before {
    content: counter(list-item) ". "; /* Display list-item number */
    font-weight: bold;
}

This gives you the flexibility to style the list exactly as you need without relying on browser defaults. You can also apply similar techniques to other lists or sets of elements where you need custom numbering.

And here is what that would look like:

  1. First item
  2. Second item
  3. Third item

Styling the Counters

One of the major benefits of using CSS counters is that they are highly customizable. You can style them to match the design of your website. For example, you could change the font, color, or even format the counter values using CSS.

Here’s a simple example of how you can style your counters:

h2::before {
    content: "Section " counter(section) ". ";
    font-weight: bold;
    color: #3498db;
    font-size: 1.5em;
}

Conclusion

CSS counters offer an elegant and dynamic way to number content without relying on hardcoded HTML or JavaScript. Whether you're creating numbered sections, lists, or nested subsections, counters allow you to maintain clean, organized, and automatically updated structures.

By mastering CSS counters, you'll not only streamline your code but also ensure a consistent user experience, even when content is frequently updated.

Walter G. author of blog post
Walter Guevara is a Computer Scientist, software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20 years.

Get the latest programming news directly in your inbox!

Have a question on this article?

You can leave me a question on this particular article (or any other really).

Ask a question

Community Comments

No comments posted yet

Add a comment