HTML & CSS
Article
By James Edwards

Making CSS Count Backwards

By James Edwards

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.

--ADVERTISEMENT--

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

More:
CSS
Login or Create Account to Comment
Login Create Account
Recommended
Sponsors
Get the most important and interesting stories in tech. Straight to your inbox, daily.