ADA Compliance Professionals

    Avoid aria-hidden on the document body element entirely

    Last updated:

    Who it helps:
    Blind
    Standard:
    WCAG 2.2 Level A

    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-hidden on 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-hidden affects only visuals (it does not; it hides from AT)
    • Using aria-hidden as a substitute for CSS visibility/hidden or display/none

    How to Fix

    • Remove aria-hidden from 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-hidden on html or body.
    • Use a proper dialog (native <dialog> or a container with role="dialog" and aria-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

    HTML
    !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

    HTML
    !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>) with aria-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