From beaaad49825183a49fcecafce94861577cfd9b9a Mon Sep 17 00:00:00 2001 From: cliffhall Date: Sun, 23 Aug 2026 00:02:37 -0400 Subject: [PATCH] fix(apps): make a covered elicitation dialog inert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only the top modal owned the focus trap, but every lower `Modal.Content` remained an exposed `aria-modal` dialog with reachable controls and a live iframe — so browse navigation could land in a dialog that is visually covered and that itself claims the rest of the page is inert. Non-top contents are now `inert`: out of the accessibility tree and out of focus order, without unmounting, so each app keeps its bridge and its handshake and is live the moment it becomes top. Signed-off-by: cliffhall --- .../elements/AppElicitation/AppElicitationHost.test.tsx | 6 ++++++ .../elements/AppElicitation/AppElicitationHost.tsx | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/clients/web/src/components/elements/AppElicitation/AppElicitationHost.test.tsx b/clients/web/src/components/elements/AppElicitation/AppElicitationHost.test.tsx index 420ecf08..4baeae74 100644 --- a/clients/web/src/components/elements/AppElicitation/AppElicitationHost.test.tsx +++ b/clients/web/src/components/elements/AppElicitation/AppElicitationHost.test.tsx @@ -216,6 +216,12 @@ describe("AppElicitationHost (#1854)", () => { await flushAsync(); expect(screen.getAllByTestId("app-elicitation")).toHaveLength(2); + // The covered dialog is inert: out of the a11y tree and out of focus + // order, but still mounted — its app keeps its bridge and handshake. + const [lower, top] = screen.getAllByTestId("app-elicitation"); + expect(lower.hasAttribute("inert")).toBe(true); + expect(top.hasAttribute("inert")).toBe(false); + await user.keyboard("{Escape}"); // Exactly one request is dismissed — the topmost, which is the last one. expect(onFail).toHaveBeenCalledTimes(1); diff --git a/clients/web/src/components/elements/AppElicitation/AppElicitationHost.tsx b/clients/web/src/components/elements/AppElicitation/AppElicitationHost.tsx index 5b2ea014..21677bf5 100644 --- a/clients/web/src/components/elements/AppElicitation/AppElicitationHost.tsx +++ b/clients/web/src/components/elements/AppElicitation/AppElicitationHost.tsx @@ -253,6 +253,13 @@ function AppElicitationFrame({ only the request id tells those two dialogs apart — for `landmark-unique` and for anyone navigating by screen reader. */}