bscure CSS: Implicit List-Item Counter

A few years ago I wanted to share one little-known CSS feature — a built-in list-item counter for ordered lists. But then there were a few browser issues preventing us from using it fully. Now, given that most of those bugs are fixed, we can try starting using them for our lists.

ntro

I don’t want to go into all the details of using CSS for styling listsGo to a sidenote but would like to start right from what I would be talking about.

Let’s say we want to changeGo to a sidenote the ordered list’s markers to have brackets after the numbers rather than dots:

Side note: If you’d want to read more about lists in CSS, other than reading the specs, I can recommend this in-depth article by Rachel Andrew: CSS Lists, Markers, and Counters. Jump to this sidenote’s context.

Side note: The live examples in this article would have some extra styles with them — these would be just styles for my blog, which conveniently are using what I’m talking in this article about (with some CSS variables thrown in for convenience). Jump to this sidenote’s context.

ol > li {
  list-style-type: none;
}
ol > li::before {
  content: counter(list-item) ') ';
}
<ol>
  <li>First item</li>
  <li>And the second one</li>
</ol>
  1. First item
  2. And the second one

You might notice that in the example above there are just two declarations — and no sign of counter-reset and counter-increment. This is intended and it works!

hat the Specs Say

Let me copy a bitGo to a sidenote from the CSS Lists and Counters spec.

In addition to any explicitly defined counters that authors write in their styles, list items automatically increment a special list-item counter, which is used when generating the default marker string on list items

[…]

In all other respects, the list-item counter behaves like any other counter and can be used and manipulated by authors to adjust list item styling or for other purposes.

In the specs, some examples take an advantage of such implicit counters — showing how they would work properly with various attributes on the <ol> and <li> such as start and value.

bscure HTML list attributes: start, value, and reversed

Did you know? Also not a very well-known aspect, this time of HTMLGo to a sidenote — we have a native way to control the initial number from which the item would start, adjust a particular item’s value, or reverseGo to a sidenote the count direction. Look at these examples:

<ol start="42">
  <li>A good start</li>
  <li>And a continuation</li>
</ol>
  1. A good start
  2. And a continuation
<ol>
  <li>First item</li>
  <li value="42">And then an adjusted one</li>
  <li>The following would continue from the previous</li>
</ol>
  1. First item
  2. And then an adjusted one
  3. The following would continue from the previous
<ol reversed>
  <li>This item takes the last place</li>
  <li>This item could do better</li>
  <li>The item that wins this unnecessary competition</li>
</ol>
  1. This item takes the last place
  2. This item could do better
  3. The item that wins this unnecessary competition

Neat, right? If we would go and try to implement the same behavior from scratch using completely custom CSS counters, it would be much more complicated than those two declarations at the start of the article, but thankfully, we can utilize built-in implicit counters, and for a lot of cases we could stop writing custom counter-reset and counter-increment completely.

But this was not always the way it is now.

ld Bugs

Here is my originalGo to a sidenote experiment where I first tested all of this — https://codepen.io/kizu/pen/QaKjmJ — Firefox did not support this at all, and Chrome/Safari did have bugs. The perfect implementation at that time was in Edge!

Here is a list of the issues I stumbledGo to a sidenote over at the time:

The most serious issue was, of course, the absence of the implementation in Firefox, but the other issues in Chromium and Webkit could be a bit annoying when you would try to override the display of the counters and then use the HTML attributes to control them.

cknowledgements

When first did my experiments with the counters, I didn’t find any mentions of the implicit list-item counters except for the specsGo to a sidenote. Since then there were a few mentions of them:

I would explain the low coverage of this CSS feature first by its absence in Firefox, and then by some of the issues that were present at the time.

he ::marker

Why I’m not writing about the ::marker? There are a few reasons. The main being quite poor browser support at the current moment (May 2022) — while Firefox implemented it in 2019 and Chrome in 2020, and Safari supports only styling of its color and font, limiting what we can do with it quite drastically.

However, the things would be the same for the ::marker — it also has the access to the implicit list-item counter, so if you’re ok with the way Safari doesn’t yet support overriding content on it, we can already start using it, but I would say, given we have a way to support all browsers including Safari by using ::before — we should better do this.

ccessibility

I’m not an accessibility expert, so I’m not sure if things did improve, but at least in 2019 there was an issue where Safari/VoiceOver could lose the role of the list, here is a good article about it — “Fixing” Lists by Scott O’Hara. The gist is — even though it is generally not recommended to duplicate the role of the elements that have it intrinsically, we might want to still add a role="list" to our lists if we’re heavily styling them (and not to forget to test things, making sure the content is available to everyone).

Hopefully, the support for ::marker would be one day perfect, so there won’t be a need to override the list-style, but even we would need to be careful.

inal Words

If you’re using custom list styling in your code and you have used custom counters — you might think about retiring them and start using the built-in list-item. Even more — if you have some kind of user-generated content, and there is a chance there’d be a list with start, value or reversed — there is a big chance those won’t work with anything except for the list-item counter. So I hope this post would be helpful.

And, once again: reading specs is one of the best ways to learn what CSS is capable of, and it is possible to find some real gems there. And if those things are obscure — there is a chance they’re not very well tested and you could find bugs that you could report, in the end helping improve the way we write CSS.


Let me know what you think about this article on Mastodon!