Avoid aria-hidden on the document body element entirely
Last updated:
Related Guides
aria-hidden="true" must not be set on the document body
Do not set aria-hidden="true" on the body element.
This hides every part of the page from assistive technology while sighted users still see it.
Screen reader users lose access to all content, navigation, and forms.
Why It Matters
When aria-hidden="true" is placed on body, the entire page is removed from the accessibility tree. Screen readers and other assistive tools cannot perceive or interact with anything inside it.
This creates a hard barrier for blind and low-vision users who rely on screen readers, as well as some speech input and switch users who depend on the accessibility tree for navigation.
Even short-lived states (like during a modal or page load) can strand users, stop announcements, and cause confusion.
Common Causes
- Modal or overlay scripts that toggle
aria-hiddenon html or body to “disable” the background - Page loaders that hide content from AT until scripts finish initializing
- Copy-pasted patterns that attempt to stop screen readers from reading while UI updates
- Misunderstanding that
aria-hiddenaffects only visuals (it does not; it hides from AT) - Using
aria-hiddenas a substitute for CSS visibility/hidden or display/none
How to Fix
- Remove
aria-hiddenfrom the body element in all templates and runtime code. - If you need to hide specific content from everyone, use semantic mechanisms: hidden attribute or CSS display:none on that element (not on body).
- For modals/dialogs:
- Do not put
aria-hiddenon html or body. - Use a proper dialog (native
<dialog>or a container withrole="dialog"andaria-modal="true"). - Make the rest of the page inert (recommended) or apply
aria-hidden="true"only to non-dialog sibling containers while the dialog is open. - Restore the page state when the dialog closes (remove inert/aria-hidden and return focus).
- For collapsible or dynamic sections, toggle visibility on the specific container:
- When hidden: use hidden or display:none (optionally pair with
aria-hidden="true"). - When shown: remove both hidden/display:none and
aria-hidden. - Avoid relying on
aria-hidden="false"to override CSS or hidden; if CSS still hides it, assistive tech may not expose it. Keep visual and accessibility states aligned. - After changes, test with multiple screen readers and browsers.
How to Test
- Code/DOM check:
- Inspect the page template and runtime DOM. Confirm body does not have
aria-hidden. - Screen reader check (NVDA/JAWS/VoiceOver):
- On page load, the screen reader should announce the page title and allow navigation to headings, links, and form fields.
- Open a modal: only the modal content should be navigable; background should be skipped.
- Close the modal: underlying content should be available again.
- Keyboard check:
- Ensure focusable elements are reachable in order.
- While a modal is open, focus is trapped in the modal; after closing, focus returns to the trigger.
- Mobile/touch check:
- With VoiceOver (iOS) or TalkBack (Android), swipe through the page. Content should be announced normally; during a modal, only the modal should be reachable.
Good Example
!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Dialog example</title>
<style>
[hidden] { display: none; }
.backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.5); }
.dialog { background: #fff; margin: 10vh auto; max-width: 28rem; padding: 1rem; }
</style>
</head>
<body>
<header><h1>Site</h1></header>
<main id="main">
<p>Welcome to the site.</p>
<button id="open">Open dialog</button>
</main>
<div id="modal" class="backdrop" hidden>
<section class="dialog" role="dialog" aria-modal="true" aria-labelledby="d-title">
<h2 id="d-title">Confirm action</h2>
<p>Are you sure?</p>
<button id="close">Close</button>
</section>
</div>
<script>
const openBtn = document.getElementById('open');
const closeBtn = document.getElementById('close');
const modal = document.getElementById('modal');
const main = document.getElementById('main');
function openModal() {
modal.hidden = false;
main.setAttribute('inert', '');
modal.querySelector('[role="dialog"]').focus();
}
function closeModal() {
modal.hidden = true;
main.removeAttribute('inert');
openBtn.focus();
}
openBtn.addEventListener('click', openModal);
closeBtn.addEventListener('click', closeModal);
</script>
</body>
</html>Bad Example
!doctype html>
<html lang="en">
<body aria-hidden="true">
<main id="content" aria-hidden="false">
<h1>Visible to sighted users, silent to screen readers</h1>
<p>All content appears on screen, but SRs cannot access it because body is hidden.</p>
</main>
</body>
</html>Quick Checklist
- body element never has
aria-hidden - Hidden UI uses hidden or display:none on the specific element, not on body
- Modals use
role="dialog"(or<dialog>) witharia-modal="true"and focus management - Non-dialog content becomes inert (or temporarily
aria-hidden) while the modal is open - Visual and accessibility states change together (no
aria-hidden="false"fighting CSS) - Screen reader can read page content on load and after interactions
- Background content is unreachable while a modal is open, then restored on close