The Gotcha of using counter() with container
Let's consider a basic usage of counter():
<section>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</section>
section {
counter-reset: n;
}
section div:before {
content: counter(n);
counter-increment: n;
}
Nothing fancy, and we get a counting inside the <div> elements.
Now, let's make the <section> element a container.
section {
counter-reset: n;
container-type: inline-size; /* added */
}
section div:before {
content: counter(n);
counter-increment: n;
}
The code stops working!
It appears to be a bug, but it's not. By defining container-type, we apply a "style containment" to the element, which affects how counter() works. To simplify, counter-reset is no longer available for the descendants of the container.
The previous code is equivalent to:
section {
/* counter-reset: n; removed */
container-type: inline-size;
}
section div:before {
content: counter(n);
counter-increment: n;
}
Without counter-reset, there is no existing counter for counter-increment to increment, hence it will instantiate a new one within the pseudo-element. It's like we have the following code:
section div:before {
content: counter(n);
counter-reset: n; /* added implicitely */
counter-increment: n;
}
Each pseudo-element will have its own counter that contains 1.
We can easily fix this by moving the incrementation to the <div> elements:
section {
container-type: inline-size;
}
section div {
counter-increment: n;
}
section div:before {
content: counter(n);
}
Or by resetting the counter within the <section> (the container)
section {
container-type: inline-size;
}
/* we target the first child to reset the counter once */
section div:first-child {
counter-reset: n;
}
section div:before {
content: counter(n);
counter-increment: n;
}
We can face more complex situations, but the rule is the same: "style containment" will create a scoping that prevents descendants from accessing the counter stuff defined on the container (or outside of it).
If your counter is behaving strangely, don't forget about this quirk!
Can you figure out what the code below will produce?
<section>
<div></div>
<div></div>
<div style="container-type: inline-size;"></div>
<div></div>
<div></div>
</section>
section {
counter-reset: n;
}
section div:before {
content: counter(n);
counter-increment: n;
}
Worth noting that we can also apply style containment using contain: style | strict | content.
References:
More CSS Tips
- A Squishy Button with a Hover/Click Effect Using the shape() function to add a fancy shape to a button element.
- A Squircle Shape using clip-path: shape() Using the shape() function to approximate the classic squircle shape.