DEV Community

Andreas
Andreas

Posted on • Originally published at Medium

PrintCSS: Counter and Cross References

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) " - ";  
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

The result of the above HTML and CSS codeThe 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) " - ";  
}
Enter fullscreen mode Exit fullscreen mode

Counter Reset with the value 4.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) " - ";
}
Enter fullscreen mode Exit fullscreen mode

This will return us 5, 10, and 15 as the numbers in front of our content divs.

Counter Increment by steps of 5.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) " - ";  
}
Enter fullscreen mode Exit fullscreen mode

This will give our content div’s the numbers 3, 2, and 1.

Counter Increment with a negative numberCounter 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) " - ";  
}
Enter fullscreen mode Exit fullscreen mode

Negative counter reset and counting up with the default incrementNegative 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>
Enter fullscreen mode Exit fullscreen mode

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, ".") " ";
}
Enter fullscreen mode Exit fullscreen mode

A list with nested countersA list with nested counters

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, ".") " ";
}
Enter fullscreen mode Exit fullscreen mode

Nested counter with increments of 5Nested 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 articleThe 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>
Enter fullscreen mode Exit fullscreen mode

The new HTML and CSSThe new HTML and CSS

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)
}
Enter fullscreen mode Exit fullscreen mode

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 divUsing 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 2Target-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, '.')
}
Enter fullscreen mode Exit fullscreen mode

The correct result with target-countersThe 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)