2012-01-12

Automatically numbering headings via CSS

This post shows you how to number HTML headings with CSS. That is, given the following HTML.
    <h1>My Article</h1>
    <h2>Introduction</h2>
    <h3>Rationale</h3>
    <h2>Background</h2>
With the proper CSS, the above will be displayed as
My Article
1. Introduction
1.1. Rationale
2. Background

Number the headings

To number the headings, we need the CSS construct counter. A counter is an integer variable for which there are there operations: reset, increment, read (in CSS content). To achieve the result shown above, the following CSS suffices:
    body {
        counter-reset: h2counter;
    }
    h1 {
        counter-reset: h2counter;
    }
    h2:before {
        content: counter(h2counter) ".\0000a0\0000a0";
        counter-increment: h2counter;
        counter-reset: h3counter;
    }
    h3:before {
        content: counter(h2counter) "." counter(h3counter) ".\0000a0\0000a0";
        counter-increment: h3counter;
    }
Comments:
  • The above CSS resets the counter for the first numbering level called h2counter when it enters the body. Just to be safe, we reset it again at h1.
  • The pseudo-class :before allows us to insert content before the inside of a tag.
  • The character \0000a0 is a non-breaking space in CSS. Hence there are always two non-breaking spaces after the last dot of each heading number.

Switch off numbering for some headings

Sometimes we want a single heading to not be numbered. The following CSS does not number headings that have a class nocount.
    body {
        counter-reset: h2counter;
    }
    h1 {
        counter-reset: h2counter;
    }
    
    h2:before {
        content: counter(h2counter) ".\0000a0\0000a0";
        counter-increment: h2counter;
        counter-reset: h3counter;
    }
    h2.nocount:before {
        content: none;
        counter-increment: none;
    }
    
    h3:before {
        content: counter(h2counter) "." counter(h3counter) ".\0000a0\0000a0";
        counter-increment: h3counter;
    }
    h3.nocount:before {
        content: none;
        counter-increment: none;
    }
We follow each counter clause with a non-counter version that becomes active in the presence of the class nocount and prevents the counter from being displayed and incremented.

Non-numbered headings by default

If you want to be able to choose between all headings being numbered and no headings being numbered, you have two options:
  1. Numbering is on by default: You can switch it off, e.g. by putting a class nocount in a surrounding tag. That can be achieved by replacing the single selector
        h2.nocount:before
    
    with two selectors:
        .nocount h2:before, h2.nocount:before
    
  2. Numbering is off by default: Then you prefix the :before rules with a condition, e.g. whether the class countheads is present in a surrounding tag:
        .countheads h2:before
    
The CSS shown below is a variation of idea #2: Switch on numbering if a sibling tag of h2 has a class countheads. The general sibling combinator ~ (tilde) lets us do that:
    body {
        counter-reset: h2counter;
    }
    h1 {
        counter-reset: h2counter;
    }
    
    .countheads ~ h2:before {
        content: counter(h2counter) ".\0000a0\0000a0";
        counter-increment: h2counter;
    }
    h2.nocount:before {
        content: none;
        counter-increment: none;
    }
    h2 {
        counter-reset: h3counter;
    }
    
    .countheads ~ h3:before {
        content: counter(h2counter) "." counter(h3counter) ".\0000a0\0000a0";
        counter-increment: h3counter;
    }
    h3.nocount:before {
        content: none;
        counter-increment: none;
    }

Conclusion

We have seen how to automatically count headings in HTML via CSS and how to conditionally switch it off in some places. You can download a demo file (view online) on GitHub.

8 comments:

noone said...

"Automatically numbering headings via CSS" or "How to Confuse the Hell out of a Screen Reader."

Axel Rauschmayer said...

Why would it confuse screen readers?

David Scott said...

Haven't tried it yet but it seems to be just what I was looking for.

To ratchet things up a bit further though, is it possible to render the numbering in roman numerals, or to use alphabetic numbering?

Axel Rauschmayer said...

http://www.w3.org/TR/CSS2/generate.html#propdef-list-style-type

chrisbloom7 said...

It's a neat parlor trick, but I'm not sure I see any real value to using this approach other than getting to be a little lazy in your HTML. If adding numbers to your headings makes the information more usable then the numbers really should be part of the content.

Axel Rauschmayer said...

Renumbering is painful and numbers are more meta-content than actual content. People seem to like automatic numbering for HTML’s OL tag and LaTeX has been doing it for decades for all kinds of things, including headings.

Chris Bloom said...

> Renumbering is painful

That's the lazy thing I was talking about :) I agree that the numbers are content that describes the content itself, but that doesn't necessarily make them any less important IMO. If it were my document and I felt that numbers made the headings more useful then I think I would take the time to add the numbering into the content itself.

In any case, I'm not knocking your technique - it's certainly novel.

Axel Rauschmayer said...

It might make a difference whether you use a live preview or not.

Web Analytics