Stop Faking Lists - Accessible, Maintainable Patterns That Work

Stop faking HTML Lists: Accessible, maintainable patterns that work

We may earn an affiliate commission through purchases made from our guides and tutorials.

Use semantic HTML. Browsers and assistive tech expose built-in roles and relationships for <ul>, <ol>, and <li>. Screen readers typically announce a “list” and the item count, and let users jump between lists and items. When you replace lists with <p> plus <br>, you remove that structure and risk failing WCAG 2.2 Success Criterion 1.3.1 (Info and Relationships). The fix is simple: write real lists and let CSS handle presentation.

The core pattern

Reach for <ul> when order does not matter and <ol> when it does. Wrap each item in <li>. This alone gives you accessible names, roles, and relationships without extra attributes. If you later restyle the bullets or numbers, you retain the semantics and navigation.

<ul>
  <li>HTML</li>
  <li>CSS</li>
  <li>JavaScript</li>
</ul>

Don’t override native semantics with ARIA

Avoid adding roles to native lists. For example, <ul role="navigation"> stops being a list in the accessibility tree and becomes a landmark, which removes list behavior for screen reader users. Use a <nav> element for navigation and keep the inner list as a list. Add ARIA only when you must recreate semantics in non-list containers.

When, and only when, ARIA role="list" is reasonable

Sometimes a design uses non-list elements for layout but still contains a real list of items. If you cannot change the markup, you may add role="list" on the container and role="listitem" on each item to restore semantics. Prefer real <ul>/<ol> first; use ARIA as a fallback. If you use role="listitem", it must be owned by a role="list" (or role="group").

<section role="list">
  <div role="listitem">Item 1</div>
  <div role="listitem">Item 2</div>
  <div role="listitem">Item 3</div>
</section>

Style lists without breaking them

Customize bullets and numbers with CSS, not markup hacks. Use ::marker to style list markers, including swapping a bullet for an icon or emoji, and keep the content selectable and announced. For complex numbering or sectioned outlines, use CSS counters rather than hard-coding numbers in text. These approaches preserve semantics while giving full visual control.

ul.features ::marker { content: "🔥 "; }
ol.steps { counter-reset: step; }
ol.steps > li { counter-increment: step; }
ol.steps > li::marker { content: counters(step, ".") ". "; }

Ordered vs unordered: decide by meaning

Choose <ol> when sequence, ranking, or prerequisites matter. Choose <ul> for collections without inherent order. Do not fake numbers with text or background images; assistive tech will not expose ordering and users cannot rely on keyboard list navigation. Use the semantic element and layer on CSS for appearance.

Menus, listboxes, and “lists of actions” are not lists

Application menus and listboxes are interactive widgets with distinct keyboard and ARIA patterns. Do not repurpose <ul> as a desktop-style menu unless you implement the corresponding ARIA roles and interactions, or better, use the patterns defined in the WAI-ARIA Authoring Practices. Keep content lists as lists; build widgets as widgets.

Definition lists have a niche

Use <dl> for name–value pairs such as glossaries. Do not use it to lay out two-column content that is not truly definitional, as behavior and expectations differ across screen readers. If the relationship is not “term defines description,” stick to <ul> or structured headings and paragraphs.

Copy, paste, and maintenance

Real lists copy and paste cleanly without stray bullet characters, and they convert predictably in Markdown and CMS editors. Fake lists interleave bullets with text nodes, which often paste as literal glyphs and break numbering. Semantic lists reduce the chance of those artifacts and keep the accessibility tree consistent with what users see. (This follows from how user agents expose native list semantics to assistive tech.)

Common pitfalls and fixes

First, avoid <p> with <br> to mimic bullets; convert to <ul>/<ol> with <li>. Next, do not wrap each <li> in headings solely to style them; style the <li> or child spans instead. Finally, do not nest block elements incorrectly in list markers; put structure inside the <li>, not into the marker itself. These changes preserve valid markup and predictable announcements.

Accessibility checks to run

Verify that assistive tech announces “list” and the correct item count. Confirm that keyboard navigation moves item by item. Ensure you did not add ARIA roles that suppress native list behavior. Map the outcome to WCAG 2.2 SC 1.3.1 so information and relationships survive style changes and alternative renderings.

Quick reference

  • Use <ul>/<ol> with <li> for lists; prefer native semantics over ARIA.
  • Style with ::marker and CSS counters; avoid hard-coded bullets/numbers.
  • Do not add role="navigation" or other roles to a <ul> unless you intend to replace its list semantics. Use <nav> for landmarks.
  • If you cannot change the DOM, consider role="list"/role="listitem" as a last resort, ensuring correct ownership.
  • For interactive menus/listboxes, follow WAI-ARIA Authoring Practices instead of generic lists.

Before and after

Bad (fake list)

<p>
   HTML<br>
   CSS<br>
   JavaScript
</p>

Good (semantic list)

<ul class="stack">
  <li>HTML</li>
  <li>CSS</li>
  <li>JavaScript</li>
</ul>

.stack ::marker { content: "• "; } /* change to emoji if you like */

Wrap-up

Start with the correct element, keep semantics intact, and let CSS handle decoration. You give screen reader users list navigation and counts, you satisfy WCAG’s structural requirements, and you keep your code easier to style, copy, and maintain. From there, you can iterate with ::marker, counters, and component patterns without sacrificing accessibility.

Assumptions: You target modern evergreen browsers where ::marker and CSS counters are supported; for legacy environments, fall back gracefully with unstyled markers while keeping semantic HTML intact.

Was this helpful?

Thanks for your feedback!
Alex is the resident editor and oversees all of the guides published. His past work and experience include Colorlib, Stack Diary, Hostvix, and working with a number of editorial publications. He has been wrangling code and publishing his findings about it since the early 2000s.

Leave a comment

Your email address will not be published. Required fields are marked *