Video examples
iOS Voiceover
Android Talkback
Windows Jaws Chrome
MacOS Voiceover Safari
Variations
(Spoiler alert: These all have the same rules.)
Required attributes
Do not use the Popover API
The Popover API cannot be used to launch a dialog modal. The Popover element will not have the same semantics or built in features of a JS launched using modal.showModal();
Launch button
- Should be a button, not a link
- Upon closing, focus must return to the button that launched the dialog
- Do not use
aria-haspopup
. This attribute has very low and support and unpredictable output across screen readers.
Name
- The modal window has a logical descriptive name from either:
aria-label="Modal title"
oraria-labelledby="heading-id"
pointing to an<h2>
as a title
Role
- Use
role="dialog"
so the screen reader can identify this as a dialog or modal
Group
- Upon closing, focus must return to the button that launched the dialog
State
- Use
aria-modal="true"
to indicate content beneath the modal is inert and that the screen reader must not browse outside the dialog.
Focus
- Use
tabindex="-1"
to make the modal itself targetable for focus- This is a helpful pattern because not every modal has a dismiss button (ex: Alert options for being signed out if user is inactive)
- Upon closing, focus must return to the button that launched the dialog
Screenreader differences
- NVDA
- By default, NVDA may read the entire modal upon launch. This is expected behavior.
- iOS Voiceover
- Voiceover will place focus on the first focusable control, no matter what JS targets for focus