Styling Ordered Lists with CSS Counters

Here's a scenario I find myself in now and then: I want an ordered list, and I want it to be pretty.

Because semantic HTML is important, I reach for the trusty <ol> tag:

html
  1. Stop
  2. Drop
  3. Roll

The problem is that the "bullets" (the prefix numbers) are in a CSS-selector dead zone. There is no way to style them independently!

I am not one to accept lackadaisical bullets. So I did some digging, and found an awesome solution 🎉.

Counters to the rescue

CSS has a pretty nifty trick up its sleeve to deal with this situation. It has a built-in counter mechanism.

Here's how that looks:

css

Let's go through this step by step:

  1. counter-increment is a CSS property that will increment a specific "counter" variable whenever it encounters a new element. We put it on every ordered-list item. I named my variable "muffins", because I like muffins.
  2. Before each ordered-list item, I display the current value of the count. counter() can be thought of as a CSS function which returns the value for a specific counter. In this case, muffins.
  3. I remove the default uncustomizable bullets with list-style: none, and specify a counter reset. This ensures that if I have multiple <ol> elements on a page, the counter will reset for each one.

Styling

With this CSS, we've effectively recreated a default <ol>. The difference is that we now have a CSS selector, ol li:before, we can use to apply custom styles.

Here's how ordered lists look on this blog, using this trick:

How to bake a cake

  1. I think you do something with dry ingredients
  2. The mixers play a role here
  3. Combine everything while crossing fingers
  4. Transfer to oven
  5. Poke with toothpick for fun, and then serve

It's not night-and-day, but I'm very pleased with the overall effect!

Browser support

CSS counters feel like a next-gen feature, but actually they've been around forever. They're supported in Internet Explorer 8!!

Use this property without guilt ✨

This seems like a lot of trouble!

The CSS Working Group agrees—they've penned a draft for a new ::marker pseudo-element, which would let you apply styles directly to list bullets.

Unfortunately, this is only available in Firefox and Safari.

Also: there's one more trick CSS counters have up their sleeves…

Nested lists

Here's the really cool thing: counter has a cousin, counters, and it works for nested lists.

Notice how the numbering stacks recursively, in this awesome-sounding curriculum:

  1. Welcome to web school!
  2. HTML
    1. What are tags anyway?
    2. The truth about DOM
  3. CSS
    1. Wallpaper of the Web
    2. Box Model Mayhem
      1. Padding
      2. Border
      3. Margin
    3. Specificity as a service

Here's the CSS necessary for this:

css

It's super similar, except you use counters instead of counter, and you add an "intermediary" spacer (in this case, a period).

Bringing Order Back

The ol doesn't get a lot of love compared to the ul. And yet, people love counting things! Something doesn't add up.

Maybe with this neat trick, we'll finally see ol get the attention it deserves.

A front-end web development newsletter that sparks joy

My goal with this blog is to create helpful content for front-end web devs, and my newsletter is no different! It's sent twice a month, and includes to upcoming posts, and other stuff I think you'd enjoy. No spam, unsubscribe at any time.