Written by Supun Kavinda✏️
In web design, it’s important to represent data in an organized way so that the user can easily understand the structure of the website or content. The simplest way to do this is to use ordered lists.
If you need more control over the appearance of the numbers, you might assume that you’d need to add more elements to the DOM via HTML or JavaScript and style them. Fortunately, CSS counters save you much of that trouble.
In this tutorial, we’ll demonstrate how to get started with CSS counters and go over some use cases.
The problem with ordered lists
When you write an ordered list like the one below, the browser automatically renders the numbers for you.
<ol>
<li>My First Item</li>
<li>My Second Item</li>
<li>My Third Item</li>
</ol>
This is great, but it doesn’t allow you to style the numbers. For example, let’s say you need to place the number inside a circle. How would you do that?
One way is to get rid of the list altogether and manually add numbers yourself.
<div>
<span>1</span> My First Item
</div>
<div>
<span>2</span> My Second Item
</div>
<div>
<span>3</span> My Third Item
</div>
div {
margin-bottom:10px;
}
div span {
display:inline-flex;
align-items:center;
justify-content:center;
width:25px;
height:25px;
border-radius:50%;
background-color:#000;
color:#fff;
}
This does what we need it to do, but there are some drawbacks. For one thing, it’s hard to write the numbers by hand. And what if you need to change a number? You’d have to change them all one by one. You could add the <span>
element dynamically using JavaScript to address these problems, but this would add more nodes to the DOM, which leads to heavy memory usage.
In most cases, it’s better to use CSS counters. Let’s examine why.
Introduction to CSS counters
CSS counters are webpage-scope variables whose values can be changed using CSS rules.
First, set a counter using the counter-reset
property. list-number
is the variable name to use here.
div.list {
counter-reset: list-number;
}
Next, use the counter-increment
property to increase the value of the counter.
div.list div {
counter-increment: list-number;
}
Now each time a div.list div
element appears, the list-number
variable increases by one.
Finally, use the :before
pseudo-element with the content
property and counter()
function to display the number.
div.list div:before {
content: counter(list-number);
}
Here’s the full code:
<div class="list">
<div>My first item</div>
<div>My second item</div>
<div>My third item</div>
</div>
div.list {
counter-reset: list-number;
}
/** Note that we can use counter-increment in :before psuedo element **/
div.list div:before {
counter-increment: list-number;
content: counter(list-number);
}
The output would look like this:
We’re not quite there yet. Let’s style the :before
pseudo-element to make it look better.
div.list div:before {
counter-increment: list-number;
content: counter(list-number);
margin-right: 10px;
margin-bottom:10px;
width:35px;
height:35px;
display:inline-flex;
align-items:center;
justify-content: center;
font-size:16px;
background-color:#d7385e;
border-radius:50%;
color:#fff;
}
Changing the starting point
By default, counter-reset
sets the counter to 0
. It starts from 1
after the first counter-increment
call. Set the initial value by passing an integer as the second parameter to the counter-reset
function.
div.list {
counter-reset: list-number 1;
}
If you want to start from 0
, set the initial value to -1
.
div.list {
counter-reset: list-number -1;
}
Changing incremental values
By default, counter-increment
increases the value of the counter by one. Just like counter-reset
, you can define an offset for the counter-increment
property.
In this example, counter-reset
sets list-number
to 0
. Each time the counter-increment
is called, the value of list-number
increases by 2
, so, you’ll see numbers as 2
, 4
, and 6
.
div.list {
counter-reset: list-number;
}
div.list div:before {
counter-increment: list-number 2;
// other styles
}
Counter formats
The counter()
function can have two parameters: counter-name
and counter-format
. For the second parameter, you can use any valid list-style-type value, including:
-
decimal
(e.g., 1, 2, 3…) -
lower-latin
(e.g., a, b, c…) -
lower-roman
(e.g., i, ii, iii…)
The default value is decimal
.
For example, if you love science like me, you can use lower-greek
for alpha-beta value numbering.
div.list div:before {
counter-increment: list-number;
content: counter(list-number, lower-greek);
// ... other styles
}
Nested counters
When using nested ordered lists, numbering is always shown in this format:
If you need numbers for child list items (e.g.,1.1
), you can use CSS counters with the counters()
function.
<ol>
<li>
My First Item
<ol>
<li>My Nested First Item</li>
<li>My Nested Second Item</li>
</ol>
</li>
<li>My Second Item</li>
</ol>
ol {
list-style-type:none;
counter-reset:list;
}
ol li:before {
counter-increment:list;
content: counters(list, ".") ". ";
}
Note that we’re using the counters()
function, not counter()
.
The second parameter of the counters()
function is the connection string. It can also have a third parameter to set the format (e.g., Greek or Roman).
Nested counters with headings
Elements such as <h1>
, <h2>
are not nested in a document. They appear as distinct elements but still represent a sort of hierarchy. Here’s how to prepend nested numbers to headings:
body {
counter-reset:h1;
}
h1 {
counter-reset:h2;
}
h1:before {
counter-increment: h1;
content: counter(h1) ". ";
}
h2:before {
counter-increment:h2;
content: counter(h1) "." counter(h2) ". ";
}
The h2
counter resets every time an h1
is found. Each <h2>
in the document gets a number like x.y.
relative to the <h1>
.
Browser support
Thankfully, CSS counters are widely supported by browsers since they were introduced with CSS2. While using the counter()
function in properties other than content
is still experimental, you can do all the exercises we covered in this tutorial without any hesitation.
Below are browser support details from Can I use.
A simple challenge
Are you ready for a simple challenge involving CSS counters?
Display 1
through 1000
, along with their Roman characters, in 10 lines of code using CSS counters.
If you’re stumped, here’s how you do it:
To create 1,000 div
elements, use the following.
for (var i = 0; i < 1000; i++) {
document.body.appendChild( document.createElement("div") );
}
CSS counters:
body {
counter-reset:number;
}
div:before {
counter-increment:number;
content: counter(number) " => " counter(number, lower-roman);
}
What did you come up with?
Conclusion
CSS counters are a lesser-known feature in CSS, but you’d be surprised how often they come in handy. In this tutorial, we covered how and when to use CSS counters and went over some examples.
Below is a list of the properties we used.
Property | Usage |
counter-reset |
Reset (or create) a counter to given value (default 0) |
counter-increment |
Increase a given counter by given offset (default 1) |
counter(counter-name, counter-format) |
Get the value of the counter from the given format |
counters(counter-name, counter-string, counter-format) |
Get value of nested counters from the given format |
Of course, CSS counters are cool. But one thing I’m concerned about is that all counters are global. If you use many of them on a large project that has many CSS files, you may not be able to find where they are created, reset, and incremented. Don’t overuse them if you can help it, and if you must, be sure to use descriptive names for counters to avoid conflicts.
Is your frontend hogging your users' CPU?
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web apps — Start monitoring for free.
The post Styling numbered lists with CSS counters appeared first on LogRocket Blog.
Top comments (0)