Skip to main content
Accessibility AccessLint 0.16

Interactive controls must not be nested inside each other.

About this AccessLint rule

When one interactive element is placed inside another, browsers and assistive technologies cannot reliably determine which element the user intends to activate. For example, placing a <button> inside an <a>, or an <a> inside a <button>, produces invalid HTML. Browsers attempt to recover from this by re-parsing the markup, which often results in the inner interactive element being silently removed from the accessibility tree or split into unexpected DOM fragments.

Screen reader users are most directly affected. When the inner element disappears from the accessibility tree, its label and role become invisible to assistive technology. The user may hear a single link where the developer intended a link and a button, or they may hear nothing at all for the nested control. Keyboard users also face problems: the expected focus order breaks down, and pressing Enter or Space may trigger the outer element, the inner element, or both, depending on the browser.

This rule maps to WCAG 2.1 Success Criterion 4.1.2 (Name, Role, Value) at Level A. That criterion requires every user interface component to expose its name and role to assistive technologies and to allow programmatic interaction. Nested interactive controls violate this because the browser's error recovery can strip role and name information from the inner element, making it impossible for assistive technology to convey the control's purpose.

How to fix it

Restructure the HTML so that interactive elements are siblings rather than nested inside one another. There are several common scenarios and corresponding fixes:

  • If a card or teaser component needs to be fully clickable while also containing a separate link or button, move the inner interactive element out of the outer one. You can use CSS positioning and a pseudo-element on one of the links to stretch its click area over the entire card, or attach a click handler in JavaScript that programmatically navigates when the card surface is clicked.
  • If a button wraps an anchor (or vice versa) solely for styling purposes, pick one element and apply the styles directly to it. A <button> can be styled to look like a link, and an <a> can be styled to look like a button.
  • If the design calls for a single interactive region that performs two distinct actions, separate them visually and semantically into two sibling controls.

Examples

Incorrect: link inside a button

The <a> nested inside the <button> produces invalid HTML. Browsers will attempt error recovery, and the link may vanish from the accessibility tree.

<button>
Save and <a href="/details">view details</a>
</button>

Incorrect: button inside a link

Same problem in reverse. The <button> is nested inside the <a>, and assistive technologies cannot reliably expose both controls.

<a href="/products/1">
<h3>Product name</h3>
<p>A short description of the product.</p>
<button>Add to cart</button>
</a>

Correct: sibling interactive elements

Separate the link and button so neither is a descendant of the other.

<div class="card">
<h3><a href="/products/1">Product name</a></h3>
<p>A short description of the product.</p>
<button>Add to cart</button>
</div>

Correct: clickable card using a pseudo-element

If the entire card should be clickable while keeping a separate button, stretch the link's hit area with CSS rather than wrapping everything in an <a>.

<div class="card">
<h3>
<a href="/products/1" class="card-link">Product name</a>
</h3>
<p>A short description of the product.</p>
<button class="add-to-cart">Add to cart</button>
</div>
.card {
position: relative;
}
.card-link::after {
content: "";
position: absolute;
inset: 0;
}
.add-to-cart {
position: relative;
z-index: 1;
}

In this pattern, the ::after pseudo-element of the link covers the entire card, making the card surface clickable. The "Add to cart" button sits above the pseudo-element thanks to z-index, so it remains independently clickable. Both controls are exposed correctly in the accessibility tree because neither is nested inside the other.

Detect accessibility issues automatically

Rocket Validator scans your site with complementary accessibility engines, helping teams find issues across every page.

Help us improve our guides

Was this guide helpful?
๐ŸŒ Trusted by teams worldwide

Validate at scale.
Ship accessible websites, faster.

Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.

Scheduled Reports
API Access
Open Source Standards
$7 / 7 days

Pro Trial

Full Pro access. Cancel anytime.

Start Pro Trial โ†’

Join teams across 40+ countries