Skip to content

Hidden content needs to stay out of the focus order

  • Rule: axe/aria-hidden-focus
  • Checks: ARIA hidden element must not be focusable or contain focusable elements
  • WCAG: 4.1.2
  • Fix class: mechanical: A fix needs no authored text or visual decision.
  • People who use a screen reader or a braille display, most of them blind.
  • People who use a keyboard or a switch device instead of a mouse.

Content behind an open modal dialog has aria-hidden="true"

Section titled “Content behind an open modal dialog has aria-hidden="true"”

Make that content inert instead, which hides it from assistive technology and takes it out of the focus order, and remove inert when the dialog closes. A <dialog> opened with showModal() does this for you.

<div class="page" inert>
<a href="/account">Account</a>
</div>
<div role="dialog" aria-modal="true" aria-labelledby="confirm-title">
<h2 id="confirm-title">Delete this order?</h2>
<button type="button">Delete</button>
<button type="button">Cancel</button>
</div>
Section titled “A closed menu, collapsed panel or off-screen slide has aria-hidden="true" but its links can still take focus”

Hide it from everyone while it is closed, with the hidden attribute or display: none, or make it inert. Remove that when it opens.

<ul id="account-menu" hidden>
<li><a href="/orders">Orders</a></li>
</ul>
Section titled “A duplicate link is hidden from screen readers, such as a card’s image link beside its text link”

Combine the image and the text into one link. Where they must stay separate, also give the hidden link tabindex="-1" so keyboard users skip it.

<a href="/products/kettle">
<img src="kettle.jpg" alt="">
Copper kettle
</a>

Adding aria-hidden="false" to the focusable children

Section titled “Adding aria-hidden="false" to the focusable children”

It does not undo aria-hidden="true" on an ancestor, so the children stay hidden from assistive technology while keyboard focus still reaches them.

Removing aria-hidden from content that should stay hidden

Section titled “Removing aria-hidden from content that should stay hidden”

The rule passes, but screen readers then read a closed menu or the page behind a dialog. Hide it from everyone or make it inert instead.

The part of the fix that differs in React, in its syntax. The rest of the guidance holds.

A modal component sets aria-hidden on the app root while it is open

Section titled “A modal component sets aria-hidden on the app root while it is open”

Render a <dialog> and open it with showModal() through a ref in an effect, which makes the rest of the page inert for you. For a custom overlay rendered with createPortal, set the boolean inert prop on the app content from the same state instead.

function ConfirmDialog({ open, onClose, onConfirm }) {
const ref = useRef(null);
const titleId = useId();
useEffect(() => {
const dialog = ref.current;
if (open && !dialog.open) dialog.showModal();
if (!open && dialog.open) dialog.close();
}, [open]);
return (
<dialog ref={ref} aria-labelledby={titleId} onClose={onClose}>
<h2 id={titleId}>Delete this order?</h2>
<button type="button" onClick={onConfirm}>
Delete
</button>
<button type="button" onClick={onClose}>
Cancel
</button>
</dialog>
);
}

A closed menu or inactive slide stays mounted, hidden only with aria-hidden

Section titled “A closed menu or inactive slide stays mounted, hidden only with aria-hidden”

Unmount it while it is closed, or set hidden or inert from state, such as inert={!active}. React 19 renders inert as a boolean attribute.

function Slide({ active, children }) {
return (
<div className="slide" inert={!active}>
{children}
</div>
);
}

React 19 treats inert as a boolean: an empty string renders no attribute, and the string "false" is truthy, so it makes the content inert. Pass true or false.

The open attribute shows the dialog without making it modal, so the page behind stays reachable by keyboard and screen readers. Call showModal() instead.

axe-core’s documentation of the rule

CORA’s fix guidance adapts content from accessibility-agents, under the MIT License: see its notice.