Creating a CSS Legal Numbering Style List

I was recently tasked with creating a comprehensive legal contract that would be available online for clients – nothing fancy, I just used some good ol’ HTML & CSS to achieve this CSS Legal Numbering Style List.

Little did I realise I had become quite rusty when it came to the correct syntax for labelling those pesky CSS counters, especially figuring out how to handle both parent and child list items!

Creating a CSS Legal Numbering Style List featuer image

After some Googling, I had everything under control, and thought it might help some people out there to show what I had come up with, to accomplish this task.

Righto – Let’s get into this front-end tutorial!

Let’s start by creating a HTML file, populating it with the below ordered list, and link up a stylesheet.

  • index.html
  • style.css
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSS Legal List Numbering</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>

    <ol class="level-1">
        <li>Top level
            <ol class="level-2">
                <li>Sub level
                    <ol class="level-3">
                        <li>Sub sub level
                            <ol class="level-4">
                                <li>Sub sub sub level</li>
                                <li>Another sub sub sub level</li>
                            </ol>
                        </li>
                        <li>Another sub sub level</li>
                    </ol>
                </li>
                <li>Another sub level</li>
            </ol>
        </li>
        <li>Top level</li>
        <li>Another top level
            <ol class="level-2">
                <li>Sub level
                    <ol class="level-3">
                        <li>Sub sub level</li>
                        <li>Another sub sub level</li>
                    </ol>
                </li>
            </ol>
        </li>
    </ol>

</body>
</html>
/* Un momento */

And we are left with this beauty…

Notes

We could have used direct child selectors in our CSS, but I have purposely added numbered level classes for each ordered list <ol class="level-1"> etc, to make things easier to follow along.

2. Setting Up Our Stylesheet

Now, we can reset all margins and padding for the page elements and center everything up.

  • style.css
* {
    padding: 0;
    margin: 0;
    box-sizing: border-box;
}
html, body {
    height: 100%;
    width: 100%; 
}
body {
    font-family: Arial, sans-serif;
    display: flex;
    align-items: center;
    justify-content: center;
}

 

And now we have something that looks like this:

3. Remove Default Numbers & Style Top Level List

Next, we have to remove the numbers, globally for our ordered list items.

And while we’re at it, let’s create a global rule for all li and li::before selectors, which will just make them relative and absolute positioned.

  • style.css
ol[class^="level-"] {
    list-style-type: none;
}
ol[class^="level-"] li {
    position: relative;
    margin-bottom: 10px;
}
ol[class^="level-"] li::before {
    position: absolute;
    left: 0;
}

…and we have the following:

3.1 Style Top Level List

Now, let’s style the top ordered list, which just needs to have positive integers (1., 2., 3. etc).

  • style.css
ol.level-1 > li {
    counter-increment: level1;
    padding-left: 40px;
    font-weight: bold;
}

ol.level-1 > li * { font-weight: normal; }

ol.level-1 > li::before {
    content: counter(level1) ".";
}

Notes

Everything here should be pretty self explanatory – we are just giving each list item some margins/padding and adding in the counters with absolute/relative positioning with the ::before pseudo selector.

The most important parts of the above code are:

  • counter-increment: level1; defines a counter variable (named “level1” in this case) that increments by 1 each time it encounters an element matching the selector.
  • content: counter(level1) "."; uses that counter variable to display the current count value followed by a period.

Anywho – here’s the current status of our CSS Legal Numbering List:

4. Styling Our Second Level List

Now, we just need to tackle the second level ordered list. This list will include our previous positive integers, a period and then a new counter called level2.

  • style.css
ol.level-2 {
    counter-reset: level2;
    padding-left: 0px;
    margin-top: 10px;
    margin-left: -40px;
}
ol.level-2 > li {
    counter-increment: level2;
    padding-left: 40px;
}
ol.level-2 > li::before {
    content: counter(level1) "." counter(level2);
}

I have also tweaked the left padding to bring everything inline with the parent list, and add some breathing room with bottom margin, so we now get…

5. Styling Our Third Level List

Right, this is where things get a little more interesting, as we are now no longer using numbers, but lowercase letters (a, b, c…). We will need to indent these also, to be in line with the second level list.

  • style.css
ol.level-3 {
    counter-reset: level3;
    padding-left: 0px;
    margin-top: 10px;
}
ol.level-3 > li {
    counter-increment: level3;
    padding-left: 30px;
}
ol.level-3 > li::before {
    content: "(" counter(level3, lower-alpha) ")";
}

Same as before really, but you will see that for the counter, I am using the lower-alpha characters, wrapped in brackets.

6. Styling Our Fourth Level List

Last up the final level list 🙂

  • style.css
ol.level-4 {
    counter-reset: level4;
    padding-left: 0px;
    margin-top: 10px;
}
ol.level-4 > li {
    counter-increment: level4;
    padding-left: 30px;
}
ol.level-4 > li::before {
    content: "(" counter(level4, lower-roman) ")";
}

And instead of lower-alpha characters, we’ll use lower-roman for the fourth counter.

7. CSS Legal Numbering Style List Conclusion

And there we have it, a complete legal numbering system using CSS counters! This implementation creates a professional document structure with proper hierarchical numbering (numbers > decimal notation > lowercase letters > lowercase Roman numerals) by leveraging CSS counters with the counter-increment and counter() functions.

By using the ::before pseudo-element and targeted class selectors, we’ve created a clean, maintainable system that gives complete control over spacing and styling while maintaining semantic HTML. This approach works across browsers without JavaScript or server-side processing, making it perfect for legal contracts and other structured documents.

If this tutorial has helped you, I wouldn't say no to a coffee as a tip ☕️

Buy Me a Coffee at ko-fi.com

Leave a Reply

Your email address will not be published. Required fields are marked *