Skip to content

Modal surfaces never mark #root aria-hidden — background stays in the a11y tree #211

Description

@HomenShum

Found during the Radix primitive migration before/after probe (PR #209,
evidence in docs/design/ui-contract/20260717-radix-primitives/README.md).

Problem: while the command palette (and by extension other modal dialogs) is
open, #root is never marked aria-hidden, so all background app content stays
reachable in the accessibility tree. This is true on BOTH sides of the migration:

  • pre-Radix: dialog rendered inline inside #root with aria-modal="true" — but
    aria-modal alone does not prune the tree (measured: aria snapshot GREW 185→202
    lines with the modal open).
  • post-Radix: dialog is portaled to document.body with focus guards, aria-modal
    dropped (Radix relies on hideOthers), but hideOthers is not marking #root
    aria-hidden (measured: #root aria-hidden stays null while open).

The observed 185→49 aria-snapshot shrink post-Radix is most plausibly scroll-lock
unmounting virtualized binder rows, NOT aria-hiding — do not count it as the fix.

Definition of done: with any shared-primitive modal open, #root (or every
body-level sibling of the portal) carries aria-hidden="true"/inert, verified by
a Playwright assertion added to the primitive suite, not by manual inspection.

Suspects to check first: whether react-remove-scroll/aria-hidden package is
present and why hideOthers skips #root; whether a custom DialogPortal container
breaks Radix's sibling-hiding walk.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions