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.
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,
#rootis never markedaria-hidden, so all background app content staysreachable in the accessibility tree. This is true on BOTH sides of the migration:
#rootwitharia-modal="true"— butaria-modalalone does not prune the tree (measured: aria snapshot GREW 185→202lines with the modal open).
document.bodywith focus guards,aria-modaldropped (Radix relies on
hideOthers), buthideOthersis not marking#rootaria-hidden(measured:#rootaria-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 everybody-level sibling of the portal) carries
aria-hidden="true"/inert, verified bya Playwright assertion added to the primitive suite, not by manual inspection.
Suspects to check first: whether
react-remove-scroll/aria-hiddenpackage ispresent and why
hideOthersskips#root; whether a customDialogPortal containerbreaks Radix's sibling-hiding walk.