Automatic Heading Numbers with CSS


I've played around with automatic heading numbering before but always thought it needed to be done through scripting. I only found out this week (from Ivan Herman) that you can do it with CSS. Not only that, it's been around for years (it's in CSS 2.1). Ah well, you learn something new every day.

What Am I Trying To Do?

I want to create a CSS file that I can link to that will automatically number headings and not have to think about it - include a link to the stylesheet and it should "just do it."

Constraint: start at h2

I use h1 for the title of the page and I always make the content of h1 match the title element - so that should not be part of the numbering. The first heading to be numbered should therefore be the first h2 element.

Constraint: make it possible switch numbering off for a given heading

There are some circumstances in which it's useful to be able to include a heading that isn't part of the numbering so we need to be able to switch it off. Looking at what others have done the usual way is to define a class of nocount.

So for demonstration purposes, here's an h3 that doesn't increment the counter.

This heading has no number

And switch it back on again …

This heading has a number

And picks up where the previous numbered one left off.

Constraint: it needs to work across all 6 levels

HTML5 defines 6 levels of heading (i.e. h1 through h6) so I need to make a single CSS file that will cover the extreme, rarely encountered in my life so far but it could happen, case where a document used all those levels and numbered them.

Here's an h4

Blah blah

Here's an h5

Blah blah

Here's an h6

Just a quick check - everything still working as it should?

Here's another h4

Blah blah

Here's another h5

Blah blah

Here's another h6

Blah blah

What Am I not trying to do?

In this case, I don't want to style the headings - that's a separate task. This is just about the numbers.

And what about section elements?

I really like the section element that HTML5 introduces, as well as aside, header, footer etc. and I generally use them. The algorithm for defining the outline numbering is part of the HTML5 spec and the Mozilla Developer pages [broken link removed] are really good on this. Personally, I have trained myself to make sure I use headings at the right level so, unless I make a mistake, I'll only use an h4 within the scope of an h3. That said, I will usually preceed each heading with a section element. I believe the idea of always beginning a section with an h1 element has lost favour - good, because that seemed to lead to the need to define all sorts of extra CSS just to make an h1 look like the h3 you actually meant it to be.

Bottom line: I fully understand the usefulness of sections but, for this exercise, I'm going to ignore them and just focus on the hn elements.


Time to actually put the CSS on show:

  body {counter-reset: h2}
  h2 {counter-reset: h3}
  h3 {counter-reset: h4}
  h4 {counter-reset: h5}
  h5 {counter-reset: h6}

  h2:before {counter-increment: h2; content: counter(h2) ". "}
  h3:before {counter-increment: h3; content: counter(h2) "." counter(h3) ". "}
  h4:before {counter-increment: h4; content: counter(h2) "." counter(h3) "." counter(h4) ". "}
  h5:before {counter-increment: h5; content: counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) ". "}
  h6:before {counter-increment: h6; content: counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) "." counter(h6) ". "}

  h2.nocount:before, h3.nocount:before, h4.nocount:before, h5.nocount:before, h6.nocount:before { content: ""; counter-increment: none } 

If the W3C spec is too dense (and I admit they generally are), then David Storey provides a good explanation in his article Automatic numbering with CSS Counters and little is served by my repeating his explanation. Basically you first define your counters (by resetting them) and then you increment and insert them as generated content using the :before psuedo class.

Watch the cascade!

I wanted to use as few lines of CSS as possible so I first tried this:

h2 {counter-reset:h2; counter-reset:h3; counter-reset:h4; counter-reset:h5; counter-reset:h6;}

See why that doesn't work? It's the C in CSS - the cascade. You're provding a series of different values for the same property and each one replaces the other so that in fact the only counter that gets reset here is the last one.

Use It By All Means

If this is useful to you, by all means use it. If you use it directly from then no attribution is necessasry. Otherwise please treat it as being released under CC BY.

Just because I can …

…I thought I'd end with some …

Pointless extra headings

All of which should …

… be numbered…


All the Way down to h6
So that works?

Yep. That should do it.