Lists — ul, ol, dl
Ordered, unordered, and description lists — nesting them correctly, and the semantic reasons to choose one over the other.
ul — A List Where Order Carries No Meaning
<ul> (unordered list) groups a set of related items where the sequence they appear in does not change what they mean. Every item lives inside a <li> (list item), and <li> is only ever valid as a direct child of <ul> or <ol> — it has no meaning sitting on its own.
<ul>
<li>Milk</li>
<li>Eggs</li>
<li>Bread</li>
<li>Coffee</li>
</ul>"Unordered" describes the semantics, not the visual rendering — by default, browsers draw a bullet point (•) in front of each <li>, which is exactly why beginners reach for <ul> whenever they want bullet points on the page. That instinct happens to be right most of the time, but the actual reasoning should run the other direction: choose <ul> because the items form an unordered collection, and the bullet styling follows naturally from that choice — not because you want bullets and are working backward from the visual result.
<ul>.ol — A List Where Sequence Is Part of the Meaning
<ol> (ordered list) is structurally identical to <ul> — the same <li> children — but it carries a semantic promise that <ul> does not: the order of the items is meaningful. Browsers render <ol> items with numbers by default, precisely because a reader needs to know an item is "step 3," not just "one of several steps."
<ol>
<li>Preheat the oven to 425°F</li>
<li>Toss the vegetables in olive oil and salt</li>
<li>Spread on a baking sheet in a single layer</li>
<li>Roast for 25 minutes, stirring halfway through</li>
</ol>Swap the order of a recipe's steps, a set of installation instructions, or a countdown, and the content is now wrong — not just visually rearranged, actually incorrect. That is the exact signal that <ol>, not <ul>, is the right element.
start, reversed, and type — controlling how ol counts
<ol> supports a few attributes that change how its automatic numbering behaves, useful anywhere the list does not simply start at 1 and count upward.
<ol start="5">
<li>Fifth step</li>
<li>Sixth step</li>
<li>Seventh step</li>
</ol>
<!-- Useful for a list that continues a sequence broken up by other content --><ol reversed>
<li>Third place</li>
<li>Second place</li>
<li>First place</li>
</ol>
<!-- Renders as: 3, 2, 1 --><ol type="A">
<li>First option</li>
<li>Second option</li>
</ol>
<!-- Renders as: A. First option B. Second option -->
<ol type="i">
<li>First</li>
<li>Second</li>
</ol>
<!-- Renders as: i. First ii. Second -->type here changes the actual numbering style rendered by the browser's default styles — this is distinct from the CSS list-style-type property covered briefly in Part 07, which can achieve similar visual results but through styling rather than a content attribute. Prefer the type attribute only when the letter/numeral choice is part of the content's actual meaning (a legal document's numbered sub-clauses, for example); reach for CSS when it is purely a visual preference.The Real Test: Does Reordering the Items Change What They Mean?
The single question that decides between <ul> and <ol> is not "do I want bullets or numbers" — it is "if I shuffled these items into a random order, would the content still be correct?" If yes, use <ul>. If shuffling it would make it wrong, incomplete, or confusing, use <ol>.
<!-- The order genuinely doesn't matter — any arrangement is equally correct -->
<ul>
<li>Wireless keyboard</li>
<li>USB-C hub</li>
<li>Laptop stand</li>
</ul>
<!-- The order IS the content — step 2 only makes sense after step 1 -->
<ol>
<li>Unplug the device from power</li>
<li>Hold the reset button for 10 seconds</li>
<li>Plug the device back in</li>
</ol>This distinction is not pedantic — screen readers announce list length and, for <ol>, each item's position ("item 2 of 4") as part of navigating the list, giving a listener genuinely useful positional information for a sequence but potentially misleading information for a collection with no real order. Search engines and other tools that parse page structure make the same distinction. Choosing the semantically correct element is not just "more correct" in an abstract sense — it changes what real software tells real users about your content.
counter-* properties or list-style-type on a <ul> — not a reason to reach for <ol> and misrepresent the content's actual structure.Nested Lists — And the li-Wrapping Rule Almost Everyone Gets Wrong at First
A list can nest inside another list — a sub-list under a main topic, a multi-level outline, a site's navigation with dropdown sub-menus. The rule that trips up nearly every beginner: the nested <ul> or <ol> must live inside an <li> of the parent list, not as a sibling sitting after it.
<ul>
<li>
Frontend
<ul>
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
</ul>
</li>
<li>Backend</li>
</ul><ul>
<li>Frontend</li>
<ul>
<li>HTML</li>
<li>CSS</li>
</ul>
<li>Backend</li>
</ul>
<!-- This is invalid HTML: ul is not a permitted child of ul directly.
Browsers will attempt to recover, but the resulting structure is
unpredictable and does not reflect the intended hierarchy. -->The mental model worth keeping: a sub-list is a more detailed breakdown of one specific item, so it belongs nested where that item lives, not floating between items at the parent level. This same containment rule applies identically to <ol>, and mixed nesting — an <ol> nested inside a <ul>'s <li>, or the reverse — is completely valid whenever the semantics genuinely call for it.
Putting more than text inside an li
An <li> is not limited to plain text — it can contain paragraphs, images, links, or any other flow content, exactly like a <div> can. The common mistake is placing block-level content like a heading or a full form directly beside a nested list without a wrapper, making it genuinely ambiguous whether that content belongs to the item above it, below it, or stands on its own.
<ul>
<li>
<strong>Senior Frontend Engineer</strong>
<p>Remote · Full-time · Posted 3 days ago</p>
</li>
<li>
<strong>Product Designer</strong>
<p>Austin, TX · Full-time · Posted 1 week ago</p>
</li>
</ul>dl — The Description List, for Term/Description Pairs
<dl> (description list — historically called a "definition list," and the name you will still hear used interchangeably) is a third, distinct list type for content that is neither a simple unordered collection nor a sequence: pairs of a term and its description. Each term is a <dt>, each description a <dd>, and both live as direct children of <dl>.
<dl>
<dt>Hoisting</dt>
<dd>JavaScript's behavior of moving variable and function declarations to the top of their scope before code executes.</dd>
<dt>Closure</dt>
<dd>A function that retains access to variables from its enclosing scope, even after that outer function has returned.</dd>
</dl>This is the correct element for a genuine glossary, a dictionary-style word list, or any content that is fundamentally a set of labeled definitions — not just any content that happens to look like a two-column layout. Reaching for <dl> purely because it visually resembles a design mockup, without the content actually being term/description pairs, misuses the element the same way choosing <ol> purely for its numbers misuses that one.
Metadata and key-value pairs — a second legitimate use
Beyond glossaries, <dl> is also the correct element for structured metadata — a set of labeled facts about something, where each label pairs with one value.
<dl>
<dt>Author</dt>
<dd>Maria Chen</dd>
<dt>Published</dt>
<dd>March 3, 2026</dd>
<dt>Reading time</dt>
<dd>8 minutes</dd>
</dl>One Term, Several Descriptions — and Several Terms, One Description
The relationship inside a <dl> is not strictly one <dt> to one <dd>. A single term can have multiple descriptions listed one after another, and a description can follow multiple consecutive terms that share it — both are valid, and both come up in real content.
<dl>
<dt>API</dt>
<dd>Application Programming Interface.</dd>
<dd>A set of rules that lets one piece of software communicate with another.</dd>
</dl><dl>
<dt>HTTP</dt>
<dt>Hypertext Transfer Protocol</dt>
<dd>The protocol web browsers and servers use to communicate — the same concept, referred to two ways.</dd>
</dl>As with the other two list types, <dl> can be nested — a <dd> can contain its own <dl> for a sub-breakdown of a definition, following the exact same containment principle from Part 04: the nested list lives inside the element it elaborates on, not beside it.
Styling Lists With CSS — A Preview, Not the Full Picture
The default bullets and numbers you have seen throughout this module are browser default styles, not something baked permanently into the elements — CSS can change or remove them entirely. Full CSS styling depth is out of scope for this HTML-focused module, but the single property worth knowing now is list-style-type, since you will reach for it almost immediately in real projects.
nav ul {
list-style-type: none;
padding-left: 0;
}This exact rule is why navigation menus built from a semantically correct <ul> of links do not visually show bullet points on a real website — the markup stays semantically honest (still genuinely an unordered list of links) while CSS controls how it looks. Removing the bullets does not change what screen readers or search engines understand about the structure; the list is still announced as a list of a certain length, exactly as before.
<div> full of <span> or <p> elements) purely to avoid dealing with default bullet styling — that trades away real semantic meaning for a styling problem CSS already solves cleanly. The full CSS styling toolkit for lists, spacing, and custom markers is covered properly once you reach the CSS phases of this track.An Accessibility Bug Report at a Seattle SaaS Company
A customer using a screen reader files a support ticket about a project-management tool's "Setup Checklist" widget: it announces as "5 items" but reads out of order every time the page reloads, making the step-by-step onboarding instructions genuinely impossible to follow. The ticket gets escalated to engineering the same day.
<div class="checklist">
<div class="checklist-item">Connect your GitHub repository</div>
<div class="checklist-item">Invite your team</div>
<div class="checklist-item">Create your first project</div>
<div class="checklist-item">Configure notifications</div>
<div class="checklist-item">Complete your profile</div>
</div>What the engineer finds
There is no list markup at all — just five sibling <div> elements styled to look like a numbered checklist with CSS ::before counters. Because nothing here is an actual <ol>, a screen reader has no idea these five blocks form a sequence, announces them as five unrelated pieces of generic content, and — since the checklist items are re-fetched from an API and rendered in whatever order the response happens to arrive — the visual numbering (drawn purely by CSS position, not tied to real content order) occasionally does not match the actual step sequence a sighted user sees either, an entirely separate bug the ticket had accidentally also surfaced.
<ol class="checklist">
<li class="checklist-item">Connect your GitHub repository</li>
<li class="checklist-item">Invite your team</li>
<li class="checklist-item">Create your first project</li>
<li class="checklist-item">Configure notifications</li>
<li class="checklist-item">Complete your profile</li>
</ol>The fix is two changes, both directly from this module: swapping the <div> wrapper and item elements for a genuine <ol>/<li> structure (Part 02), and sorting the items by an explicit step_order field from the API before rendering, rather than trusting whatever order the response happened to arrive in — since <ol>'s entire semantic promise (Part 03) is that the markup order itself is the meaningful order. The existing CSS number-styling is deleted entirely, replaced by the browser's native <ol> numbering, which now stays correctly synced to the actual content order by construction, not by two separate systems that happened to usually agree.
Four Misconceptions About HTML Lists
5 Interview Questions — With Complete Answers
List Mistakes Beginners Make Constantly
Errors and Rendering Bugs You Will Hit With Lists — And Exactly Why
🎯 Key Takeaways
- ✓ul is for collections where item order carries no meaning; ol is for sequences where order is genuinely part of the content — test it by asking whether shuffling the items would make the content wrong.
- ✓ol supports start, reversed, and type attributes for controlling how its automatic numbering behaves — distinct from CSS-driven visual numbering styles.
- ✓li is only valid as a direct child of ul or ol, but can itself contain any flow content — paragraphs, images, links, even a nested list.
- ✓A nested list must live inside the li of the parent item it elaborates on, never as a sibling positioned between li elements — that produces invalid HTML.
- ✓dl (description list) pairs dt (term) with dd (description) — correct for glossaries, FAQs, and structured label/value metadata, not a deprecated element.
- ✓A single dt can be followed by multiple dd elements, and multiple consecutive dt elements can share one dd, for synonyms or grouped terms.
- ✓Visual bullet/number styling is fully controlled by CSS (list-style-type and related properties) regardless of which list element is used — never choose ul vs. ol based on appearance alone.
What comes next
Module 07 covers HTML tables — table, thead/tbody/tfoot, th, td, colspan/rowspan — and exactly why tables should never be used for page layout, a mistake with a genuinely important history behind it.
Module 07 → Tables — Structure and Correct UsageDiscussion
0Have a better approach? Found something outdated? Share it — your knowledge helps everyone learning here.