CivicTechTO / CivicTechTO/civictech.ca
Modal dialog has no focus trap
- Dominant language
- HTML
- Stars
- 1
- Forks
- 6
- Avg merge
- 9h 45m
- Merged PRs (30d)
- 6
Description
## Summary
The site uses a native HTML `` element with `showModal()`, which provides some built-in accessibility behaviour. However, the current implementation in `modal.js` overrides the native Escape key handler without ensuring focus is properly managed, and focus can potentially escape the modal to the background content depending on browser/AT combinations.
## Affected location
`assets/js/modal.js`
## Details
Native `` with `showModal()` does implement a focus trap in modern browsers. However:
1. The custom Escape key handler in `modal.js:60–64` calls `closeModal()` which uses a CSS animation delay of 400ms before calling `dialog.close()`. During this window, the dialog is visually closing but still open in the DOM, which can cause focus confusion.
2. Focus is not explicitly returned to the trigger element that opened the modal after it closes — it falls back to the browser default (usually ``).
## Fix
After calling `modal.close()`, return focus to the element that triggered the modal:
```js
// Track the trigger element when opening
const openModal = (modal) => {
modal._trigger = document.activeElement; // store trigger
// ... existing open logic
modal.showModal();
};
const closeModal = (modal) => {
const trigger = modal._trigger;
// ... existing close logic (with animation timeout)
setTimeout(() => {
// ... existing cleanup
modal.close();
if (trigger) trigger.focus(); // return focus to trigger
}, animationDuration);
};
```
## WCAG criterion
2.1.2 No Keyboard Trap (Level A), 2.4.3 Focus Order (Level A)
Contributor guide
Assessment
This issue has not been assessed yet.