All posts

CSS / List counter-reset and the start attribute

Posted On 03.25.2022

In CSS, we can create a custom list numbering with counter-reset, counter-increment and counter() functions.

For example:

ol {
    counter-reset: olcounter;
 
    li {
        counter-increment: olcounter;
 
        &::before {
            content: counter(olcounter) ". ";
        }
    }
}

This approach works fine for most of the continuous list, for example:

<ol>
    <li>first item</li>
    <li>second item</li>
    <li>third item</li>
</ol>

But sometimes, you will have a list with a much more complex structure, the HTML structure would not be continuous. For example, when using a markdown generator on the following content:

This is a list:
 
1. First item
   
   Another line here
   
2. Second item
   
   Some more lines
   Ok?
   
3. Third item

The generated HTML structure would be broken up into multiple <ol> tags. They are technically multiple lists, but each of them has the start attribute to specify the start value of the list. So they would look like a continuous list.

<ol start="1">
    <li>
        <p>First item</p>
        <p>Another line here</p>
    </li>
</ol>
<ol start="2">
    <li>
        <p>Second item</p>
        <p>Some more lines</p>
        <p>Ok?</p>
    </li>
</ol>
<ol start="3">
    <li>
        <p>Third item</p>
    </li>
</ol>

If we are using the CSS style defined from the beginning of the post, the rendered list would look wrong, all the list items will start with 1 instead of incrementing:

1. First item
   
   Another line here
   
1. Second item
   
   Some more lines
   Ok?
   
1. Third item

The reason for this behavior is, we are resetting the counter at the beginning of each <ol> element.

ol {
    counter-reset: olcounter;
}

To fix the problem, we should ignore the counter reset if the start attribute is presented, it can be done with the following CSS selector:

ol {
    counter-reset: olcounter;
 
    &[start] {
        counter-reset: none;
    }
}

Please note that this approach does not really respect the value of the start attribute. If you want to make counter-reset respect the value of the start attribute, a possible approach is to use JavaScript to inline the style for each <ol> element, like this:

const olWithStarts = document.querySelectorAll("ol[start]");
 
olWithStarts.forEach(ol => {
    const start = ol.getAttribute('start');
    ol.style = 'counter-reset: olcounter ' + start;
});