diff --git a/index.html b/index.html
index 06cff1c..b82f418 100644
--- a/index.html
+++ b/index.html
@@ -993,6 +993,16 @@ Sovereignty and service always.`;
function trapFocusInOverlay(e) {
if (!crisisOverlay.classList.contains('active')) return;
+
+ // Escape: dismiss overlay (only when dismiss button is enabled after countdown)
+ if (e.key === 'Escape') {
+ e.preventDefault();
+ if (!overlayDismissBtn.disabled) {
+ overlayDismissBtn.click();
+ }
+ return;
+ }
+
if (e.key !== 'Tab') return;
var focusable = getOverlayFocusableElements();
@@ -1001,6 +1011,13 @@ Sovereignty and service always.`;
var first = focusable[0];
var last = focusable[focusable.length - 1];
+ // If focus escaped outside the overlay, bring it back
+ if (!crisisOverlay.contains(document.activeElement)) {
+ e.preventDefault();
+ first.focus();
+ return;
+ }
+
if (e.shiftKey) {
// Shift+Tab: if on first, wrap to last
if (document.activeElement === first) {
@@ -1050,7 +1067,11 @@ Sovereignty and service always.`;
}
}, 1000);
- overlayDismissBtn.focus();
+ // Focus the Call 988 link (always enabled) — not the disabled dismiss button
+ var callLink = crisisOverlay.querySelector('a.overlay-call');
+ if (callLink) {
+ callLink.focus();
+ }
}
// Register focus trap on document (always listening, gated by class check)
diff --git a/tests/test_crisis_overlay_focus_trap.py b/tests/test_crisis_overlay_focus_trap.py
index f657afc..5daf224 100644
--- a/tests/test_crisis_overlay_focus_trap.py
+++ b/tests/test_crisis_overlay_focus_trap.py
@@ -52,6 +52,38 @@ class TestCrisisOverlayFocusTrap(unittest.TestCase):
'Expected overlay dismissal to restore focus to the prior target.',
)
+ def test_overlay_initial_focus_targets_enabled_element(self):
+ """Issue #69: overlay must not focus the disabled dismiss button on open."""
+ # The showOverlay function should NOT call overlayDismissBtn.focus()
+ # while the button is disabled. Instead it should focus an enabled element.
+ self.assertNotRegex(
+ self.html,
+ r"overlayDismissBtn\.disabled\s*=\s*true;.*overlayDismissBtn\.focus\(\)",
+ 'showOverlay must not focus the dismiss button while it is disabled (issue #69).',
+ )
+ # Verify focus goes to the Call 988 link (always enabled)
+ self.assertIn(
+ "querySelector('a.overlay-call')",
+ self.html,
+ 'Expected showOverlay to focus the Call 988 link on open.',
+ )
+
+ def test_overlay_escape_key_dismisses(self):
+ """Issue #69/95: Escape key should dismiss the overlay when countdown completes."""
+ self.assertRegex(
+ self.html,
+ r"e\.key\s*===\s*['\"]Escape['\"]",
+ 'Expected Escape key handler in overlay focus trap.',
+ )
+
+ def test_overlay_focus_recovery_when_focus_escapes(self):
+ """Focus trap should recover focus if it escapes the overlay."""
+ self.assertRegex(
+ self.html,
+ r"crisisOverlay\.contains\(document\.activeElement\)",
+ 'Focus trap should check if focus is still within the overlay.',
+ )
+
if __name__ == '__main__':
unittest.main()