CSS provides numerous ways to customize the look and feel of web pages, and one of its lesser-known but powerful features is the counter style rule.
Counters in CSS are particularly useful when you need to create custom numbering or automatic numbering for lists, headings, or any other HTML elements. With counter, you can go beyond standard numbered lists (<ol>) and apply custom numbering styles anywhere on your page.
In this article, we’ll cover:
- What the
counterstyle rule is and its core functionality. - How to create, increment, and display counters in CSS.
- Creative and practical examples of using counters.
- How counters can enhance your web design.
What is the counter Style Rule?
The counter rule in CSS is a way to create and manipulate custom counters. These counters work similarly to variables that increment or decrement based on CSS rules and are most commonly used for list items or any element that requires automatic numbering.
You can define, increment, and display a counter using these three core concepts:
counter-reset: Initializes or resets the counter.counter-increment: Increments (or decrements) the counter.content: counter(): Displays the counter value in thecontentproperty.
Creating a Simple Counter
Let’s start with a basic example that demonstrates how to create and display a simple counter.
Example 1: Numbering Custom Headings
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Counters Example</title>
<style>
/* Initialize the counter */
body {
counter-reset: section;
}
/* Increment the counter for each h2 */
h2::before {
counter-increment: section;
content: "Section " counter(section) ": ";
font-weight: bold;
}
</style>
</head>
<body>
<h2>Introduction</h2>
<p>This is the introduction section of the document.</p>
<h2>Usage of Counters</h2>
<p>This section discusses the usage of counters in CSS.</p>
<h2>Conclusion</h2>
<p>This section provides the conclusion.</p>
</body>
</html>
Output:

Explanation:
- Counter Initialization: The
counter-resetproperty in thebodyelement initializes a custom counter calledsectionwith an initial value of 0. - Incrementing the Counter: For each
<h2>element, the counter is incremented usingcounter-increment: section;. Every time an<h2>element appears, thesectioncounter increases by 1. - Displaying the Counter: The
content: counter(section)rule inserts the current counter value before the content of the<h2>tag using::before. Each heading will be prefixed by its section number like “Section 1”, “Section 2”, etc.
This simple example shows how CSS counters can automatically number headings without needing to manually insert numbers in your HTML.
Customizing Counters
CSS counters are highly customizable, allowing you to change the numbering style (e.g., Roman numerals, letters) and even nest counters for more complex structures.
Example 2: Nested Counters with Different Numbering Styles
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nested CSS Counters Example</title>
<style>
/* Initialize counters for sections and subsections */
body {
counter-reset: section;
}
/* First level (main sections) */
h2::before {
counter-increment: section;
content: "Section " counter(section) ": ";
font-weight: bold;
}
/* Initialize subsection counter based on the main section */
h3 {
counter-reset: subsection;
}
/* Second level (subsections) with Roman numeral style */
h3::before {
counter-increment: subsection;
content: "Section " counter(section) "." counter(subsection, upper-roman) ": ";
font-style: italic;
}
</style>
</head>
<body>
<h2>Main Section 1</h2>
<p>This is the first main section.</p>
<h3>Subsection A</h3>
<p>This is the first subsection of section 1.</p>
<h3>Subsection B</h3>
<p>This is the second subsection of section 1.</p>
<h2>Main Section 2</h2>
<p>This is the second main section.</p>
<h3>Subsection A</h3>
<p>This is the first subsection of section 2.</p>
</body>
</html>
Output:

Explanation:
- We’ve initialized two counters:
sectionfor the main headings (h2) andsubsectionfor the subheadings (h3). - The
counter-reset: subsectioninside theh3selector ensures that thesubsectioncounter is reset for each new main section. - We display the
sectioncounter followed by a Roman numeralsubsectioncounter for the subheadings by using thecounter(subsection, upper-roman)function. - This results in numbering like “Section 1.I”, “Section 1.II”, etc., for subsections.
Advanced Usage: Counters with List Items
Counters are not limited to headings—they can also be used for list items, where you may want more control over numbering than what <ol> or <ul> tags offer.
Example 3: Custom List Numbering
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom List Counter Example</title>
<style>
/* Initialize list counter */
ol.custom-counter {
counter-reset: list-counter;
}
/* Increment and display counter for each list item */
ol.custom-counter li::before {
counter-increment: list-counter;
content: counter(list-counter) ". ";
font-weight: bold;
color: #4CAF50;
}
/* Nested list items */
ol.custom-counter li ol {
counter-reset: nested-counter;
}
ol.custom-counter li ol li::before {
counter-increment: nested-counter;
content: counter(list-counter) "." counter(nested-counter) " ";
}
</style>
</head>
<body>
<ol class="custom-counter">
<li>First item</li>
<li>Second item
<ol>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
</ol>
</li>
<li>Third item</li>
</ol>
</body>
</html>
Output:

Explanation:
- The custom class
custom-counteris added to an ordered list (ol). Thecounter-reset: list-counterinitializes a counter for the list items. - The
::beforepseudo-element is used to display the value of thelist-counterfor each<li>, which is incremented bycounter-increment: list-counter. - We then create nested lists and reset the
nested-counterwithin sub-lists. The numbering for nested items displays as “1.1”, “2.1”, “2.2”, etc.
This allows you to create custom list numbering with CSS without relying on the default browser behavior for <ol>.
Example 4: Fancy Numbered Lists with Custom Styles
Let’s get a bit more creative and style our counters in a visually distinct way.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fancy Numbered Lists</title>
<style>
/* Initialize the counter */
ol.fancy-counter {
counter-reset: fancy;
list-style: none;
padding-left: 0;
}
/* Style for the list items */
ol.fancy-counter li {
counter-increment: fancy;
position: relative;
margin-bottom: 15px;
padding-left: 30px;
font-size: 18px;
}
/* Adding a circle with the counter value */
ol.fancy-counter li::before {
content: counter(fancy);
position: absolute;
left: 0;
top: 0;
background-color: #3498db;
color: white;
width: 25px;
height: 25px;
text-align: center;
border-radius: 50%;
line-height: 25px;
}
</style>
</head>
<body>
<ol class="fancy-counter">
<li>First fancy list item</li>
<li>Second fancy list item</li>
<li>Third fancy list item</li>
</ol>
</body>
</html>
Output:

