Using CSS Counters To Apply Custom Labels To An HTML List
The other day, one of my teammates, Hector Yeomans, shared a blog post by Nicolas Carlo on the Strangler Pattern. The post, in and of itself, was an intriguing look at how to avoid "big bang" rewrites. However, as I was reading the post, something else caught my eye. Nicolas was doing something that I had never seen before: he was styling his Ordered List labels using CSS Counters. I had never heard of CSS Counters before; so, I wanted to try them out for myself.
Run this demo in my JavaScript Demos project on GitHub.
View this code in my JavaScript Demos project on GitHub.
CSS Counters provide a way to adjust the appearance of content based on where it is located within the HTML document. Ordered List Items already do this for us, providing an auto-incrementing index. CSS Counters give us the kind of functionality; however, we can apply counters across all sorts of elements, not just lists.
Considering that the browser support for CSS Counters goes all the way back to IE8, I'm somewhat shocked that I've never heard of this before! But, my CSS skills have always been a bit lacking; so, I'm not all that surprised.
Since this is my first look at CSS Counters - this post is essentially one big note to self - I'm not going to get into too many details. But, the fundamental CSS Counter mechanics revolve around resetting, incrementing, and then reading values:
counter-reset: name [ default ]
- This resets the given counter to0
by default.counter-increment: name [ delta ]
- This increments the given counter by1
by default.counter( name [ , format ] )
- This reads the current counter value.
There's more to it than that, especially when you get into nested lists. But, this is all that I understand for the moment.
To try this out for myself, I've created a list of Friends that includes a stylized list item label. The label is created using the ::before
pseudo-element; and, its content
is generated using the counter()
function:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>
Using CSS Counters To Apply Custom Labels To An HTML List
</title>
</head>
<body>
<h1>
Using CSS Counters To Apply Custom Labels To An HTML List
</h1>
<style type="text/css">
ul.friends {
/*
Start a new counter whose default value is 0.
// --
NOTE: It appears that if you want to use a counter across several sibling
elements (such as across multiple UL elements), the counter has to be
reset at a higher level in the DOM (such as Body, Section, etc).
*/
counter-reset: friends ;
/* -- */
font-size: 26px ;
list-style-type: none ;
margin: 20px 0px 20px 15px ;
padding: 0px 0px 0px 0px ;
}
ul.friends li {
/* Increment the counter by 1. */
counter-increment: friends ;
/* -- */
align-items: center ;
display: flex ;
margin: 5px 0px 5px 0px ;
padding: 0px 0px 0px 0px ;
}
ul.friends li::before {
/*
Use the current counter value to set the content of the pseudo-element.
--
CAUTION: Use of counter() in anything other than "content" is considered
to be experimental.
*/
content: counter( friends ) ;
/* -- */
background-color: #eaeaea ;
border-radius: 5px 5px 5px 5px ;
box-sizing: border-box ;
flex: 0 0 auto ;
font-family: monospace ;
font-size: 14px ;
line-height: 25px ;
margin-right: 10px ;
min-width: 35px ;
padding: 0px 5px 0px 5px ;
text-align: center ;
}
ul.friends li.bff::before {
background-color: #ff3366 ;
color: #ffffff ;
}
</style>
<ul class="friends">
<li class="bff">Kim</li>
<li>Danny</li>
<li>Sarah</li>
<li class="bff">Luke</li>
<li>Hanah</li>
<li>Timmy</li>
<li>Henry</li>
<li>Cindi</li>
<li>Donna</li>
<li>Benji</li>
<li class="bff">Carl</li>
<li>Nancy</li>
</ul>
</body>
</html>
As you can see, at the ul
level, I'm initializing my CSS Counter using counter-reset
. Then, at each li
, I'm incrementing the CSS Counter using counter-increment
. And, finally, in the ::before
pseudo-element, I'm applying the counter value to the DOM (Document Object Model) using counter()
. And, when we run the above page, we get the following browser output:
As you can see, the list of friends has completely customized labels. And, we didn't need to apply any fancy JavaScript or keep track of an external value. It's all just CSS!
Such fun! Much wow!
I'm certain I'll be making use of CSS Counters. In the past, I've achieved similar things in my Angular apps by applying $index
values in my ng-for
loops. But, with CSS Counters, not only does this become easier, it becomes doable outside of any dynamically-rendered, JavaScript-driven content.
Want to use code from this post? Check out the license.
Reader Comments