Redundant ARIA role
aria/role-redundant
- WCAG success criterion
- 4.1.2 (level A)
- Outcome class
- Hint only. An automated check that by design reports review hints only: its findings are marked for manual review, never as confirmed violations.
- Area
- Accessibility, Technical Robustness
- Default severity
- low
- axe-core rule
redundant-role
Why we flag this
No immediate user harm, but an unnecessary risk of inconsistency in future changes.
role attribute duplicates the tag's native implicit role.
What the rule checks
Scope. Elements in the accessibility tree with an explicit role whose implicit role does not depend on context (button, a[href], heading, table, ...).
Expectation. Reports (best practice) an explicit role that only repeats the element's implicit role (<button role="button">). A role that changes the semantics (<a href role="button">) is not redundant. role="list" on <ul>/<ol> is deliberately not reported (WebKit list-style workaround). Hidden subtrees are out of scope.
Source. ARIA in HTML section 2 ("Authors SHOULD NOT set the role to the implicit role of the element"); best practice under WCAG 2.2 SC 4.1.2, not a failure of it
Detection: shared; needs dom, ax_tree.
Failing example
The check flags this for review. Corpus case aria_roles_invalid, element button#reorder. The page may contain further findings for other rules.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Order overview - Example shop</title>
</head>
<body>
<header>
<p>Example shop</p>
</header>
<main>
<h1>Order overview</h1>
<!-- aria/role-invalid: misspelled role, the element stays generic -->
<div id="orders" role="tabel">
<p>Order 1001 - shipped</p>
</div>
<!-- aria/role-abstract: abstract roles must not be authored -->
<div id="filters" role="widget" tabindex="0">Filter orders</div>
<!-- aria/role-redundant: the role repeats the implicit role -->
<button type="button" id="reorder" role="button">Reorder</button>
<!-- aria/required-parent-missing: tab outside any tablist -->
<div id="tab-returns" role="tab" tabindex="0" aria-selected="true">Returns</div>
<!-- aria/required-children-missing: a tablist owning buttons, not tabs -->
<div id="views" role="tablist" aria-label="Views">
<button type="button" id="view-list">List</button>
<button type="button" id="view-grid">Grid</button>
</div>
<!-- aria/required-attribute-missing: custom checkbox and switch without
aria-checked, combobox without aria-expanded -->
<div id="gift" role="checkbox" tabindex="0">Gift wrap</div>
<div id="express" role="switch" tabindex="0">Express delivery</div>
<div id="carrier" role="combobox" tabindex="0" aria-controls="carriers" aria-label="Carrier">Standard</div>
<ul id="carriers" role="listbox" aria-label="Carriers">
<li role="option" id="carrier-std" aria-selected="true">Standard</li>
<li role="option" id="carrier-fast">Fast</li>
</ul>
</main>
<footer>
<p>Example shop</p>
</footer>
</body>
</html>Passing example
The check passes here and reports nothing. Corpus case aria_roles_valid. The page may contain further findings for other rules.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Order overview - Example shop</title>
</head>
<body>
<header>
<p>Example shop</p>
</header>
<main>
<h1>Order overview</h1>
<!-- Valid concrete roles -->
<div id="orders" role="table" aria-label="Orders">
<div role="rowgroup">
<div role="row" id="order-head">
<span role="columnheader">Order</span>
<span role="columnheader">Status</span>
</div>
<div role="row" id="order-row">
<span role="cell">Order 1001</span>
<span role="cell">shipped</span>
</div>
</div>
</div>
<div id="filters" role="group" aria-label="Filter orders">
<button type="button" id="filter-open">Open orders</button>
</div>
<!-- A role that changes the element's semantics is not redundant -->
<a href="/orders/export" id="export" role="button">Export</a>
<!-- Tabs inside their tablist, through a generic wrapper; the tablist
owns a tab through aria-owns as well -->
<div id="views" role="tablist" aria-label="Views" aria-owns="view-map">
<div class="tab-wrap">
<div id="view-list" role="tab" tabindex="0" aria-selected="true" aria-controls="view-panel">List</div>
</div>
<div id="view-grid" role="tab" tabindex="-1" aria-selected="false" aria-controls="view-panel">Grid</div>
</div>
<div id="view-map" role="tab" tabindex="-1" aria-selected="false" aria-controls="view-panel">Map</div>
<div id="view-panel" role="tabpanel" aria-labelledby="view-list">Three orders.</div>
<!-- A busy feed may hold a placeholder instead of articles while loading -->
<div id="loading" role="feed" aria-busy="true" aria-label="Recent orders">
<p class="spinner">Loading</p>
</div>
<!-- Required states present; native controls carry them natively -->
<div id="gift" role="checkbox" tabindex="0" aria-checked="false">Gift wrap</div>
<div id="express" role="switch" tabindex="0" aria-checked="true">Express delivery</div>
<input type="checkbox" id="insure" role="switch">
<label for="insure">Insure parcel</label>
<div id="carrier" role="combobox" tabindex="0" aria-expanded="false" aria-controls="carriers" aria-label="Carrier">Standard</div>
<ul id="carriers" role="listbox" aria-label="Carriers" hidden>
<li role="option" id="carrier-std" aria-selected="true">Standard</li>
</ul>
<hr id="divider">
<div id="static-sep" role="separator"></div>
</main>
<footer>
<p>Example shop</p>
</footer>
</body>
</html>This page is generated from the rule catalogue and the rule contracts; see therule overview for how the outcome classes are derived.