[Solved] Using CSS to style a numbered list


Below is a sample on how the desired result can be achieved using <ol> (ordered lists) and CSS counters. It has a bit of a hack-ish feel about it because of the expectation that when a line is wrapped around, it should not start from under the numberings. Otherwise, I feel this method is much better than manually keying in the numbers (or) using tables.

Consistent spacing between the numbering and text is obtained by setting a width to the li:before pseudo-element and making its display as display: inline-block. Modify the width based on how much spacing is required. Note that when modifying the width, the margin-left and padding-left also have to be modified accordingly to maintain the styling.

CSS Counters have reasonably good browser support also.

.level1, .level2, .level3 {
    list-style-type: none;
}
.level1 {
    counter-reset: level1; /* first level counter - for 1, 2, 3 */
}
.level2 {
    counter-reset: level2; /* second level counter - for 1.1, 1.2 */
}
.level3 {
    counter-reset: level3; /* third level counter - for (a), (b) */
}
li {
    display: block;
}
li:not(:last-child):after, li > ol:before{
    content: " ";
    display: block;
    position: relative;
    height: 20px; /* this is filler where height should be equal to required line height */
    left: 0px; top: 100%;
}
.level1, .level2, .level3 {
    margin: 0;
    padding: 0;
}
.level1 > li, .level3 > li {
    padding-left: 40px;
}
li:before {
    margin-left: -40px;
    /* following 2 props are for consistent spacing between numbering and text */
    width: 40px;
    display: inline-block;
}
.level1 > li{
    font-weight: bold;
}
.level1 > li:before, .level1 > li * {
    font-weight: normal;
}
.level1 > li:before {
    content: counter(level1)"."; /* display current item number + dot */
    counter-increment: level1; /* increment counter everytime a new element of that level is encountered */
}
.level2 > li:before {
    content: counter(level1)"." counter(level2); /* format level 1 counter + dot + level 2 counter */
    counter-increment: level2;
}
.level3 > li:before {
    content: "(" counter(level3, lower-latin)") "; /* format ( + level3 counter + ) */
    counter-increment: level3;
}
<ol class="level1">
    <li>one
        <ol class="level2">
            <li>one dot one - has some really really lengthy text which wraps around to the next line when it overflows the width.</li>
            <li>one dot two
                <ol class="level3">
                    <li>A</li>
                    <li>B - has some really really lengthy text which wraps around to the next line when it overflows the width.</li>
                </ol>
            </li>
        </ol>
    </li>
    <li>two - has some really really lengthy text which wraps around to the next line when it overflows the width.</li>
</ol>

Feedback to comments:

  1. The {content: "\a"; white-space: pre;} trick does not quite work with inline-block elements. The inner level li tags are inline-block in our case (for reasons explained above in 2nd paragraph) and hence that particular trick doesn’t work. The alternate is to insert a blank filler line with height of the line equal to the required line-height and position it below the li tag. Note that the selector used is li:not(:last-child):after because we don’t need an extra line break after the last li (if we do not do it, we will have double line space after the inner li ends). This is a CSS3 selector and so might not work with lower versions of IE. If you need to support those versions also then we would need to tweak it further (or simpler would be to use br).

  2. You were on the correct path here, but the :before pseudo-element (which has the numbering) is not on the element with class="level1". It was on the .level1 > li and hence doing a reset of font-weight for selector .level1 > li:before, .level1 > li * would fix it. As you would have already known/guessed, .level1 > li * means every element under the level one li.

4

solved Using CSS to style a numbered list