fix(ui5-popup): announce invisible messages inside open popups - #13889
fix(ui5-popup): announce invisible messages inside open popups#13889ivoplashkov wants to merge 8 commits into
Conversation
Screen readers scope their accessibility tree to a modal popup's subtree while it is open, so announcements written to the default body-level aria-live region are not read out. InvisibleMessage now supports registering per-container aria-live regions. Popup registers its root as a region on open and deregisters it on close, so announce() routes messages into the open popup and they are heard. Fixes #13613
The test page used absolute cross-package src paths (/packages/base/src/...ts) which Rollup could not resolve during the production/test bundle, breaking the build. Use the window["sap-ui-webcomponents-bundle"] global (as other test pages do) to access announce(), passing the mode as a plain string.
|
🚀 Deployed on https://pr-13889--ui5-webcomponents-preview.netlify.app |
Registering a per-popup aria-live region for every popup rerouted announcements away from the default body-level region even for non-modal popovers (e.g. ComboBox suggestion lists), breaking their existing announcements. Only modal popups need their own region, since a screen reader scopes its accessibility tree to a modal popup's subtree. Gate registration on isModal so non-modal popovers keep using the default region.
|
Please check how the aria-notify polyfil is implemented. Seems like exactly the things we need: https://github.com/github/arianotify-polyfill/blob/main/arianotify-polyfill.js |
Already did, however this is not yet fully available on all browsers and the support is limited. I have also discussed this with the central accessibility team and the overall assumption is that it's still early to adopt. Other than that, this is definitelly the thing we need to solve this case and we should adopt it in future. |
The native <dialog> example called announce() without registering the dialog as an aria-live region, so announcements were silenced under the modal a11y scoping. Expose registerInvisibleMessageRegion and deregisterInvisibleMessageRegion on the test bundle and call them on the native dialog's open/close so announce() routes into the dialog subtree.
Register the in-popup aria-live region only for modal popups. A screen reader scopes its accessibility tree to a genuinely modal popup (focus trap / backdrop), which silences the body-level region; non-modal popups such as the ComboBox dropdown do not cause this scoping, so their announcements are still heard from the default body-level region and must not be routed into the popup subtree. Add native <dialog>, native popover, and native popover + aria-modal comparison cases to the InvisibleMessageInDialog test page to demonstrate that modal scoping (not the bare aria-modal attribute) is what silences the body-level region.
|
|
||
| this._deregisterResizeHandler(); | ||
| this._detachBrowserEvents(); | ||
| this._deregisterInvisibleMessageRegion(); |
There was a problem hiding this comment.
You register it on open and deregister on close, why is this not in the if statement above?
| </div> | ||
|
|
||
| <h1 style="margin-top: 2rem;">Comparison: native popover (top layer) WITH aria-modal="true"</h1> | ||
| <p class="note"> |
There was a problem hiding this comment.
Just a note - JAWS announces the body spans of all popover examples, the only issue is with the native dialog's showModal() behavior
Screen readers scope their accessibility tree to a modal popup's subtree while it is open, so announcements written to the default body-level aria-live region are not read out.
InvisibleMessage now supports registering per-container aria-live regions. Popup registers its root as a region on open and deregisters it on close, so announce() routes messages into the open popup and they are heard.
Fixes #13613