Consider this (very similar to your structure, just a bit simpler):
body {
counter-reset: h1;
}
h1:before {
content: counter(h1) ". ";
counter-increment: h1;
}
h1 {
counter-reset: h2;
}
h2:before {
content: counter(h1) "." counter(h2) ". ";
counter-increment: h2;
}
<div>
<h1>Heading first level 1</h1>
</div>
<h2>Heading second level 1</h2>
<h2>Heading second level 2</h2>
<h2>Heading second level 3</h2>
<h2>Heading second level 4</h2>
<h2>Heading second level 5</h2>
<h1>Heading first level 2</h1>
<h2>Test</h2>
<h2>Test</h2>
<h2>Test</h2>
<h1>Heading first level 3</h1>
<h1>Heading first level 4</h1>
Why your layout does not work
By recommendation:
Counters are "self-nesting", in the sense that resetting a counter in
a descendant element or pseudo-element automatically creates a new
instance of the counter. This is important for situations like lists
in HTML, where elements can be nested inside themselves to arbitrary
depth.
The problem in your layout is that the first <h1>
inside the <div>
initializes a separate instance of the h1
counter. That happens because counters are sensitive to nested elements. Every time you reset a counter in a different level, that does not affect the same counter instance scoped into another level!
What happens
Following is what happens:
01. <body> | h1 = *
02. <div> |
03. <h1>Heading first level 1</h1> | h1 = 1 | div-h2 = *
04. </div> |
05. <h2>Heading second level 1</h2> | | h2 = 1
06. <h2>Heading second level 2</h2> | | h2 = 1
07. <h2>Heading second level 3</h2> | | h2 = 1
08. <h2>Heading second level 4</h2> | | h2 = 1
09. <h2>Heading second level 5</h2> | | h2 = 1
10. <h1>Heading first level 2</h1> | h1 = 2 | h2 = *
11. <h2>Test</h2> | | h2 = 1
12. <h2>Test</h2> | | h2 = 2
13. <h2>Test</h2> | | h2 = 3
14. <h1>Heading first level 3</h1> | h1 = 3
15. <h1>Heading first level 4</h1> | h1 = 4
As you can see, in 01.
we reset counter h1
which is working fine. However in h1
elements we reset counter h2
.
The problem occurs in 03.
where we reset counter h2
in nested level div
, to clarify this, I call this counter: div-h2
. In fact, next h2
elements from 05.
to 09.
are using a different counter which has not been reset! That is counter h2
(no nesting) and even if they try to increment it, there is nothing to increment as a reset is mandatory!
In 10.
we do not have a div
nesting, thus counter h2
is correctly reset, thus incremented as well.
What to do
In your case, you must avoid generating nested structures that are not homogeneous in your page. This is a suggestion to have a clean HTML, in your case, if you really need to keep that div, just add a div
selector where you reset counter h2
:
div {
counter-reset: h2;
}
So here is the final working snippet:
body {
counter-reset: h1;
}
h1:before {
content: counter(h1) ". ";
counter-increment: h1;
}
h1 {
counter-reset: h2;
}
div {
counter-reset: h2;
}
h2:before {
content: counter(h1) "." counter(h2) ". ";
counter-increment: h2;
}
<div>
<h1>Heading first level 1</h1>
</div>
<h2>Heading second level 1</h2>
<h2>Heading second level 2</h2>
<h2>Heading second level 3</h2>
<h2>Heading second level 4</h2>
<h2>Heading second level 5</h2>
<h1>Heading first level 2</h1>
<h2>Test</h2>
<h2>Test</h2>
<h2>Test</h2>
<div>
<h1>Heading first level 1</h1>
</div>
<h2>Test</h2>
<h2>Test</h2>
<h2>Test</h2>
<h1>Heading first level 3</h1>
<h1>Heading first level 4</h1>
The solution above works if you only have those div
nesting for h1
, in case you expect to have other header elements wrapped into div
s, than you need to reset h3
counter there too! But I do not recommend this because it is a very messy CSS arrangement: hard to maintain and not easy to follow.
Remember in CSS there is always a way to achieve things, you said you need that div
, but I think you did not try all possibilities to get rid of it.
h1
directly, without the div? – Saltsh2
counter fails buth3
counter works in the above scenario, it would always be better to reset all counters once inbody
itself like here. I am not adding as answer because I can't explain why it happens this way. – Ruth