no-noninteractive-element-to-interactive-role
Configuration
Rule Details
Inherently non-interactive HTML elements such as <address>, <article>,
<table>, <ul>, headings, list items, and structural landmarks are
content containers — the browser supplies them no keyboard focus, no
activation behavior, and no assistive-technology widget semantics.
Promoting them to a widget by assigning an interactive ARIA role
(button, checkbox, link, menuitem, tab, slider, …) without
also wiring up the keyboard and focus contract those roles imply produces
a control that screen-reader and keyboard users cannot meaningfully
operate.
If the surrounding context calls for an interactive widget, use the
genuine HTML element (<button>, <a href>, <input>, …) so the
browser provides the affordance, or wrap the non-interactive content
inside a separate interactive container.
The rule fires on every role JSX attribute when all of the
following hold:
- The resolved element name is in the HTML DOM set (custom React components are skipped — the rule does not know what low-level element they render).
- The attribute name is literally
role(case-sensitive); namespaced attributes such asmynamespace:roleare not checked. - The element + role pair does not match an entry in the per-element allow-list (see Rule Options below).
- The element is inherently non-interactive (e.g.
<article>,<address>,<h1>,<li>,<ul>,<table>). - The
roleattribute, when statically a literal string, resolves to an interactive (widget) role such asbutton,checkbox,link,menuitem,tab, orradio.
Examples of incorrect code for this rule:
Examples of correct code for this rule:
Rule Options
Per-element allow-list
Type: { [tagName: string]: string[] }. Default: not set. The upstream
recommended preset enables list / table allowances — ul and ol
accept listbox / menu / menubar / radiogroup / tablist /
tree / treegrid; li accepts menuitem / menuitemcheckbox /
menuitemradio / option / row / tab / treeitem; table
accepts grid; td accepts gridcell; fieldset accepts
radiogroup / presentation. The strict preset omits the allow-list
entirely.
Each key is an HTML element name and each value is an array of role
strings exempt from the rule for that element. Non-string entries are
silently ignored; non-array values cause the entire entry to be
dropped — only string[] allow-lists are honored.
Examples of correct code with { "ul": ["menu", "menubar"] }:
Examples of incorrect code with { "ul": ["menu", "menubar"] }:
Resources
- WCAG 4.1.2 — Name, Role, Value
- WAI-ARIA — Widget Roles
- WAI-ARIA — Non-interactive Content Roles
- MDN — Using ARIA: Roles, states, and properties