Displaying the Current Step with CSS Counters
Let’s say you have five buttons, each representing a step. If you click the fourth one, you’re on step 4 of 5, and you need to display that. You could hard-code the text, or use JavaScript. But CSS? Turns out the browser’s built-in counter features can handle this job without a single line of scripting.
HTML
The elements don’t have to be buttons—just siblings that can be counted. But for this demo, buttons work well:
<div class="steps">
<button class="active">Shop</button>
<button>Cart</button>
<button>Shipping</button>
<button>Checkout</button>
<button>Thank You</button>
<div class="message"></div>
</div>
The empty .message div is where the CSS content property will inject the step text.
CSS
The technique relies on three separate counters:
- A total count of all the buttons
- A count of the current step
- A count of the remaining steps after the current one
.steps {
counter-reset:
currentStep 0
remainder 0
totalStep 0;
}
Counting all buttons is straightforward:
button {
counter-increment: totalStep;
}
The next step is to add a pseudo-element whose only job is to count buttons too:
button::before {
content: "";
counter-increment: currentStep;
}
The key move is to stop counting that pseudo-element on every element that comes after the active one. With an .active class, that looks like this:
button.active ~ button::before {
/* prevents currentStep from being incremented! */
counter-increment: remainder;
}
Notice that the remainder counter is incremented there, which means the currentStep counter is left alone—so it never gets advanced on the later siblings. That selective incrementing is the whole trick.
Finally, the counters are combined to produce the message:
message::before {
content: "Step: " counter(currentStep) " / " counter(totalStep);
}
There’s a bit of JavaScript in the demo so you can move the active class around, but the counting and the message generation are entirely done in CSS.



