How to fix a list with the wrong children, or a list item outside a list?

list page

The issue

When a list is marked up properly, a screen reader says “list, five items” before reading it, and lets the listener move from item to item or skip the list. When the markup is broken, the count is wrong, items are read as loose text, or the list isn’t announced as one. Navigation menus, feature lists and step-by-step instructions all lose the structure that made them easy to follow.

The cause is one of two structural mistakes. Either a <ul> or <ol> has something other than <li> directly inside it, such as a <div> wrapper or a heading, or an <li> sits somewhere other than inside a list. WCAG 1.3.1 requires structure conveyed visually to be available in the markup too.

Scope

Two rules check lists:

  • list checks each visible <ul> and <ol>. Only <li>, <script> and <template> may be direct children; anything else is reported, naming the elements found. A list given a role other than list is skipped.
  • listitem checks each visible <li>. Its direct parent must be a <ul>, <ol> or <menu>, or carry role="list".

Elements hidden with hidden, aria-hidden="true", or an inline display: none or visibility: hidden, on the element or an ancestor, are skipped.

How to check you have it

  • In probeo: the page reports list, “<ul> and <ol> must only directly contain <li>, <script> or <template> elements”, or listitem, “<li> elements must be contained in a <ul> or <ol>”. Both have serious impact and map to WCAG 1.3.1.
  • By hand: in the browser’s DevTools, inspect the list and look at its direct children. Every one should be an <li>, and every <li> should sit directly inside a list.
  • With a tool: axe DevTools reports both rules by the same names, and Lighthouse’s accessibility audit includes them.

The fix

Make every direct child of a list an <li>, and put every <li> directly inside a list.

Move wrappers inside the item rather than around it. If a component library wraps each item in a <div>, change it to render the <li> as the outer element.

<!-- Before: a div between the list and its items -->
<ul>
  <div class="item"><li>Free shipping</li></div>
</ul>

<!-- After -->
<ul>
  <li class="item">Free shipping</li>
</ul>

Done when: no list has a non-item child and no item sits outside a list, and the check above comes back clean.

Do not

  • Do not put headings or paragraphs directly inside a <ul>; put them inside an <li> or before the list.
  • Do not use <li> for things that aren’t list items just to get its styling.
  • Do not fix it by adding role="none" to the list, which removes the structure instead of repairing it.

FAQ

Why does a wrapper div matter if the page looks right?

Screen readers don’t see the layout; they see the structure. A <div> between a list and its items breaks the parent-child relationship the list depends on, so the items may stop being counted as part of it.

Is a navigation menu a list?

Usually, yes: a <nav> holding a <ul> of links is the common pattern, and screen readers announce how many links it has. The same structural rules apply.

What about a list built from divs with ARIA roles?

role="list" and role="listitem" can describe a list built from other elements. A native <ul> or <ol> is simpler and needs no extra attributes to work.

Which product detects this

Probeo checks this on every page of your site as part of Site Clarity. This page explains one instance; the product tells you every page that has it.