How to create a table of contents with HTML and CSS

Learn how to use HTML and CSS to build a table of contents

How to create a table of contents with HTML and CSS

In this article, I’ll explain how to generate a table of contents with automatic numbering using pure HTML and CSS.

CSS counters to the rescue

In the past, I imagine that quite a lot of JavaScript code has been written for this purpose. Luckily, nowadays, the Web platform is more powerful than ever. We can do wonderful things with HTML and CSS alone. For one, counting things!

With CSS counters, we can use and increment variables maintained transparently for us by CSS; allowing us to customize the appearance of our content easily.

The value of a CSS counter can be displayed using counter() or counters() in a content property.

Let’s walk through the example provided on MDN:

body {
  counter-reset: section;

h3::before {
  counter-increment: section;
  content: "Section " counter(section) ": ";

With the above, each time an h3 tag is added to the document, the CSS counter called section is incremented using counter-increment. The first occurrence thus has a value of 1, the second a value of 2, etc.

Secondly, the value of that counter is displayed using counter(section) in the content property.

Note that counter-reset allows resetting the value of a specific counter.

But CSS counters are cooler than that!

Nesting CSS counters

CSS counters support nesting. As a matter of fact, a nested counter instance is created automatically for each child element, so there’s no additional configuration needed. How cool is that?

Using the counters CSS function, we can display all the nested counter values at once and decide which character to use to separate each value.

Check out the example on MDN.

Table of contents example

Since nested CSS counters do exactly what I needed, I’ve decided to use those to create my table of contents.

Here’s the gist of it:

As you can see, this is a simple ol & ul nested HTML structure.

To style it, we only need to use the following:

The above uses a CSS counter that I’ve called item, whose value is reset/incremented when needed and rendered using content.

This results in the following:

Which is pretty much what I wanted, styling details aside. You can indeed go further, use responsive Web design techniques and adapt the margins and whatnot depending on the available space.

You can find the working example here:

You can also take a look at the table of contents of my book to see it in action!


In this article, I’ve quickly introduced CSS counters and explained that they support nesting, making it really straightforward to create a table of contents with automatic numbering using pure HTML and CSS.

That's it for today! ✨