Making CSS Count Backwards

Tweet

Did you know that CSS Counters can go backwards?

Neither did I until I needed it, and I discovered that the counter-reset and counter-increment properties can be “seeded” with extra numbers, intended to allow for more flexible numbering, which can also be used to make them count backwards!

Counting Forwards

The basic idea with counters is that they allow you to implement number-systems in CSS, essentially replacing and supplementing ordered-list attributes like value and start. With two properties, you declare the name of a counting variable in a base selector, then increment it in an instance selector; the result is then typically output using the content property. Here’s a simple example, that re-creates the numbering of a standard ordered-list (example1):

ol
{
    list-style-type:none;
    counter-reset:item;
}
ol > li
{
    counter-increment:item;
}
ol > li:before
{
    content:counter(item) ". ";
}

But as I said earlier, you can “seed” both these properties with additional values, so that they start from a different number and/or count in different steps. Here’s another example, this time starting from ten and counting up in twos (example2):

ol
{
    list-style-type:none;
    counter-reset:item 10;
}
ol > li
{
    counter-increment:item 2;
}
ol > li:before
{
    content:counter(item) ". ";
}

Counting Backwards!

Now here’s the cunning bit! The additional value in counter-increment doesn’t have to be positive, it can also be zero or negative. So here’s an example of a counter that goes backwards from ten to one (example3):

ol
{
    list-style-type:none;
    counter-reset:item 11;
}
ol > li
{
    counter-increment:item -1;
}
ol > li:before
{
    content:counter(item) ". ";
}

The thing to note is how we have to start counter-reset one-count higher than the highest number we want to display, because the iterating property counter-increment has already counted once when the first element is displayed (it’s counted that element).

Of course the fact that we have to hard-code the starting number at all makes this difficult to use in the wild, because the lists it encounters will generally have different numbers of items. The thing to do then is split the rules between a stylesheet and an inline style rule.

Production Code

So first we have the following stylesheet rules, which define an activating class, and the counting rule (there are also a couple of CSS hacks for IE6 and IE7, to restore their normal list-style since the counters won’t work):

ol.count-backwards
{
    list-style-type:none;
}
ol.count-backwards > li
{
    counter-increment:start-from -1;
}
ol.count-backwards > li:before
{
    content:counter(start-from) ". ";
}

* html ol.count-backwards { list-style-type:decimal; }
*+html ol.count-backwards { list-style-type:decimal; }

Then we instantiate a counter instance with a counter-reset style, defining the starting number (or rather, one-higher than the starting number, as we’ve seen). I’ve used "start-from" as the counter name so that it’s easy to remember what it is and what it does (example4):

<ol class="count-backwards" style="counter-reset:start-from 11">
    <li>In tenth place..</li>
    <li>In ninth place..</li>
    <li>In eighth place..</li>
    <li>In seventh place..</li>
    <li>In sixth place..</li>
    <li>In fifth place..</li>
    <li>In fourth place..</li>
    <li>In third place..</li>
    <li>In second place..</li>
    <li>In first place..</li>
</ol>

We can even define additional class rules to implement different types of counter. Here’s one for lower-case greek (example5):

ol.count-backwards.lower-greek > li:before
{
    content:counter(start-from, lower-greek) ". ";
}

But what’s it all for? Well, I wanted to make an archive list of blog posts for my site, listing the most-recent at the top. Since the first and earliest is then at the bottom, this means that the list needs to count down.

And I’m sure you can think of your own situations where this might be useful, and handy to have a way of implementing instantly, without the inevitable output-lag of doing it with scripting.

If the lack of IE6/7 bothers you, you could always add a scripting layer as well…

Afterthought: Hacking IE

Or at a pinch, you could do it with an expression property (something I’ve written about before), and leverage the <ol> start attribute for IE to read the start number from (example6):

<ol start="10" class="count-backwards" style="counter-reset:start-from 11">

ol.count-backwards li
{
    zoom:expression(
        (typeof counter == "undefined" 
            ? counter = parseInt(this.parentNode.getAttribute('start'), 10) 
            : counter--), 
        this.innerHTML = (typeof this.processed == "undefined" 
                ? ("<strong>" + counter + ". </strong>") 
                : "") 
            + this.innerHTML, 
        this.style.marginLeft = "-1.5em",
        this.processed = true, 
        this.runtimeStyle.zoom = "1"
        );
}

IE8 will ignore this code since it no longer implements expression, but then it supported the counters in the first place! Woo-hoo!

Thumbnail credit: stephenvance

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Anonymous

    when’s the last time ya’ll checked your print.css?

  • http://www.brothercake.com/ brothercake

    Checked who’s print css?

  • Anonymous

    Guessing he means this site (sitepoint.com).

    Print preview in Opera starts with 2 blank pages.

  • Oa Berg

    Print.css seems to be okey in Safari 5 (mac).

  • Nicholas

    Fine, but what if only the raw html is left over, without css (screen readers, search engines, …) :-/

    • http://www.brothercake.com/ brothercake

      Then it’s no different than if you’d done nothing in the first place.

      • Anonymous

        So we can’t actually use this technique since it’s purpose is just to turn things around… which isn’t actually done.

    • http://www.brothercake.com/ brothercake

      Well you can use it for anything where the numbering is a matter of style rather than content – ie. where the content still makes sense whichever way the numbering goes.

      Arguably, that’s the case for most ordered-lists (that its numbering is a matter of style rather than content, I mean). Arguably, if the numbering is a matter of content (such as referenced clauses in a legal agreement) then you shouldn’t rely on list numbering at all, but should use something more substantive – like a two-column table where explicit numbers are part of the content.

      But even if you discount that, there are still situations where the numbering is arbitrary. Here’s my specific case, an archive list of blog posts, which I wanted to be numbered in reverse-order, but which could go either way and still make sense: http://www.brothercake.com/site/portfolio/blogging/

  • http://www.pmob.co.uk Paul O’B

    Well explained James :)

    We had a similar question in the forums a while ago that posed the same question:

    http://www.sitepoint.com/forums/showthread.php?t=662275&highlight=count+list+backwards

  • David Moorhouse

    Starts with a blank page in FF3.6 on XPSP3 :(

  • Anonymous

    This was a good summary, and very useful information about CSS for ordered lists.

  • http://www.brothercake.com/ brothercake

    Oh and thanks for the heads-up on print.css guys – I’ll look into it.