Adam Lombard
Posted on April 11, 2020
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:
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>?;
}
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>?;
}
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>?)
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 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:
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.
Posted on April 11, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.