skip to content

In HTML, what does the inert attribute do to the subtree it is applied to, and how does that differ from aria-hidden="true"?

level: middleimportance: should knowfreq 38%

answer

  1. one edits the a11y tree only
  2. the other blocks focus and clicks too
  3. focusable but unannounced is the bug
  4. boolean attribute, inherited by descendants
  5. not the same as disabled

basics

~10 s

inert removes a whole subtree from interaction: nothing inside can be focused or clicked, and it disappears from the accessibility tree. aria-hidden="true" only hides content from assistive technology, leaving it focusable and clickable.

solid answer

~40 s

`inert` is a boolean global attribute. Applied to an element, it and its entire subtree become non-focusable — Tab skips them and `focus()` on them does nothing — click activation is suppressed, the content is removed from the accessibility tree, and it is skipped by browser find-in-page and text selection. `aria-hidden="true"` does exactly one of those things: it removes the subtree from the accessibility tree. Focus and clicks are untouched. That is why a focusable control inside an `aria-hidden` container is a classic bug and a standard automated-checker failure: a keyboard user tabs onto a control that announces nothing at all. When you need background content behind an overlay to be genuinely unreachable, `inert` is the attribute; `aria-hidden` is for decorative or duplicated content that stays visually present but should not be announced.

code

html · 7 lines
html
<main inert>
  <button>Skipped by Tab, ignores clicks, absent from the a11y tree</button>
</main>

<aside aria-hidden="true">
  <button>Still focusable and clickable, but announces nothing</button>
</aside>

go deeper

for a junior

Know that inert makes a whole section non-interactive while aria-hidden only hides it from screen readers, and that the two are not substitutes for each other.

for a middle

List what inert actually blocks — focus, clicks, accessibility exposure, find-in-page — and explain why a focusable element inside aria-hidden is a defect rather than a style choice.

for a senior

Show the judgment call: which of inert, aria-hidden, disabled, hidden, or DOM removal fits a given state, and how you would migrate a legacy tabindex-saving focus trap onto inert without regressions.

for a principal

Own the convention across a codebase: where background neutralisation lives in the overlay primitive, how you keep aria-hidden from being used as an interaction blocker in feature code, and what your automated checks enforce.

## The problem inert solves Some content is on the page but must not be part of the current interaction: the page behind a modal, the body behind an open off-canvas navigation, a panel that is animating out. Before `inert` existed, teams simulated it by walking the subtree, saving each focusable element's `tabindex`, setting it to `-1`, adding `aria-hidden`, then restoring everything on close. It was fragile precisely because it had to remember prior state, and it broke whenever content changed while the overlay was open. ## What inert does `inert` is a boolean attribute — its presence is what counts, `inert="false"` still makes the element inert. On the element and every descendant: - nothing is focusable: Tab skips the subtree, and a programmatic `focus()` call on a node inside it has no effect; - click activation is suppressed, so pointer events do not fire activation behaviour on controls inside; - the content is removed from the accessibility tree, so screen readers do not reach it; - browser find-in-page skips it, and the text is not selectable. Crucially it is inherited down the subtree and a descendant cannot opt back in — there is no way to un-inert a child of an inert ancestor. ```html <main inert> <button>Cannot be focused, cannot be clicked</button> </main> <div role="dialog" aria-modal="true" aria-labelledby="title"> <h2 id="title">Settings</h2> <button>Close</button> </div> ``` ## What aria-hidden does, and does not `aria-hidden="true"` edits one thing: the accessibility tree. The subtree stops being exposed to assistive technology. It stays visible, stays clickable, and stays in the tab order. That single-purpose behaviour is useful — a decorative icon next to a text label, or a visual duplicate of text already announced elsewhere. It is the wrong tool for blocking interaction, and the mismatch produces the best-known failure in this area: a focusable element inside an `aria-hidden` container. The keyboard user tabs to it, the screen reader has nothing to announce because the node is not in the tree, and the user is stranded on a control that, as far as their software is concerned, does not exist. Automated accessibility checkers ship a dedicated rule for it. ## What neither of them is `inert` is not `disabled`. A form control inside an inert subtree is still a successful control: its value is still submitted with the form. `inert` governs focus, hit testing and accessibility exposure, not form participation. If you want the value excluded, `disabled` is the attribute. `inert` is also not a visual state. The subtree renders exactly as before — same size, same colours, same position in layout. Any dimming is a styling decision made separately. And `inert` is not `hidden`. Hidden content is not rendered at all; inert content is rendered and visible but inactive. ## inert and native dialogs A `<dialog>` opened with `showModal()` makes the rest of the document inert automatically, which is why the native modal needs no focus trap. If you are using `showModal()`, you do not add `inert` yourself. If you are hand-rolling an overlay, `inert` on the background is the piece that replaces a hand-written trap for background escapes. `inert` is supported across current major browsers, which is why the manual tabindex-walking workaround is now legacy. ## Choosing between them Ask what the content should be. If it is present but not part of the current interaction for anyone, use `inert`. If it is present and interactive but redundant to announce, use `aria-hidden`. If it should not exist right now at all, remove it from the DOM or use `hidden`. Reaching for `aria-hidden` when you meant `inert` is the mistake to name in an interview, because it produces content that is reachable but silent — worse than either intent.

  • Does a form control inside an inert subtree still submit its value?
    Yes. `inert` affects focus, hit testing and accessibility exposure, not form participation, so the control is still successful and its value is still submitted. If you want the value left out of the submission, use `disabled`, which excludes it and also makes the control unfocusable.
  • When you open a <dialog> with showModal(), do you still need to set inert on the rest of the page?
    No. A modal dialog makes everything outside it inert automatically, which is what confines Tab and hides the background from assistive technology. Adding your own `inert` is redundant, and setting it on an ancestor of the dialog would be actively wrong.
  • Why do accessibility checkers report a focusable element inside aria-hidden="true" as an error?
    Because the two states contradict each other. The element can still be tabbed to, but it has been removed from the accessibility tree, so a screen reader announces nothing when focus lands there. The user is on a control their software cannot describe.

saying these in an interview costs you the question

  • Says inert and aria-hidden are interchangeable
  • Claims aria-hidden also removes an element from the tab order
  • Thinks inert hides the subtree visually
  • Uses inert expecting form values to stop submitting
  • Adds inert to the background of a dialog opened with showModal()

context