Paragraphs and Lists
Chapter 06 was about the skeleton: headings, and the outline they build. This chapter is about the content that hangs off that skeleton. Paragraphs and lists are the elements you’ll write more than any other, and most of the time you’ll write them on autopilot. That’s fine until you reach for one of them as a layout tool instead of a meaning tool. Then it stops being fine, and the failure is the kind that doesn’t show up until a screen reader reads the page back.
What <p> is, and what it isn’t
A <p> element marks a paragraph: a distinct unit of prose, one coherent thought. That’s the whole definition. It carries semantic weight. A screen reader treats each <p> as a navigable chunk of reading, and the boundary between two paragraphs tells the listener one thought has ended and another has started.
What <p> is not is a spacing device. You will see markup that uses empty <p> tags to push two elements apart vertically. It works visually, which is why people do it. It’s still wrong. An empty paragraph is a structural element that represents nothing, and some screen readers will announce it anyway, so the user hears a beat of silence labeled as content. Vertical space is a styling concern. Set it with margin and let the CSS own it.
There’s a second misuse that’s harder to spot, because the browser hides it from you. You cannot nest a block-level element inside a <p>. A <div> inside a <p>, a <ul> inside a <p>, a <figure> inside a <p>: all invalid HTML. The browser won’t show you an error. It silently closes the <p> before the block element and moves on, which means the DOM the browser builds no longer matches the HTML you wrote.
<!-- what you wrote -->
<p>
Here is some text.
<ul>
<li>A list item</li>
</ul>
And here is more text.
</p>
<!-- what the browser actually builds -->
<p>Here is some text.</p>
<ul>
<li>A list item</li>
</ul>
And here is more text.That trailing sentence is now loose text outside any paragraph, and your CSS targeting p won’t touch it. This is the source of bugs that look impossible until you open DevTools and see markup you never wrote.
<ul> versus <ol>: when order is part of the meaning
There are two list elements for ordered content, and the choice between them is about semantics, not appearance.
<ul> is an unordered list. The items belong together, but their sequence carries no meaning. A set of product features, the links in a footer. Shuffle them and nothing breaks.
<ol> is an ordered list. The sequence is part of the content. Steps in an installation process, a ranked list of finishers. Shuffle these and the meaning is gone.
Here’s the test that trips people up: the decision has nothing to do with whether you want bullets or numbers on the screen. Bullets and numbers are the default styling, and you can change either with CSS. If you have a genuinely ordered list but don’t want it numbered visually, you still use <ol> and set list-style: none. The semantics stay correct and the numbers disappear. Reaching for <ul> because “I don’t want numbers” gets you the wrong element for a visual reason.
This matters because the list type is announced. A screen reader says “list, 4 items” for a <ul> and “numbered list, 4 items” for an <ol>. That word “numbered” tells the listener the sequence is meaningful, so they should pay attention to position. Pick the wrong element and you’ve told them something false.
What goes inside <li>
A common assumption is that <li> holds text and nothing else. It holds more than that. The content model for <li> is flow content, which means a list item can contain paragraphs, headings, images, even another whole list. Per MDN, <li> accepts the same broad range of content a <div> does.
So a list item with two full paragraphs inside it is valid and sometimes correct:
<ul>
<li>
<h3>Self-hosting your fonts</h3>
<p>Downloading the font files into your project removes a
third-party request and a point of failure.</p>
<p>It also means the font is under version control with the
rest of the design system.</p>
</li>
<li>
<h3>Loading fonts from a CDN</h3>
<p>Faster to set up, but you've added a dependency on a
service you don't control.</p>
</li>
</ul>Knowing this frees you from a clumsy workaround: building a “list” out of stacked <div> elements because the items are too rich to be “just text.” The items were never required to be just text.
Nested lists
A list inside a list is valid, and for some content it’s the only honest markup. An outline with sub-points, a navigation menu where a top-level item opens a submenu, a table of contents with chapters and sections. The nesting is structural, and it should be marked as structure.
The rule for nesting is precise: the nested <ul> or <ol> goes directly inside an <li>, as a sibling of that item’s text. It does not go inside a <p> that’s inside the <li>, and it is never a direct child of the outer list. A <ul> whose direct child is another <ul> is invalid; only <li> is allowed as a direct child of a list.
<ul>
<li>Typography
<ul>
<li>Type scale</li>
<li>Measure and leading</li>
</ul>
</li>
<li>Color</li>
</ul>The submenu lives inside its parent <li>. That nesting is what tells assistive technology the relationship: these two items belong under “Typography,” not beside it.
Description lists: <dl>, <dt>, <dd>
This is the list family people skip, partly because it’s rarely taught and partly because the names are opaque. A description list pairs terms with their descriptions. <dl> wraps the whole thing. <dt> is a term. <dd> is the description of the term above it.
The honest use cases are specific. A glossary, where each <dt> is a word and each <dd> is its definition. A metadata block, where the terms are labels like “Author” and “Published” and the descriptions are the values. An FAQ, where each <dt> is a question and the <dd> after it is the answer.
<dl>
<dt>Leading</dt>
<dd>The vertical space between lines of text, set in CSS as
line-height.</dd>
<dt>Measure</dt>
<dd>The width of a column of text, usually counted in characters
per line.</dd>
<dt>Tracking</dt>
<dd>Uniform spacing applied across a run of letters, set with
letter-spacing.</dd>
</dl>One term can take several descriptions, and several terms can share one description. The structure is flexible because real glossaries are.
The misuse is reaching for <dl> whenever you want a two-column grid of labels and values, with no actual term-and-description relationship in the content. If the left column doesn’t define or describe the right column, it isn’t a description list. It’s just a layout, and a layout is built with CSS Grid, not borrowed semantics.
A line break is not a paragraph break
One last misuse worth naming on its own: using <br> to separate paragraphs. The <br> element is a line break inside a single block of text. It exists for content where the line ending is part of the meaning and the lines are not separate paragraphs. The lines of a poem. The lines of a postal address.
<p>
Worlds End Books<br>
120 Main Street<br>
Belchertown, MA 01007
</p>That’s correct: one address, one paragraph, line breaks that belong to it. Two <br> tags stacked to gap two paragraphs apart is the same mistake as the empty <p>, dressed differently. Two paragraphs are two <p> elements, and the space between them is margin.
The pattern under all of this is the same one from Chapter 06. The element you choose is a claim about what the content means. Choose it for the meaning, then style it however the design needs. Chapter 08 moves to a narrower case where the same discipline applies: quotation and citation, and the elements that mark someone else’s words as theirs.