CSS: Automatically number subheadings in nested lists
Tags: css, beginners, learning, tutorial
Published: 2020-04-11 17:35:49

---
title: CSS: Automatically number subheadings in nested lists
published: true
description: How to automatically number headings and subheadings in nested CSS lists.
tags: css, beginners, learning, tutorial
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/0qbohr7ogq44b4k9yrj5.png
---

In documents like technical specifications, academic outlines, contracts, etc., it's not uncommon to find nested lists with numbered headings and subheadings:
```
1. Heading
  1.1 Subheading
    1.1.1 Sub-subheading
```

How do we use CSS to automatically number nested lists?

Let's start with a nested mix of ordered (`<ol>`) and unordered (`<ul>`) list elements:
{% codepen https://codepen.io/AdamLombard/pen/WNQbLrB default-tab=html,result %}

We want to number the ordered items, and not the unordered ones. Let's remove the default number prefixes from our ordered list elements, leaving the bullets for our unordered list elements:
{% codepen https://codepen.io/AdamLombard/pen/ExVaGXm default-tab=css,result %}

We'll make a [CSS counter](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Lists_and_Counters/Using_CSS_counters) using a property called `counter-reset`, which works like this:
```css
element {
  counter-reset: <identifier> <integer>?;
}
```

The `identifier` is the name that will hold our counter. The `integer` is an optional number to which we would like to initialize (or reset) our counter. By default, `integer` is `0`.

We'll pair `counter-reset` with `counter-increment`, which works like this:
```css
element {
  counter-increment: <identifier> <integer>?;
}
```

The `identifier` in `counter-increment` is the name of the counter to increment; in our case this will match the `identifier` we initialize with `counter-reset`. The `integer` is optional, and determines by how much the counter will be incremented; it defaults to `1`.

In our code we want separate counters for each `<ol>` element, incremented on each `<li>`, but we want to ignore `<ul>` elements completely, and not have them affect the heading counter. We'll target `<li>` children of `<ol>` elements like so:
{% codepen https://codepen.io/AdamLombard/pen/NWGPeBW default-tab=css,result %}


Lastly, we want to prefix our counters to each ordered list item. To do so, we'll target a `::before` pseudo-element on our ordered list items, and pass the [`counters()` function](https://developer.mozilla.org/en-US/docs/Web/CSS/counters) to their content property. It works like this:
```
  counters(<identifier>, <separator-string>, <counter-style>?)
```

The `identifier` here must match an `identifier` initialized in `counter-reset` and used in `counter-increment`. It is technically a list of all the separate instances of `identifier` for the current context (there is a [`counter()` function](https://developer.mozilla.org/en-US/docs/Web/CSS/counter) for single counters). So, if we are nested three levels deep, there will be three separate `identifier`s, and the `counters()` function will concatenate them together. `separator-string` will be placed between each concatenated `identifier` instance. The optional `counter-style` can be used to change the counter to, say, Roman numerals. It defaults to integers.

In our code, we will also pass a `' - '` string to the `content` property, to separate our numbered prefixes from the rest of the element. Our final CSS will look something like this:
{% codepen https://codepen.io/AdamLombard/pen/gOabxwQ default-tab=css,result %}

Let me know what you make! 🙂

---

Was this helpful? Did I save you some time? For just $1 you can:

## {% cta https://www.adamlombard.com/support-me %} 🫖 Buy Me A Tea! ☕️ {% endcta %}

---

Learn more about [using CSS counters](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Lists_and_Counters/Using_CSS_counters).