CSS Counters: counter-increment and Friends

Using counter-increment in CSSThe CSS2.1 spec introduced a new technique allowing developers to combine three CSS properties and a pseudo-element to create auto-incrementing counters — similar to what is displayed in an ordered list.

While counters for lists are limited to <ol> or <ul> elements and only with simple incrementation, the new counter method introduced in CSS2.1 allows for integers to be prepended to any set of elements, and is quite flexible.

This technique is a bit confusing because it uses multiple CSS properties, and looks different than most CSS code. I hope to clarify how it’s used and I’ll run down some possible ways it can be implemented, along with some benefits and drawbacks.

The Syntax

Here’s what the code for a typical CSS counter looks like:

div.section {
  counter-reset: headings 0;

h2:before {
  counter-increment: headings 1;
  content: "Chapter " counter(headings, decimal) ": ";

As mentioned, there are three CSS properties involved: counter-reset, counter-increment, and content. If any one of these is omitted, the technique will not work (the values, however, are more flexible, as you’ll see).

Syntax Breakdown

Here’s a breakdown of the two declaration blocks shown above and what the property/value pairs accomplish:

The Scope Indicator
The first declaration block (the selector div.section) identifies what section in your markup will contain the set of elements that will have the auto-incrementing integers. It’s important to note the distinction of this declaration block from the one following. This particular element (the <div>) will not receive a prepended integer value. This is just the “container” or “scope” wherein the actual incrementing elements will exist.

When the name of the counter is identified, it creates an instance of that specific counter that will be used in the following declaration block.

The counter-reset property must be used in conjunction with the selector that identifies the scope, as just discussed. The first part of the value is a custom name that you can give to the counter. This value is required. The name can be anything you want, as long as it doesn’t conflict with reserved words in CSS (for example, you can’t use the names “none”, “inherit”, or “initial”. And evidently you shouldn’t use the name “paged-media” for a counter, but I didn’t have any trouble getting that to work).

The second value for counter-reset is optional. The default is “0”. This value tells the numbered set of elements where the counter is reset, so the count begins one integer after the reset value. Therefore, if you set this value to “0”, the count will begin at “1”. If you set it to “-5”, the counter will start at “-4”, and so on.

The Numbered Elements
The second declaration block (the h2 selector) uses the :before pseudo-element to tell your document to place specific content (using the content property) inside all targeted elements (in this case the <h2> elements), “before” their content.

The counter-increment property identifies which scope the counter is actually associated with. So the first value (which is required) is the matching custom-named counter from the counter-reset property in the scope identifier from the first declaration block.

The second value in the counter-increment property is optional, and represents the amount by which you want the prepended integers to increment (or decrement if you use a negative value). The default is “1”.

The Prepended Content
Finally, the document is told by means of the content property exactly what content should be prepended to each element in the identified element set (the <h2> elements).

When used in conjunction with CSS counters, the only required value for the content property is the counter() function, but normally you will add other content (including whitespace) before and/or after (as I’ve done in the example above) to make the numbering look cleaner.

The counter() Function
The counter() function can take two comma-delimited arguments. The first argument tells the document to insert the current value of the named counter. The second argument is optional and tells the counter what style the integers should have. The default style is “decimal”, but can also be “upper-roman”, “upper-alpha”, and so on (the same as the list-style-type property for ordered lists).

You can also use a counters() function (plural), and you can nest counters, but for simplicity I won’t go into those right now.

A Graphic Summary

If the above seems a bit complex, here’s an infographic that describes the primary parts that comprise a CSS counter:

CSS Counter Syntax Breakdown

Browser Support and Drawbacks

Because of the cascade, multiple counters applied to the same scope need to be declared together, instead of one after the other. So this would not work:

div.numbered { counter-reset: headings 0 }
div.numbered { counter-reset: subheads 0 } 

Only the second reset counter will take effect, because it overwrites the previous one. So instead you need to declare both counters at the same time, like this:

div.numbered { counter-reset: headings 0 subheads 0 }

A potential drawback to using counters is the fact that you’re mixing content with presentation. Of course, this is not necessarily a problem with counters, but more of a problem with the content property. So, while the counters technique is flexible and useful, it does cause the line between content and presentation to be a bit blurred.

As far as browser support, the three properties and the pseudo-element that comprise CSS counter functionality are not supported in IE7 and lower, and have varying levels of partial and buggy support in IE8, Opera, and Safari. Firefox and Chrome offer the most support, all things considered.

UPDATE: In the comments, Fritz pointed out that CSS counters are not good for accessibility. This is basically because generated content (via the content property) is not accessible by current screen readers, as this article explains. If anyone has any further info on this subject, feel free to comment.

Benefits and Practical Uses

CSS counters are beneficial to a project for the obvious reason that they are programmatic and don’t require shuffling around of numeric values in your content. Similar to ordered lists, since the content doesn’t have the actual numbering, you can insert elements, remove elements, and switch elements around, and the numbering will intuitively correct itself.

Conversely, if you hard code incrementing numbers into your content, you have to correct the numbers any time you add or remove content. Thus, if counters were more widely supported by current browsers, they would be a huge benefit to a site’s overall maintenance.

Some practical uses might include:

  • Chapter names/numbers (as in the code example shown above)
  • Numbered table rows and/or table cells
  • Numbered HTML headings for lists (for list posts?)
  • To improve/change the look or incrementation value of regular ordered lists

Can We Use CSS Counters Today?

Because of the lack support in IE6 and IE7, for client sites I would say CSS counters shouldn’t be used unless you can code or find a suitable fallback option. But for websites and web apps aimed at a technical user base, I think they would be an effective way to create incrementing numbering systems without having to resort to using back-end programming.

If you’ve used CSS counters in any of your projects, or if you can think of any other practical uses for them, feel free to offer your comment below.

I’ve also included a simple demo page that shows two CSS counters in action.

22 Responses

  1. Andrei says:

    Very interesting, i had no idea about this . Unfortunately I find it too dificult for the simple result they produce, so I wouldn’t call it a very practical method.

  2. Colin says:

    Interesting, but until it becomes possible (or, if it is possible, I learn how to do it) to also update references to chapters; it leaves the possibility of breaking references and so doesn’t really save much time. I.e. I add a chapter between 3 and 4, adding 1 to chapters 4+… I need to check for references now, otherwise anyone using chapters as a reference will get lost. I’m also unsure if this is CSS’s place… Numbers are content, the fact that it’s ordered belongs in semantic markup, neither of these are CSS’s job.

    • Actually, in retrospect, “chapters” was probably not a good example to use for this. I might change that.

      CSS counters are better for basic numbered lists, similar to an ordered list, but are useful when you want to apply numbering to items other than HTML lists, and/or when you want to increment or decrement the values in ways that are not possible with just plain HTML lists.

  3. Fritz says:

    I’m sorry, but this is highly inaccessible, and an affront to all screen reader users. If you want to show an ordered list, use <ol> and <li> instead!

  4. colinbashbash says:

    This is kinda cool. I’d rather use jQuery and write something like:

           $(this).prepend("Chapter "+ index + ": ");

    (There are more people who use IE7 then people who block javascript, at the moment.)

    But if this was supported across the board, it could be really useful for adding row numbering to tables as well as other things.

    • colinbashbash says:

      also note, that using the _S_P_A_M_ word in your email address prevents you from posting here… oh well, guess i should make up another junk email address

  5. you published this on 24/08/2010 – today is 26/08/2010
    you are #4 in SERPS…that’s some wicked awesome SEO.
    great article too.

    • It’s actually #2 for the phrase “CSS counter-increment”, but as you mention, #4 for a few similar phrases. That’s certainly been helped by a whole whack of people tweeting the article and a few big sites linking to it.

      Glad you enjoyed it.

    • Bogdan says:

      I think it’s all about blog’s authority, and this is only great news for Louis. :)

  6. tom says:

    Thank you SO much for your incredibly clear description of how to use counters.
    I wrote an ebook and now I’m working on the pdf and print versions which require page numbers.

  7. John McLear says:

    Great tutorial. Good job documenting the Gotcha too, it caught me out until I RTFM properly :) Thanks!

Leave a Reply

Comment Rules: Please use a real name or alias. Keywords are not allowed in the "name" field and deep URLs are not allowed in the "Website" field. If you use keywords or deep URLs, your comment or URL will be removed. No foul language, please. Thank you for cooperating.

Markdown in use! Use `backticks` for inline code snippets and triple backticks at start and end for code blocks. You can also indent a code block four spaces. And no need to escape HTML, just type it correctly but make sure it's inside code delimeters (backticks or triple backticks).