An accessible list uses the semantic HTML elements <ul> (unordered), <ol> (ordered), and <li> (list item) so screen readers can announce the list type, the total item count, and each item's position. Visually styling <div> or <p> elements to look like a list strips away every one of those signals.
<ul> is read as "List with 5 items" on entry; each <li> is read as "1 of 5," "2 of 5," and so on. None of that fires for a styled <div>.list-style: none causes Safari + VoiceOver to stop announcing list semantics. Adding role="list" on the <ul> restores them.<ol> and <ul> markup. A "How to" page with a real <ol> is far more likely to win the snippet than one with line-broken paragraphs.<li> must be a direct child of <ul>, <ol>, or <menu>. Any other parent is invalid HTML.Think of a list element the way you think of the "ingredients" heading in a recipe. Strip the heading and you still have a wall of words — but no one can find the part they came for. <ul> and <ol> are that heading, but for the structure underneath.
<ul>in NVDA produces "List with 5 items." That single phrase tells the user how much content is coming, whether to skip it, and where they are when they navigate inside it.<li>is announced with its index ("1 of 5," "2 of 5"). Without semantic markup, a user near the bottom has no idea how much further the list goes.<br>-separated paragraphs offer no such shortcut.<ol>for "How to do X" is dramatically more likely to be cited as bullets in an AI answer.<ul>/<ol>. Indenting <div>s with CSS communicates nothing to assistive tech.HTML provides three list types. Choose the one that matches the relationship between items.
<ul> (unordered list): bullets. Order does not change the meaning. Examples: features, ingredients, key points.<ol> (ordered list): numbers. Order is meaningful. Supports start, reversed, and type attributes. Examples: steps, ranked results, instructions.<li> (list item): one entry. Must be a direct child of <ul>, <ol>, or <menu>.<dl> (description list): term/description pairs (covered in a separate article).<!-- Bad: <br>-separated text. No list semantics at all. -->
<p>
Apples<br>
Oranges<br>
Bananas
</p>
<!-- Bad: <div>s styled to look like a list. -->
<div class="list">
<div>Apples</div>
<div>Oranges</div>
<div>Bananas</div>
</div>
<!-- Bad: <li> outside of a <ul> or <ol>. Invalid HTML. -->
<li>Apples</li>
<li>Oranges</li>
<!-- Good: unordered list -->
<ul>
<li>Apples</li>
<li>Oranges</li>
<li>Bananas</li>
</ul>
<!-- Good: ordered list with start attribute -->
<ol start="3">
<li>Preheat the oven to 180C.</li>
<li>Mix the dry ingredients.</li>
<li>Fold in the wet ingredients.</li>
</ol>
<!-- Good: nested list -->
<ul>
<li>
Fruits
<ul>
<li>Apples</li>
<li>Oranges</li>
</ul>
</li>
<li>Vegetables</li>
</ul>
<!-- Good: bulletless list with VoiceOver workaround -->
<ul role="list" class="no-bullets">
<li>Item one</li>
<li>Item two</li>
</ul><br>-separated text or styled <div>s, and pairs each issue with an AI-generated fix or one-click GitHub PR.<ul>/<ol>.aria-setsize on each item.axe-core — its list and listitem rules fail any <li> outside a list parent, and any list with non-<li> children.The fastest and most durable accessibility win for lists is also the simplest: replace <br>-separated text and <div> stacks with real <ul> or <ol> markup. Every screen reader, browser, and AI extractor benefits from this single change.
Use <ol> when order matters (steps, rankings, sequenced instructions) and <ul> when it does not (features, options, tags). Screen readers announce each appropriately, and AI engines treat numbered steps differently from unordered bullets when extracting answers.
Remove visual bullets with CSS, never by removing the <ul>. list-style: none keeps the list semantically intact in most screen readers — except in Safari + VoiceOver (see the next tip).
.no-bullets {
list-style: none;
padding-left: 0;
}Safari + VoiceOver has a long-standing behavior where list-style: none causes the list role to be dropped from the accessibility tree. The accepted workaround is the redundant-but-effective role="list" attribute on the <ul>.
<ul role="list" class="no-bullets">
<li>One</li>
<li>Two</li>
</ul>The HTML specification requires <li> to be a direct child of <ul>, <ol>, or <menu>. Wrapping items in a <div>for layout breaks the parent/child contract that screen readers rely on for "N of M" announcements.
When an ordered list continues across page breaks or interrupts itself with prose, use <ol start="5"> instead of typing "5. Step name" into a <ul>. The numbers stay accessible and the list type remains correct.
Sub-items belong inside their parent <li>, not as siblings of it. The nested <ul> goes inside the parent <li>, after its text content.
For short list items, put the text directly inside <li>. Wrapping each one in a <p> adds an extra paragraph announcement on every item — friction with no benefit.
What's happening: a paragraph uses <br> between items so they appear on separate lines. Screen readers read it as one continuous paragraph with no item count or skip-list shortcut.
Fix: replace the paragraph with a <ul> and turn each line into an <li>. No additional CSS is required.
What's happening: the design removes bullets with list-style: none. Safari + VoiceOver responds by dropping the list role from the accessibility tree.
Fix: add role="list" to the <ul>. It is redundant in every other browser/screen-reader pair and completely correct in Safari + VoiceOver.
What's happening: a developer wraps each <li> in a <div> to apply card styling. The HTML is now invalid, and screen readers can no longer count items reliably.
Fix: apply the card styling directly to <li> with CSS. <li> can be styled exactly like a <div> — there is no reason to wrap it.
What's happening: a list of features uses <ol> because the design shows numbers. Screen readers announce them as "step 1, step 2," misleading users into thinking the order is meaningful.
Fix: use <ul> and add the numbers visually with CSS counters if needed. Reserve <ol> for genuinely sequential content.
The difference between a real list and a fake one is invisible to sighted users and dramatic to everyone else.
| Approach | Screen-Reader Announcement | SEO / AI Extraction | WCAG 1.3.1 |
|---|---|---|---|
<ul> + <li> | "List with N items" + "X of N" per item | Eligible for bulleted snippets and AI citations | Pass |
<ol> + <li> | "List with N items" + numbered position per item | Eligible for "How to" numbered snippets and step citations | Pass |
<div>s styled like a list | No structure — read as continuous text | Treated as paragraphs; rarely extracted as a list | Fail |
<br>-separated text | One paragraph; no item count, no skip shortcut | Treated as one block of prose | Fail |
<ul> with list-style: none (no role="list") | Correct in most readers; Safari + VoiceOver drops the role | Same as <ul> for SEO/AI | Partial — fix with role="list" |
On entering a <ul> with five items, NVDA announces "List with 5 items." As the user moves down, it reads each item as "1 of 5," "2 of 5," and so on. JAWS and VoiceOver behave similarly. None of these announcements fire when the "list" is built from <div>s or <br>s.
Yes. Safari intentionally drops the list role from the accessibility tree when a list has list-style: none applied — a behavior preserved through every recent macOS and iOS release. The community fix is to add role="list" back onto the <ul>, which restores the announcement.
Yes. Google's "How to" featured snippets and AI Overview answers preferentially extract numbered <ol> markup for sequential content. Using <ul> for steps reduces the chance of being chosen for that snippet, even if the visual result looks identical.
Yes — <li> is flow content and can hold paragraphs, headings, nested lists, and most other elements. Use that flexibility for genuinely structured items, but for simple labels just put the text directly in <li> without an extra <p> wrapper.
No. <li> is only valid as a direct child of <ul>, <ol>, or <menu>. A floating <li> is a parser-level error and will break the list and listitem rules in any automated audit.
Generative engines parse list semantics to recognize "these are steps," "these are options," or "these are 5 features," and they preserve that structure when citing. A page with proper <ol> markup is more likely to appear in an AI answer as actual bullets — sometimes with a direct citation back to the source — than a page where the same content lives in <br>-separated prose.
Yes. Wrap navigation links in a <ul> inside <nav>. The list provides structure (item count, position), and the <nav> provides the landmark. Apply list-style: none visually and add role="list" for the Safari/VoiceOver fix if the design has no bullets.
Lists are one of the highest-leverage accessibility wins on any page. Switching from <br>-separated text or styled <div>s to real <ul> and <ol> markup hands every screen-reader user item counts and skip shortcuts, hands Google clean snippet candidates, and hands AI engines the structure they need to cite your steps as bullets. Cost: a few minutes per template.
Run a Greadme deep scan to see every page where lists are missing real semantics, and fix them in order of traffic.