Structural Role On An Interactive Element
ID |
html.interactive_element_non_interactive_role |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Html |
Tags |
accessibility, reliability |
Rationale
An interactive element keeps its behaviour whatever role it claims: the button is still clickable, the link still navigates, both are still in the tab order. Only the announcement changes, and it changes to something that says the element does nothing. A screen reader user is told this is a heading, or a decoration, and has no reason to activate it — the control becomes invisible while remaining reachable by Tab, which is the worst of both.
<button role="heading" onclick="open()">Details</button> <!-- FLAW — announced as a heading -->
<a href="/home" role="presentation">Home</a> <!-- FLAW — a link with no semantics -->
<input type="text" name="q" role="img"> <!-- FLAW — a field announced as an image -->
<button onclick="open()">Details</button> <!-- OK -->
<button role="tab" onclick="open()">Details</button> <!-- OK — still a control -->
<a role="presentation">No href</a> <!-- OK — not a link without one -->
<input type="hidden" name="csrf" role="img"> <!-- OK — a hidden field is no control -->
<button role="${kind}">Save</button> <!-- OK — settled at render time -->
role="presentation" and role="none" are the sharpest form of this: they ask for the semantics to
be stripped, and on a focusable element the specification says that request is ignored, so the markup
states an intent the browser will not honour.
The elements judged are the interactive controls: button, select, textarea, summary and
details; a and area when they carry an href; and input of any type but hidden. The roles
reported are the structural ones — headings, landmarks, lists, content and presentation. Roles whose
interactivity depends on their container (row, gridcell) and group, the implicit role of
details, are left out on purpose.
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
Drop the role: the element already exposes the right one. When the document needs the structure the role was reaching for, wrap the control in the element that provides it, so the heading is a heading and the control is a control.
<h2><button onclick="open()">Details</button></h2>
<a href="/home">Home</a>