-
Notifications
You must be signed in to change notification settings - Fork 13.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(overlay): hide from screen readers while animating #29951
Changes from all commits
68f11a2
be4a504
a42ad86
6ced157
3240e88
1415d7b
d05ced8
94d2c73
f2984ab
37ea30f
a03b562
9c80de6
993dcca
69c59d0
b16c045
f2a43f8
bd5dc0b
0b8ac51
b70f53f
c3d74ad
f5e7494
60820bb
294f665
36fe7f1
491efca
94c165c
bc2c086
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -514,7 +514,8 @@ export const present = async <OverlayPresentOptions>( | |
|
||
document.body.classList.add(BACKDROP_NO_SCROLL); | ||
|
||
hideOverlaysFromScreenReaders(overlay.el); | ||
hideUnderlyingOverlaysFromScreenReaders(overlay.el); | ||
hideAnimatingOverlayFromScreenReaders(overlay.el); | ||
|
||
overlay.presented = true; | ||
overlay.willPresent.emit(); | ||
|
@@ -560,6 +561,11 @@ export const present = async <OverlayPresentOptions>( | |
* it would still have aria-hidden on being presented again. | ||
* Removing it here ensures the overlay is visible to screen | ||
* readers. | ||
* | ||
* If this overlay was being presented, then it was hidden | ||
* from screen readers during the animation. Now that the | ||
* animation is complete, we can reveal the overlay to | ||
* screen readers. | ||
*/ | ||
overlay.el.removeAttribute('aria-hidden'); | ||
}; | ||
|
@@ -644,6 +650,13 @@ export const dismiss = async <OverlayDismissOptions>( | |
overlay.presented = false; | ||
|
||
try { | ||
/** | ||
* There is no need to show the overlay to screen readers during | ||
* the dismiss animation. This is because the overlay will be removed | ||
* from the DOM after the animation is complete. | ||
*/ | ||
hideAnimatingOverlayFromScreenReaders(overlay.el); | ||
|
||
// Overlay contents should not be clickable during dismiss | ||
overlay.el.style.setProperty('pointer-events', 'none'); | ||
overlay.willDismiss.emit({ data, role }); | ||
|
@@ -929,6 +942,29 @@ export const createTriggerController = () => { | |
}; | ||
}; | ||
|
||
/** | ||
* The overlay that is being animated also needs to hide from screen | ||
* readers during its animation. This ensures that assistive technologies | ||
* like TalkBack do not announce or interact with the content until the | ||
* animation is complete, avoiding confusion for users. | ||
* | ||
* If the overlay is being presented, it prevents focus rings from appearing | ||
* in incorrect positions due to the transition (specifically `transform` | ||
* styles), ensuring that when aria-hidden is removed, the focus rings are | ||
* correctly displayed in the final location of the elements. | ||
* | ||
* @param overlay - The overlay that is being animated. | ||
*/ | ||
const hideAnimatingOverlayFromScreenReaders = (overlay: HTMLIonOverlayElement) => { | ||
if (doc === undefined) return; | ||
|
||
/** | ||
* Once the animation is complete, this attribute will be removed. | ||
* This is done at the end of the `present` method. | ||
*/ | ||
overlay.setAttribute('aria-hidden', 'true'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional: maybe add a comment referencing where the attribute is being added. My initial thought was: when we set it here, will it ever be removed somewhere else? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
}; | ||
|
||
/** | ||
* Ensure that underlying overlays have aria-hidden if necessary so that screen readers | ||
* cannot move focus to these elements. Note that we cannot rely on focus/focusin/focusout | ||
|
@@ -939,7 +975,7 @@ export const createTriggerController = () => { | |
* @param newTopMostOverlay - The overlay that is being presented. Since the overlay has not been | ||
* fully presented yet at the time this function is called it will not be included in the getPresentedOverlays result. | ||
*/ | ||
const hideOverlaysFromScreenReaders = (newTopMostOverlay: HTMLIonOverlayElement) => { | ||
const hideUnderlyingOverlaysFromScreenReaders = (newTopMostOverlay: HTMLIonOverlayElement) => { | ||
if (doc === undefined) return; | ||
|
||
const overlays = getPresentedOverlays(doc); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just changed the name to distinguish between this function and the next.