diff --git a/core/src/components/menu/menu.tsx b/core/src/components/menu/menu.tsx index d83e53abb39..8a70dd8fb1d 100644 --- a/core/src/components/menu/menu.tsx +++ b/core/src/components/menu/menu.tsx @@ -8,6 +8,7 @@ import type { Attributes } from '@utils/helpers'; import { inheritAriaAttributes, assert, clamp, isEndSide as isEnd } from '@utils/helpers'; import { menuController } from '@utils/menu-controller'; import { BACKDROP, GESTURE, getPresentedOverlay } from '@utils/overlays'; +import { isPlatform } from '@utils/platform'; import { hostContext } from '@utils/theme'; import { config } from '../../global/config'; @@ -631,6 +632,23 @@ export class Menu implements ComponentInterface, MenuI { private beforeAnimation(shouldOpen: boolean, role?: string) { assert(!this.isAnimating, '_before() should not be called while animating'); + /** + * When the menu is presented on an Android device, TalkBack's focus rings + * may appear in the wrong position due to the transition (specifically + * `transform` styles). This occurs because the focus rings are initially + * displayed at the starting position of the elements before the transition + * begins. This workaround ensures the focus rings do not appear in the + * incorrect location. + * + * If this solution is applied to iOS devices, then it leads to a bug where + * the overlays cannot be accessed by screen readers. This is due to + * VoiceOver not being able to update the accessibility tree when the + * `aria-hidden` is removed. + */ + if (isPlatform('android')) { + this.el.setAttribute('aria-hidden', 'true'); + } + // this places the menu into the correct location before it animates in // this css class doesn't actually kick off any animations this.el.classList.add(SHOW_MENU); @@ -687,6 +705,17 @@ export class Menu implements ComponentInterface, MenuI { } if (isOpen) { + /** + * When the menu is presented on an Android device, TalkBack's focus rings + * may appear in the wrong position due to the transition (specifically + * `transform` styles). The menu is hidden from screen readers during the + * transition to prevent this. Once the transition is complete, the menu + * is shown again. + */ + if (isPlatform('android')) { + this.el.removeAttribute('aria-hidden'); + } + // emit open event this.ionDidOpen.emit(); @@ -703,6 +732,8 @@ export class Menu implements ComponentInterface, MenuI { // start focus trapping document.addEventListener('focus', this.handleFocus, true); } else { + this.el.removeAttribute('aria-hidden'); + // remove css classes and unhide content from screen readers this.el.classList.remove(SHOW_MENU); diff --git a/core/src/utils/overlays.ts b/core/src/utils/overlays.ts index 25a6597d1ad..90a4062840e 100644 --- a/core/src/utils/overlays.ts +++ b/core/src/utils/overlays.ts @@ -971,11 +971,12 @@ export const createTriggerController = () => { * 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. This only - * applies to Android devices. + * When the overlay is presented on an Android device, TalkBack's focus rings + * may appear in the wrong position due to the transition (specifically + * `transform` styles). This occurs because the focus rings are initially + * displayed at the starting position of the elements before the transition + * begins. This workaround ensures the focus rings do not appear in the + * incorrect location. * * If this solution is applied to iOS devices, then it leads to a bug where * the overlays cannot be accessed by screen readers. This is due to