In this article, I want to explain how counters work and how to do cross-references in your document. Both counter and also cross-references were already a topic in the article about the table of contents.
There we used the ‘page’ and ‘pages’ counters, which are predefined in any of the PDF generation tools. To create the table of contents, we then used the ‘target-counter’ function on the headlines to figure out on which pages they are.
But let’s look at counters once again as you do not need to stick to the predefined ones.
Counter
To use a counter you first need to initialize it by using the ‘counter-reset’ property. By default, the reset sets your counter to 0. But you can also set the counter to a specific number by passing it as a second parameter.
To display a counter in your document, you can use the ‘counter’ function inside the ‘content’ property.
div.content {
counter-reset:item;
}
div.content > div {
counter-increment: item;
}
div.content > div:before {
content: counter(item) " - ";
}
The HTML structure for the above CSS would be a div element with the class ‘content’, containing further div elements. The child div elements will get counted.
<div class="content">
<h1>PrintCSS: Counter</h1>
<div>first content div</div>
<div>second content div</div>
<div>third content div</div>
</div>
The result of the above HTML and CSS code
As I mentioned before, we can directly set the counter to a different number. For this, we need to adjust our CSS code.
div.content {
counter-reset:item 4;
}
div.content > div {
counter-increment: item;
}
div.content > div:before {
content: counter(item) " - ";
}
Counter Reset with the value 4.
Now take a guess what number our ‘first content div’ would start with? The correct answer is 5. Why 5, you may ask, because we increment the counter by one for every div inside the content div.
As we saw until now, the counter increment is one by default, but also here; we can pass any other number. Let’s, for example, increment in steps of five and use the default reset of 0.
div.content {
counter-reset:item;
}
div.content > div {
counter-increment: item 5;
}
div.content > div:before {
content: counter(item) " - ";
}
This will return us 5, 10, and 15 as the numbers in front of our content divs.
Counter Increment by steps of 5.
You can also pass a negative number to the counter increment to lower the current value. So let’s say we do the reset to 4 again and then increment by -1.
div.content {
counter-reset:item 4;
}
div.content > div {
counter-increment: item -1;
}
div.content > div:before {
content: counter(item) " - ";
}
This will give our content div’s the numbers 3, 2, and 1.
Counter Increment with a negative number
Also, the counter reset allows you to use negative numbers, and we can count up with the counter increment.
div.content {
counter-reset:item -1;
}
div.content > div {
counter-increment: item;
}
div.content > div:before {
content: counter(item) " - ";
}
Negative counter reset and counting up with the default increment
Nested Counter
You can nest counters to create something like 1.2.3 or 2.1. Let us have a look at how that is done. First, we will need to create a nested HTML structure.
<div class="content">
<h1>PrintCSS: Nested Counter</h1>
<ol>
<li>item</li>
<li>item
<ol>
<li>item</li>
<li>item</li>
<li>item
<ol>
<li>item</li>
<li>item</li>
</ol>
</li>
<li>item</li>
</ol>
</li>
<li>item</li>
<li>item</li>
</ol>
<ol>
<li>item</li>
<li>item</li>
</ol>
</div>
To display our counter value now, we need to use the function ‘counters’ instead of ‘counter’. This function requires a second parameter, which is the separator of the nested counters. The rest of the CSS is quite similar, only the selectors change.
ol {
counter-reset: listnumber;
list-style-type: none;
}
li{
counter-increment: listnumber;
}
li::before {
content: counters(listnumber, ".") " ";
}
Everything else works exactly like before, for example incrementing by 5 instead of 1.
ol {
counter-reset: listnumber;
list-style-type: none;
}
li{
counter-increment: listnumber 5;
}
li::before {
content: counters(listnumber, ".") " ";
}
Nested counter with increments of 5
Cross References
Cross References are usually used to point to a chapter or page number. The most common example is the table of contents.
The first counter example from this article
Looking back to our counter example with the content div’s. Let’s say we wanna reference to the second content div. For this to work, we first need to give the HTML element an ID.
Then we create a text anchor to this ID and make the ‘marker’ IDs content bold and red.
<a href="#marker">The magic number is</a>
<div>first content div</div>
<div id="marker">second content div</div>
<div>third content div</div>
</div>
To get the ‘magic’ number, or better said to cross-reference to this counter value, we need to use the target-counter function.
.content a::after {
content: ": " target-counter(attr(href, url), item)
}
First, we say that we want the ‘href’ attribute, and then we say which counter we want to get in our case ‘item’. And in this way, we will get our ‘magic’ number 2!
Using target-counter to get the counter value on the second div
Last let’s try the same on our nested counter sample from above. Of course, we need to change the counter name in the target-counter function to ‘listnumber’. We will put the marker ID on the item with the counter value 2.3.2.
Target-counter on the nested counters, we should get 2.3.2 but we get 2
As you see, we still get 2 as our ‘magic’ number and not 2.3.2 cause we use target-counter. This only gives us the last counter, so as we want 2.3.2, the last number in this is ‘2’. If we would have put the marker on the 2.3.1 element, we would have gotten 1 in return.
Here, we need to use the ‘target-counters’ function and add another parameter at the end, the separator for the single counter values.
.content a::after {
content: ": " target-counters(attr(href, url), listnumber, '.')
}
The correct result with target-counters
All the samples in this article are rendered with PDFreactor on the website printcss.live. The samples are working with Prince and Weasyprint too!
Top comments (0)