Skip to main content
CSS Tip

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.

Click to see a demo

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!

Click to see a demo

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.

Click to see a demo

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);
}

Click to see a demo

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;
}

Click to see a demo

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;
}

Click to see a demo

Worth noting that we can also apply style containment using contain: style | strict | content.

References:


More CSS Tips