Skip to content

axe-core aria-allowed-attr: <Menu> emits aria-level / aria-setsize / aria-posinset on role="menuitem" (WAI-ARIA disallows) #8565

@Hibryda

Description

@Hibryda

Summary

The PrimeVue <Menu> component renders each item as <li role="menuitem"> with aria-level, aria-setsize, and aria-posinset attributes. Per WAI-ARIA 1.2 §6.6 (menuitem role), those three attributes are not allowed on menuitem:

  • aria-level — applies to heading, listitem, row, treeitem only.
  • aria-setsize and aria-posinset — apply to article, listitem, option, radio, row, tab, treeitem only.

axe-core 4.x flags this as a critical aria-allowed-attr violation.

Versions

Package Version observed
primevue 4.3.6
@primevue/nuxt-module 4.3.6 / 4.3.9
@primevue/forms 4.3.9
axe-core (via axe-playwright-python) bundled with 0.1.7 (axe-core 4.10)

Confirmed against two independent Nuxt 3/4 + PrimeVue applications (anonymised: "FrontendA", "FrontendB"); the violation fires on every <Menu> instance, with one violation per model[] entry.

Reproducing example

The simplest repro is the canonical PrimeVue Menu demo. Any consumer of <Menu :model="items"> reproduces this — there's no escape via slot overrides because the offending attributes are emitted by the Menu's own rendered <li> wrapper, not by user-supplied templates.

<script setup lang="ts">
import Menu from 'primevue/menu'
const items = [
  { label: 'Refresh' },
  { label: 'Export' },
  { label: 'Delete' },
]
</script>
<template>
  <Menu :model="items" />
</template>

Then in browser console (after Menu is mounted):

axe.run({ runOnly: { type: 'tag', values: ['wcag2a','wcag2aa'] } })
   .then(r => console.log(r.violations.filter(v => v.id === 'aria-allowed-attr')))

Yields one aria-allowed-attr violation per item, with target of the form #pv_id_<n>_<m> (or class-based selectors when the auto-id is suppressed) and failureSummary:

ARIA attribute is not allowed: aria-level="1" aria-setsize="3" aria-posinset="1"

Detected violation HTML (verbatim from axe nodes)

<li id="pv_id_1_11_0"
    style=""
    class="..."
    role="menuitem"
    aria-label="..."
    aria-level="1"
    aria-setsize="3"
    aria-posinset="1"
    data-pc-section="item"
    ...>

Suggested fix

Two equivalent paths:

  1. Drop the three attributes from <li role="menuitem"> — they convey no information that isn't already implicit in the menubar/menu structure or stated more correctly via aria-orientation on the parent menu.
  2. Change the role — if the hierarchy semantics matter for the implementation, switch the wrapping role to treeitem (with a parent tree) or listitem (with a parent list). But this changes downstream keyboard-nav semantics, so option 1 is less invasive.

Affected files (from a quick scan of the PrimeVue source as of 4.3.x):

  • packages/primevue/src/menu/Menu.vueMenuItem.vue template
  • packages/primevue/src/tieredmenu/TieredMenu.vue — same root <li> shape
  • packages/primevue/src/contextmenu/ContextMenu.vue — likely identical
  • packages/primevue/src/menubar/Menubar.vue — should be checked too

Workaround

Until a fix lands, consumers can use :pt to strip the disallowed attributes:

<Menu
  :model="items"
  :pt="{
    item: ({ context }) => ({
      'aria-level': null,
      'aria-setsize': null,
      'aria-posinset': null,
    })
  }"
/>

This is fragile (relies on :pt honouring null to remove attrs, which isn't formally guaranteed across PrimeVue minor releases).

Why this matters

These violations are critical-impact under axe's severity scheme — they break screen reader expectations for menu navigation. Two production frontends shipping accessibility-compliant UIs cannot pass an automated WCAG 2.1 AA audit while using <Menu> as-shipped.

Tracking

This issue is being tracked on the consumer side at:

  • vioxen/quanta-id-web#85 (companion issue + remediation work for the broader a11y sweep)

Happy to test a fix or PR a candidate diff against the components above if maintainers prefer that route.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions