Interactive Role On A Structural Element
ID |
html.non_interactive_element_interactive_role |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Html |
Tags |
accessibility, reliability |
Description
Flags an interactive ARIA role on an 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 — an 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.
<h2 role="tab">Details</h2> <!-- FLAW — a heading is not a tab -->
<p role="textbox">Type here</p> <!-- FLAW — a paragraph is not an input -->
<li role="button" onclick="save()">Save</li> <!-- FLAW — a list item is not a control -->
<div role="button" tabindex="0">Save</div> <!-- OK — div has no role to overwrite -->
<button onclick="save()">Save</button> <!-- OK — the element for the job -->
<li role="option">One</li> <!-- OK — a listbox item is built this way -->
<ul role="listbox">...</ul> <!-- OK — a composite widget container -->
<p role="${kind}">Save</p> <!-- OK — settled at render time -->
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 not judged either — inside a grid or treegrid their interactive roles are exactly right.
The roles reported are the ones whose own element is the focus target: button, checkbox,
menuitem, tab and their kin. A composite widget role (grid, listbox, tablist) manages focus
for its children rather than taking it, so it is not reported. One pairing is exempt as well: an li
carrying an item role of a composite widget (option, menuitem, tab, treeitem), which is the
published way to build the items of a listbox, a menu or a tree.
Only a role written as plain HTML with a literal value is judged. A directive such as th:role, a
template expression, a valueless role and a namespaced element are all left alone.
Remediation
Use the HTML element that already has the behaviour the role claims — a button for role="button",
an input for role="textbox" — and keep the structural element around it if the document needs it.
When a custom control really is required, build it on a div or a span, which have no semantics to
lose, and give it a tabindex and key handling to match the role.
<h2><button onclick="open()">Details</button></h2>
<div role="button" tabindex="0" onclick="save()" onkeydown="onKey(event)">Save</div>