Web Platform

Rearranging ordered lists

7 min read

When authoring Markdown, you can choose one of two list types:

Unordered:

- Milk
- Eggs
- Cheese

Ordered:

1. Milk
2. Eggs
3. Cheese

HTML also offers the definition list (dl), also referred to as description lists. These are useful for glossaries. Think of them as as key-value lists. Here’s what they look like for reference:

<dl>
  <dt>Milk</dt>
  <dt>Eggs</dt>
  <dt>Cheese</dt>
  <dd>Delicious ingredients and standalone staples.</dd>
</dl>
Milk
Eggs
Cheese
Delicious ingredients and standalone staples.

Unordered lists are relatively straightforward. Definition lists are less common and have an interesting backstory about them for another time.

Ordered lists on the other hand have quite a lot going on. What do you do if you need to reverse its order? Perhaps start from a negative number? Or skip over integers in its sequence? Is it even possible to go from 10 back to 5 then to 20? Surely, given it’s part of HTML, it simply being a “bad idea” would not prevent me from doing so?

First, observe how ordered lists work in Markdown. I already covered the basic example above. What happens if you start from a number that isn’t 1?

1. Milk
3. Cheese

The 3 is ignored and turned into a 2. If you look at the HTML, you can see it’s just two <li>s in order:

<ol>
  <li>Milk</li>
  <li>Cheese</li>
</ol>
-1. Cheese
0. Eggs
1. Milk
0. Chocolate

Oops. Does not compute. Neither -1 nor 0 start a list. 0 can be used to continue a list, however.

What happens if you start from a positive integer above 1?

3. Cheese
2. Eggs
1. Milk
10. Chocolate

Interesting. Starting the list past 1 creates a sequential list with that number as the starting point, but non-incremental values continue to be ignored.

If you observe the HTML here, you can actually see an attribute that you’ve never come across: start

<ol start="3">
  <li>Cheese</li>
  <li>Eggs</li>
  <li>Milk</li>
  <li>Cheese</li>
</ol>

Time to take a closer look at MDN to see what’s possible.

Obscure HTML ordered list attributes

Read section Obscure HTML ordered list attributes

Much to my surprise, you can find there are three attributes on <ol> which I’ve never seen before. Here they are:

Although unsupported by Markdown, in HTML, a list can actually be reversed semantically. The reversed attribute will make it render from bottom-to-top:

<ol reversed>
  <li>Milk</li>
  <li>Eggs</li>
  <li>Cheese</li>
</ol>
  1. Milk
  2. Eggs
  3. Cheese

It is also technically possible to reverse a list using CSS. The :before selector and the very complicated counters feature (worthy of a future post) can be used to inject content before <li> elements. This is not semantic though. Use reversed if you can.

<ol
  class="!list-inside !list-none [&>li]:before:content-[counter(li)_'._'] [&>li]:before:[counter-increment:li_-1]"
  style="counter-reset: li 4;"
>
  <li>Milk</li>
  <li>Eggs</li>
  <li>Cheese</li>
</ol>
<!--
The Tailwind class compiles to approximately this CSS:
    ol > li:before {
        content: counter(li) ". ";
        counter-increment: li -1;
    }
'li' is just the name used by `counter-reset`. It can be anything you choose.
-->
  1. Milk
  2. Eggs
  3. Cheese

As you’ve seen just now, this attribute is actually used by Markdown implementations. When present, it indicates the starting value of a list:

<ol start="3">
  <li>Milk</li>
  <li>Eggs</li>
  <li>Cheese</li>
</ol>
  1. Milk
  2. Eggs
  3. Cheese

Negative integers are also fully supported. A slight quirk is that negative zero is simply rendered as zero:

<div class="columns-2">
  <ol start="-10">
    <li>Milk</li>
    <li>Eggs</li>
    <li>Cheese</li>
  </ol>
  <ol start="-0">
    <li>Milk</li>
    <li>Eggs</li>
    <li>Cheese</li>
  </ol>
</div>
  1. Milk
  2. Eggs
  3. Cheese
  1. Milk
  2. Eggs
  3. Cheese

start only supports decimal integers. Non-decimal characters are truncated, including decimal points.

<div class="columns-2">
  <ol start="0xff">
    <li>Milk</li>
    <li>Eggs</li>
    <li>Cheese</li>
  </ol>
  <ol start="0.9">
    <li>Milk</li>
    <li>Eggs</li>
    <li>Cheese</li>
  </ol>
</div>
  1. Milk
  2. Eggs
  3. Cheese
  1. Milk
  2. Eggs
  3. Cheese

Although not officially deprecated, you should use list-style-type instead. It can be very convenient for HTML embedded inside of Markdown, however. Latin letters and Roman numerals, both lower and uppercase, are supported. list-style-type supports practically every list type through the use of @counter-styles, but these cannot be referenced from HTML.

<div class="columns-4 [&>ol]:!list-[revert-layer]">
  <ol type="A">
    <li>Milk</li>
    <li>Eggs</li>
    <li>Cheese</li>
  </ol>
  <ol type="a">
    <li>Milk</li>
    <li>Eggs</li>
    <li>Cheese</li>
  </ol>
  <ol type="I">
    <li>Milk</li>
    <li>Eggs</li>
    <li>Cheese</li>
  </ol>
  <ol type="i">
    <li>Milk</li>
    <li>Eggs</li>
    <li>Cheese</li>
  </ol>
</div>
  1. Milk
  2. Eggs
  3. Cheese
  1. Milk
  2. Eggs
  3. Cheese
  1. Milk
  2. Eggs
  3. Cheese
  1. Milk
  2. Eggs
  3. Cheese

Overriding the value of list items

Read section Overriding the value of list items

The <li> element also offers a hidden gem: the value attribute. This can be used for explicitly setting the numerical value of a given item. Subsequent list items will start counting from this integer.

<ol>
  <li>Milk</li>
  <li value="10">Eggs</li>
  <li>Cheese</li>
</ol>
  1. Milk
  2. Eggs
  3. Cheese

Like with <ol start>, value also supports negative integers:

<ol>
  <li>Milk</li>
  <li value="-10">Eggs</li>
  <li>Cheese</li>
</ol>
  1. Milk
  2. Eggs
  3. Cheese

Explicit values do not need to be unique, and can make the list go all over the place:

<ol>
  <li>Milk</li>
  <li value="-1">Eggs</li>
  <li>Cheese</li>
  <li value="10">Chocolate</li>
  <li value="1">Butter</li>
</ol>
  1. Milk
  2. Eggs
  3. Cheese
  4. Chocolate
  5. Butter

This attribute has no effect on unordered lists, unless it explicitly sets its list-style-type to a counter-style value (at which point it’s visually, although not semantically, an ordered list).

<div class="columns-2">
  <ul>
    <li>Milk</li>
    <li value="10">Eggs</li>
    <li>Cheese</li>
  </ul>
  <ul class="!list-decimal">
    <li>Milk</li>
    <li value="10">Eggs</li>
    <li>Cheese</li>
  </ul>
</div>
  • Milk
  • Eggs
  • Cheese
  • Milk
  • Eggs
  • Cheese