Applying role="button" to a <button> element, or role="navigation" to a <nav>, creates conflicting semantics in the accessibility tree that screen readers must reconcile — some resolve the conflict predictably, others do not. More dangerously, using <div role="heading"> instead of an <h2> loses the implicit heading level, which assistive technology uses for document outline navigation. WCAG 2.2 SC 4.1.2 (Name, Role, Value) requires that roles accurately represent the element's function. SC 1.3.1 (Info and Relationships) requires structure to be programmatically determinable. Section 508 2018 Refresh 502.3.1 enforces the same. The practical failure mode: screen reader users who navigate by headings (the most common navigation strategy) find that custom role="heading" elements without aria-level are announced inconsistently or omitted from the heading list entirely.
Low because redundant roles rarely block usage outright but create unnecessary complexity and occasional screen reader inconsistencies across browsers.
Prefer native HTML elements. ARIA roles should only appear on <div> or <span> elements when no semantic native equivalent exists.
// Use native elements — they carry implicit roles
<button>Submit</button> // implicit role="button"
<nav>...</nav> // implicit role="navigation"
<h2>Section Title</h2> // implicit role="heading" aria-level="2"
<ul><li>Item</li></ul> // implicit role="list"
// Reserve ARIA for genuine custom widgets
<div
role="tablist"
aria-label="Product options"
>
{tabs.map((tab, i) => (
<div
key={tab.id}
role="tab"
aria-selected={i === activeTab}
tabIndex={i === activeTab ? 0 : -1}
>
{tab.label}
</div>
))}
</div>
Search your codebase for role="button", role="navigation", role="heading", and role="list" applied to elements that already carry those semantics natively, and remove the redundant attributes.
ID: accessibility-basics.semantic-structure-aria.aria-role-semantics
Severity: low
What to look for: Enumerate every relevant item. Check for redundant ARIA roles applied to native HTML elements. For example, <button role="button"> is redundant; <div role="heading"> is semantically awkward. Examine whether semantic HTML is preferred over ARIA when possible.
Pass criteria: At least 1 of the following conditions is met. ARIA roles do not contradict HTML semantics. Native semantic elements are used when available. ARIA roles are only applied to <div>, <span>, or custom elements where native semantics don't exist.
Fail criteria: Native elements have redundant roles applied (e.g., <button role="button">), or ARIA roles are used where semantic HTML would be more appropriate.
Skip (N/A) when: Never — semantic consistency applies to all pages.
Detail on fail: Identify the problematic role usage. Example: "<button role='button'> used in HeaderMenu component — redundant. <div role='heading'> used instead of <h2> — semantically awkward."
Remediation: Use semantic HTML when possible; reserve ARIA for custom components:
{/* Good: Use semantic HTML */}
<button>Click me</button>
<h1>Page Title</h1>
<nav>{links}</nav>
{/* Avoid: Redundant ARIA */}
<button role="button">Click me</button>
<div role="heading">Page Title</div>
<div role="navigation">{links}</div>
{/* Acceptable: ARIA for custom components with no semantic equivalent */}
<div role="tablist" aria-label="Tabs">
{tabPanels}
</div>