Skip to content

[a11y] Boot navigation: listitem elements are not contained by a list role, failing aria-required-parent #78803

Open

Description

@hi0001234d

Description

Description

In the new navigation screens (e.g. Site Editor admin.php?page=site-editor-v2 — Pages, Styles, Navigation views), each navigation entry is rendered via the Item component, which applies role="listitem". However, these items are not wrapped in any element with role="list" — their direct parent is a layout <div> with no list semantics.

Per WAI-ARIA, an element with role="listitem" requires an owning element with role="list". Without it, assistive technologies cannot convey the list structure (item count, position), and the markup fails the aria-required-parent check (WCAG 1.3.1, Info and Relationships).

Element: .boot-navigation-item (rendered as <div role="listitem" class="…itemWrapper">)
Current structure (simplified):

  <div data-wp-component="HStack">…title…</div>
  <div role="listitem">…Home…</div>
  <div role="listitem">…Styles…</div>
  <div role="listitem">…Pages…</div>
</div>

Proposed fix

I have a working fix ready for this issue — it wraps the navigation items in a role="list" container so each listitem has its required parent, restoring valid list semantics for screen readers on the navigation screen. I can share a brief outline or open a PR once the approach is confirmed.

Before fix applied
Image

After fix applied
Image

Step-by-step reproduction instructions

Step-by-step reproduction instructions

Open the Site Editor navigation (admin.php?page=site-editor-v2).
Inspect any navigation entry (.boot-navigation-item).
Observe it has role="listitem" but no ancestor with role="list".

Screenshots, screen recording, code snippet

No response

Environment info

No response

Please confirm that you have searched existing issues in the repo.

  • Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

  • Yes

Please confirm which theme type you used for testing.

  • Block
  • Classic
  • Hybrid (e.g. classic with theme.json)
  • Not sure

Activity

  1. added
    [Type] BugAn existing feature does not function as intended
    on May 29, 2026
  2. t-hamano commented on Jun 2, 2026

    @t-hamano
    Contributor

    I would like to reopen this issue as #78829 did not completely fix it. See #78829 (review)

    Also, note that DropdownItem is not a list item. This means that if parent_type is dropdown, the tree will look like this:

    <div role="list">
      <div class="boot-dropdown-item">
        <div role="listitem">
          <button class="boot-navigation-item"></button>
        </div>
      </div>
    </div>

    This is a bit tricky to fix, so we can ignore this issue for this PR.

  3. hi0001234d commented on Jun 23, 2026

    @hi0001234d
    ContributorAuthor

    Picking this up. Confirmed the current structure in trunk:

    <div role="list">
      <div class="boot-dropdown-item">              <!-- no role -->
        <div role="listitem">…toggle button…</div>
        <div class="boot-dropdown-item__children">  <!-- no role -->
          <div role="listitem">…nav item…</div>
        </div>
      </div>
    </div>

    The boot-dropdown-item and children wrappers sit between role="list" and the listitems, so the toggle and child items are orphaned from their list parent.

    Proposed fix make the dropdown a single listitem with a nested list for its children:

    <div role="list">
      <div class="boot-dropdown-item" role="listitem">
        <button>…toggle…</button>
        <div class="boot-dropdown-item__children" role="list">
          <div role="listitem">…nav item…</div>
        </div>
      </div>
    </div>

    Concretely, in dropdown-item/index.tsx:

    1. Add role="listitem" to the boot-dropdown-item wrapper.
    2. Pass role="presentation" to the toggle Item so its default listitem wrapper doesn't create a nested listitem (the button stays the interactive toggle).
    3. Add role="list" to the children motion wrapper so the child NavigationItems are owned correctly.

    Does the above approach look right?

    (One note on testing: since no menu item in default Gutenberg registers with parent_type: 'dropdown', this path doesn't render in the default editor, so I can't visually or axe-verify it the change would be code-level for correctness.)

  4. Mustafabharmal commented on Oct 6, 2026

    @Mustafabharmal
    Contributor

    Since #82509, Item only applies role="listitem" inside an ItemGroup, so on current trunk the boot navigation items render with no role at all, and the role="list" added in #78829 owns no list items. That also means the role="presentation" workaround for the dropdown toggle isn't needed anymore.

    My proposed fix is to set role="listitem" explicitly on NavigationItem and DrilldownItem, and to make DropdownItem's wrapper a listitem with its children container as a nested role="list". @hi0001234d, I saw you picked this up. Happy to leave it to you, or to open a PR if you're no longer working on it. Let me know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

[Focus] Accessibility (a11y)Changes that impact accessibility and need corresponding review (e.g. markup changes).[Package] Boot/packages/boot[Status] In ProgressTracking issues with work in progress[Type] BugAn existing feature does not function as intended

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions