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.