CSS: Automatically number subheadings in nested lists

adamlombard

Adam Lombard

Posted on April 11, 2020

CSS: Automatically number subheadings in nested lists

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
Enter fullscreen mode Exit fullscreen mode

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:

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:

We'll make a CSS counter using a property called counter-reset, which works like this:

element {
  counter-reset: <identifier> <integer>?;
}
Enter fullscreen mode Exit fullscreen mode

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:

element {
  counter-increment: <identifier> <integer>?;
}
Enter fullscreen mode Exit fullscreen mode

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:

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 to their content property. It works like this:

  counters(<identifier>, <separator-string>, <counter-style>?)
Enter fullscreen mode Exit fullscreen mode

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 for single counters). So, if we are nested three levels deep, there will be three separate identifiers, 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:

Let me know what you make! 🙂


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

🫖 Buy Me A Tea! ☕️


Learn more about using CSS counters.

💖 💪 🙅 🚩
adamlombard
Adam Lombard

Posted on April 11, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related