Explanation:
- We initialize a
fancycounter for the list items. - Each list item displays its number inside a circular background using the
::beforepseudo-element. - The result is a list where each item is numbered inside a visually distinct circle, creating a polished and professional look.
Vegetable List with Counters
Let’s create a custom list of vegetables, each numbered and styled with a corresponding vegetable icon.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vegetable Counter List</title>
<style>
ol.vegetable-counter {
counter-reset: veg;
list-style: none;
padding-left: 0;
}
ol.vegetable-counter li {
counter-increment: veg;
position: relative;
margin-bottom: 20px;
padding-left: 40px;
font-size: 18px;
}
ol.vegetable-counter li::before {
content: counter(veg);
position: absolute;
left: 0;
top: 0;
background-color: #28a745;
color: white;
width: 30px;
height: 30px;
text-align: center;
border-radius: 50%;
line-height: 30px;
font-weight: bold;
}
/* Adding custom icons for each vegetable item */
ol.vegetable-counter li:nth-child(1)::after {
content: "🥕 Carrot";
font-size: 20px;
margin-left: 10px;
}
ol.vegetable-counter li:nth-child(2)::after {
content: "🥦 Broccoli";
font-size: 20px;
margin-left: 10px;
}
ol.vegetable-counter li:nth-child(3)::after {
content: "🌽 Corn";
font-size: 20px;
margin-left: 10px;
}
</style>
</head>
<body>
<ol class="vegetable-counter">
<li></li>
<li></li>
<li></li>
</ol>
</body>
</html>

Explanation:
- We use a
vegetable-counterclass for the ordered list. - Each
<li>element is incremented usingcounter-increment, and a number is displayed inside a green circle. - We use the
::afterpseudo-element to append vegetable icons like “🥕”, “🥦”, and “🌽”, making the list both informative and playful.
Fruit List with Emoji Counters
This example uses fruit emojis to create a visually appealing numbered list.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fruit Counter List</title>
<style>
ol.fruit-counter {
counter-reset: fruit;
list-style: none;
padding-left: 0;
}
ol.fruit-counter li {
counter-increment: fruit;
position: relative;
margin-bottom: 20px;
padding-left: 40px;
font-size: 18px;
}
ol.fruit-counter li::before {
content: counter(fruit);
position: absolute;
left: 0;
top: 0;
background-color: #f39c12;
color: white;
width: 30px;
height: 30px;
text-align: center;
border-radius: 50%;
line-height: 30px;
font-weight: bold;
}
/* Adding fruit emojis for each item */
ol.fruit-counter li:nth-child(1)::after {
content: " 🍎 Apple";
}
ol.fruit-counter li:nth-child(2)::after {
content: " 🍌 Banana";
}
ol.fruit-counter li:nth-child(3)::after {
content: " 🍓 Strawberry";
}
</style>
</head>
<body>
<ol class="fruit-counter">
<li></li>
<li></li>
<li></li>
</ol>
</body>
</html>

Explanation:
- Similar to the vegetable list, this list uses fruit emojis and colorful number counters.
- Each list item is followed by a different fruit emoji, such as 🍎, 🍌, or 🍓.
Comic-Themed List with Speech Bubbles
Imagine creating a list styled like a comic strip, where each item is in a speech bubble, and numbers are shown inside stars for dramatic effect.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Comic Style Counter List</title>
<style>
ol.comic-counter {
counter-reset: comic;
list-style: none;
padding-left: 0;
}
ol.comic-counter li {
counter-increment: comic;
position: relative;
margin-bottom: 20px;
padding: 10px 30px;
background: #fff8dc;
border: 2px solid #f39c12;
border-radius: 10px;
font-family: 'Comic Sans MS', sans-serif;
box-shadow: 5px 5px 0px #e67e22;
}
ol.comic-counter li::before {
content: counter(comic);
position: absolute;
left: -35px;
top: 50%;
transform: translateY(-50%);
background-color: #e74c3c;
color: white;
width: 30px;
height: 30px;
text-align: center;
border-radius: 50%;
font-size: 16px;
line-height: 30px;
box-shadow: 2px 2px 0px black;
font-weight: bold;
}
/* Adding speech bubble shape */
ol.comic-counter li::after {
content: "";
position: absolute;
left: -15px;
top: 20px;
width: 20px;
height: 20px;
background-color: #fff8dc;
border: 2px solid #f39c12;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
</style>
</head>
<body>
<ol class="comic-counter">
<li>First comic panel</li>
<li>Second comic panel</li>
<li>Third comic panel</li>
</ol>
</body>
</html>
Output:

Conclusion
CSS counters offer a powerful and flexible way to introduce dynamic numbering into your web pages without relying on static HTML. From numbering headings and subheadings to creating custom ordered lists, counters allow for greater control and customization of your content. Whether you’re building complex documents, forms, or simply adding a creative touch to your lists, CSS counters can enhance the user experience by automating numbering and making your pages more interactive and visually appealing.
In addition to their utility, counters can be combined with other CSS properties (such as ::before, ::after, and styling techniques) to create beautiful and functional designs that go beyond traditional list-based layouts.
The next time you need to add automatic numbering to your web project, consider using CSS counters to make your design more efficient and flexible!





Leave a Reply