From 5b4625b41d1f56f2e63861fa25da78cb27498cfd Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Thu, 18 Jun 2026 13:56:23 -0400 Subject: [PATCH] fix(17): WR-01 exclude hidden/zero-size nodes from focus-trap boundaries --- apps/pwa/src/hooks/useFocusTrap.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/pwa/src/hooks/useFocusTrap.ts b/apps/pwa/src/hooks/useFocusTrap.ts index cbd5727..12fb791 100644 --- a/apps/pwa/src/hooks/useFocusTrap.ts +++ b/apps/pwa/src/hooks/useFocusTrap.ts @@ -26,7 +26,17 @@ export function useFocusTrap( dialogRef.current.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])', ), - ).filter((el) => !el.hasAttribute('disabled') && el.getAttribute('tabindex') !== '-1'); + ).filter((el) => { + // Exclude disabled / explicitly-untabbable nodes … + if (el.hasAttribute('disabled') || el.getAttribute('tabindex') === '-1') return false; + // … and nodes that are not actually rendered/visible (WR-01). A focusable + // inside a hidden/collapsed block would otherwise become the computed + // first/last and `last.focus()` would no-op, leaking Tab to background + // content that `aria-modal="true"` promises is unreachable. + if (el.hasAttribute('hidden') || el.offsetParent === null) return false; + const r = el.getBoundingClientRect(); + return r.width > 0 && r.height > 0; + }); if (focusable.length === 0) return;