>
Software

The aria-hidden warning is the browser overwriting your code

The Chrome accessibility tree is its own document, and that is the source of the warning you keep seeing in the console. The aria-hidden attribute (an HTML attribute that tells assistive technology to ignore the marked subtree) does not behave the way the name suggests. It hides content from the screen reader API and from keyboard focus order at the same time, but only when those two systems happen to agree. The instant focus lands inside a region you have just marked hidden, the two paths diverge, and the browser quietly rewrites the accessibility tree you wrote. The console entry is the browser telling you that rewrite happened, not a hint that something might be slightly off.

If you arrived here from a stack trace, the rest of this article is for you. The fix is not a one-liner, and the one-liners shipping in production code right now are the reason the warning exists.

What the warning is actually saying

The two lines that surface in DevTools differ in their trigger but describe the same underlying state:

  • Blocked aria-hidden on an element because a descendant retains focus. This fires when you hide a region that already contains a focused node. The browser refuses to actually hide the subtree from the accessibility tree because removing the focused node would leave the screen reader with no place to point.
  • Blocked aria-hidden on the body because a descendant element retains focus. The same condition, one level up. The <body> is marked hidden or inert, and a control inside it still has focus.

Both lines describe the same architectural problem: focus is sitting inside a region that has just been removed from the accessibility tree. The browser’s response is to refuse the removal, not to refuse the focus. The screen reader receives the subtree, and the keyboard focus order still includes the focusable nodes inside it. The end result is a person using a screen reader tabbing through controls that no longer have names, because the tree that supplies those names has been pruned around them.

That outcome has a name in this corner of the field. It is the silent-tab problem. The user presses Tab, the focused element changes, and the assistive technology announces nothing.

Why the order of operations is the entire bug

Modal code is the most common site because opening and closing a dialog is one of the few flows that moves focus on purpose. The state the browser is warning you about is a momentary one, the time between the line that hides the background and the line that moves focus back to the trigger button. In a synchronous JavaScript task, those two lines run back to back, but the browser’s accessibility tree is rebuilt at the end of the task, not after each statement. The moment the task ends, the tree is in a state that matches the second line, not the first. The warning fires because the first line was already true when the second line ran.

The ordering that produces the warning looks like this:

// Order that produces the warning
function closeModal() {
  overlay.setAttribute('aria-hidden', 'true');   // hide first
  overlay.classList.add('fade-out');              // then fade
  // ... later, after the transition
  triggerButton.focus();                          // focus restore
}

The version that does not produce the warning uses the inert attribute (a related but distinct attribute that removes a region from both keyboard focus and the accessibility tree) on the closing overlay, not aria-hidden. It also moves focus before the hide, so the moment the background becomes inert, no focused node is inside it:

// Order that does not produce the warning
function closeModal() {
  triggerButton.focus();                          // focus leaves first
  overlay.setAttribute('inert', '');              // then hide from focus + a11y
  overlay.style.pointerEvents = 'none';
  overlay.classList.add('fade-out');
}

The inert attribute is the path the browser is steering you toward. aria-hidden is for content that should be invisible to assistive technology but still present in the keyboard focus order, which is not the situation a closing modal is in.

The shortcuts that look right and ship anyway

The top results for this warning all belong to the same family. They each find a way to make the console message disappear without addressing the underlying ordering. Common offenders:

  • Calling document.activeElement.blur() inside the close handler. This drops focus, which satisfies the warning. It also leaves the user with no focus at all, which screen readers handle by re-reading the page title or, on some assistive tech, by saying nothing.
  • Wrapping the close in a setTimeout long enough for the focus restore to happen first. The trick works, but the timing is a heuristic. CSS transition durations change, and the moment the heuristic is wrong, the warning is back.
  • Removing the aria-hidden attribute once the close completes. The hide worked, the show undid it, and the warning did not fire. The accessibility tree is back to the state you wanted, but the fade-out animation is over a region that is technically visible to assistive tech for the entire duration of the transition.
  • The modal={false} flag on Radix, shadcn, and MUI portals. Setting the flag suppresses the warning at the React layer. The component still produces the same DOM state, and the silence is a UI-source-of-truth problem, not a fix.

The pattern in every case is the same: the developer was told the warning was advisory, and the shortcut reduced the noise without disturbing the user-facing behavior they were tracking. The user-facing behavior worth tracking is what the screen reader announces, which is not what your console says.

What to do in a code base you cannot rewrite this quarter

Component libraries and design systems put the close handler in a place you cannot edit. The realistic options in that situation:

  • Find the close event and run focus restore in the before-close handler, not the after-close. Most libraries expose a beforeClose or onCloseStart hook. Calling triggerButton.focus() there puts the focus move before the hide, regardless of what the library does in the next tick.
  • Replace aria-hidden with inert on the closing overlay. If the library lets you customize the close DOM, the inert attribute on the wrapping element side-steps the warning entirely. The accessibility tree is pruned by the engine from both directions, so the ordering no longer matters.
  • Migrate to the native <dialog> element with showModal() if you can. The native element takes over the focus management for the duration of the modal: focus moves into the dialog on open, focus returns to the trigger on close, and the engine handles the inert state. The warning class disappears.

The inert path is the one that scales to a working code base. Before-close focus restoration is the patch; the native dialog is the rewrite. The fixes in the top of the search results are the band-aid.

Trade-offs

Native <dialog> is the right answer for new code, and it is not always the right answer for a code base that already has a modal abstraction. The amount of behavior your existing fix has accumulated is the cost. <dialog> handles the focus dance, but it also handles the Escape key, the backdrop click, the scroll lock, the focus trap, and the initial focus query. Replacing your fix with <dialog> is a project, not a recipe.

The inert attribute is well-supported in modern browsers but is not a complete replacement for aria-hidden everywhere. Content that should be visible to a screen reader but not focusable still needs aria-hidden. The two attributes are for different jobs, and the warning is the symptom of using the wrong one for this job.

The beforeClose focus patch is the lowest-risk option. It does not require a library upgrade, an architecture change, or a release of new behavior. The downside is that the patch is per-library, and it needs to be re-applied when the component library version changes.

Bottom line

The warning is a fork in the road. Going down the shortcut path produces a quiet console and a broken screen reader experience. Going down the ordering path produces a loud console during development and a working screen reader experience in production. The shortcuts work in the only way that matters to the metrics you are watching, which is exactly why the warning exists.

Leave a comment