Lists are one of the most common markup elements. Navigation menus, post tags, how-to steps, glossaries — all of these are lists. HTML has three types, each with its own purpose.
Unordered list <ul>
Use when the order of items doesn’t matter:
<ul>
<li>Python</li>
<li>FastAPI</li>
<li>PostgreSQL</li>
<li>Docker</li>
</ul>
The browser adds bullet markers and left padding by default. CSS will let you change the appearance completely later.
Ordered list <ol>
Use when order matters — instructions, steps, rankings:
<ol>
<li>Install Python 3.12</li>
<li>Create a virtual environment</li>
<li>Install dependencies: <code>pip install -r requirements.txt</code></li>
<li>Start the server: <code>python manage.py runserver</code></li>
</ol>
<ol> attributes:
| Attribute | Value | Description |
|---|---|---|
start |
number | Start numbering from a value other than 1 |
reversed |
— | Reverse the numbering |
type |
1, A, a, I, i |
Numbering style |
<ol start="3" type="A">
<li>Third item is labelled C</li>
<li>Fourth — D</li>
</ol>
Definition list <dl>
Good for glossaries, FAQs, and term–description pairs:
<dl>
<dt>Coroutine</dt>
<dd>A function whose execution can be paused and resumed. Declared with <code>async def</code>.</dd>
<dt>Event loop</dt>
<dd>The core mechanism of asyncio that schedules and runs coroutines.</dd>
<dt>Awaitable</dt>
<dd>An object that can be used with the <code>await</code> operator: a coroutine, Task, or Future.</dd>
</dl>
Nested lists
Lists can be nested — each nested list must live inside an <li> element:
<ul>
<li>Backend
<ul>
<li>Python
<ul>
<li>FastAPI</li>
<li>Django</li>
</ul>
</li>
<li>Go</li>
</ul>
</li>
<li>Frontend
<ul>
<li>HTML / CSS</li>
<li>JavaScript</li>
</ul>
</li>
</ul>
Don’t over-nest: three levels is a reasonable maximum.
Navigation as a list
A navigation menu is semantically a list of links. The standard pattern:
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/posts/">Articles</a></li>
<li><a href="/about.html">About</a></li>
<li><a href="/contact.html">Contact</a></li>
</ul>
</nav>
CSS removes the bullets and turns the vertical list into a horizontal bar.
Styling list markers with CSS
/* Remove default markers */
ul.tags {
list-style: none;
padding: 0;
margin: 0;
display: flex;
gap: 0.5rem;
}
/* Custom markers via ::before */
ul.checklist li {
list-style: none;
padding-left: 1.5rem;
position: relative;
}
ul.checklist li::before {
content: "✓";
position: absolute;
left: 0;
color: #22c55e;
}
/* Ordered list with custom numbers */
ol.steps {
list-style: none;
counter-reset: steps-counter;
}
ol.steps li {
counter-increment: steps-counter;
padding-left: 2.5rem;
position: relative;
}
ol.steps li::before {
content: counter(steps-counter);
position: absolute;
left: 0;
background: #3b82f6;
color: white;
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
}
Example: DevBlog tag sidebar
<aside class="sidebar">
<h3>Tags</h3>
<ul class="tags">
<li><a href="/tags/python/">Python</a></li>
<li><a href="/tags/fastapi/">FastAPI</a></li>
<li><a href="/tags/asyncio/">asyncio</a></li>
<li><a href="/tags/docker/">Docker</a></li>
</ul>
<h3>Recent posts</h3>
<ol>
<li><a href="/posts/decorators/">Python Decorators</a></li>
<li><a href="/posts/asyncio-guide/">The asyncio Guide</a></li>
<li><a href="/posts/pydantic-v2/">Pydantic v2: what's new</a></li>
</ol>
</aside>
When in doubt about which list to use: if order matters — <ol>, if you have term–value pairs — <dl>, in all other cases — <ul>.
💬 Comments (0)
No comments yet
Be the first to share your opinion about this article!