Numbering of Text using CSS

When you have a long text with headers and maybe sub-headers even, it would make it more readable if the headers where numbered. Now you don't want to that in the text itself since moving blocks of text around or inserting or deleting blocks of text would require to re-number it all over again. If you want to make your text more readable, you can make use of the counter and counter-increment in your CSS.

To demonstrate what is explained here, I created a demo page on our website.

Here's the Before

As you can see it's a simple text page with some h1 and h2 headers and some images. But since it is quite a long text with lots of images, it is difficult to pinpoint a specific line of text.

And here's the After

Here are some of the things I did to improve the readability:

  • Level 1 Numbering in the margin
    In front of each h1 I have put a counter with a bit of styling so it stands out.
  • Level 2 Numbering
    In front of each h2 I have put a counter that shows the Level 1 and Level 2 numbering.
  • Image Numbering
    Each image has a number as well, plus the title that would only be visible on mouse over.
  • Tips on the side
    In the right margin I've added some tips with an image in it, also with numbering.

For all of these adjustments I used pseudo elements. To be more specific, the content attribute of the before and after. And for the counters I used the counter and counter-increment attributes which can be placed on any element.

Level 1 Numbering in the margin

Each h1 should have a unique number within the page. So for that I used the counter-reset attribute from the body element:

body {
    counter-reset: level1 tip img;

As you can see, the tips and the images also are numbered throughout the page. So I placed the counters for them in the counter-reset of the body. So you can reset (or set if they do not exist yet) multiple counters in one go. Just specify the names, seperated by spaces

Having done that, the level1 counter is created when the body element is rendered. From there you can use it every time an h1 is found after that. In this case I used the before pseudo element to display the counter value:

h1::before {
   content: counter(level1);
   counter-increment: level1;
   position: absolute;
   top: -5px;
   left: -75px;
   display: inline-block;
   width: 55px;
   height: 55px;
   line-height: 55px;
   text-align: center;
   vertical-align: middle;
   background-color: #CC0000;
   color: #FFFFFF;
   clip-path: polygon(50% 0%, 100% 15%, 100% 85%, 50% 100%, 0 85%, 0 15%);

It's really all about the first two attributes here. The rest is all optional styling. In the content I used the counter with the name I gave in the counter-reset from the body. And from there it should be counting up, so I used the counter-increment for the named counter.

Level 2 Numbering

This one is a little different. In this case I wanted to display level 1 and level 2. So the level 2 should be reset after every level 1 changes. Better said, the counter for each h2 should start a new at every h1. So here is the CSS for the h1:

h1 {
   counter-reset: level2;
   position: relative;
   color: #CC0000;
   letter-spacing: 0.5px;

Now in the h2 pseudo element before I display the value of both level 1 and 2:

h2::before {
   content: counter(level1) '.' counter(level2);
   counter-increment: level2;
   display: inline-block;
   width: 60px;

As you can see you don't need to use the + sign to put all the strings together. Just use the counter options and any fixed text encapsulated within single quotes. And again, since the next h2 should have a higher number, don't forget to increment that one.

Image Numbering

The numbering of the images is done in a similar way as the h1's. I added a little extra for this example so you can see more of what's possible:

.img::after {
   content: 'Picture ' counter(img) ': ' attr(title);
   counter-increment: img;
   position: absolute;
   bottom: -17px;
   left: 5px;
   display: inline-block;
   width: 600px;
   height: 20px;
   font-size: 12.5px;
   color: #555555;
   font-style: italic;

So for the caption I used fixed text, then a counter, some more fixt text and finally the value of one of the img attributes. In this case the title, but you could also use the src. And again, don't forget to increment the counter.

Tips on the side

And finally the tips on the side. So far we have seen you can use fixed text, counters and attribute values. But you can also use a URL for an image to be displayed. And you can also use line breaks:

.tip::after {
   content: url(images/lightbulb.png) ' Tip ' counter(tip) ' in ' counter(level1) '.' counter(level2) '\A' attr(data-tip);
   counter-increment: tip;
   white-space: pre-wrap;
   position: absolute;
   top: -15px;
   right: -325px;
   display: inline-block;
   width: 300px;
   font-size: 14px;
   color: #AAAAAA;

The url here is similar to the url for a background image. You don't need to use quotes here. For a line break you use '\A' and you will notice that it doesn't work. That's because you need to set the white-space to pre-wrap.

It's a wrap!

So there is a nice way to add some numbering in your pages. You could use this also for tables (count the rows) or unordered lists (UL and LI). It's just a matter of (re)setting the counter at the correct moment and then use the increment when needed. All the styling can be done through the pseudo elements before and/or after.

That's it for this post. Check out the demo for this on our website. Feel free to leave a comment or send a question to Let me know if you got inspired and may the source be with you!