Interactive Role On A Non-Interactive Element

ID

javascript.jsx_non_interactive_element_interactive_role

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

accessibility, jsx, reliability

Description

Reports an interactive ARIA role on a JSX element whose tag already carries a structural role.

Rationale

Overwriting a structural role with a widget role trades one wrong announcement for another. The element loses the semantics its tag gave it — a li stops being a list item, so the enclosing list stops reporting its size and position — and it gains a control’s semantics without a control’s behaviour: no focus, no key handling, no state. Screen reader users are told there is a button and find nothing that acts like one.

<li role="button" onClick={save}>Save</li>          // FLAW - a list item is not a control
<h2 role="tab">Details</h2>                         // FLAW - a heading is not a tab

<div role="button" onClick={save} tabIndex={0} />   // OK - div has no role to overwrite
<button onClick={save}>Save</button>                // OK - the element for the job
<li role="option" tabIndex={0}>One</li>             // OK - the item role of a listbox
<ul role="listbox">...</ul>                         // OK - a composite widget container

div and span are deliberately not judged: they carry no implicit role, so a widget role on them overwrites nothing, and that is the standard way to build a custom control. The table row and cell tags are excluded too, since inside a grid their interactive roles are exactly right. The roles reported are the ones whose own element is the focus target; a composite widget role such as listbox or tablist manages focus for its children rather than taking it. One pairing is exempt: a li carrying the item role of a composite widget, which is the published way to build the items of a listbox, a menu or a tree.

Remediation

Use the HTML element that already means what the role claims — a button inside the li rather than a li pretending to be one — or move the role onto a div or span and give it the focus and key handling a control needs.

© 2026 Xygeni. All rights reserved