Skip to content

bug: Accessibility issue - ion-popover has aria-hidden error when dismissed #30040

Closed

Description

@mitchfaulcon

Prerequisites

Ionic Framework Version

v8.x

Current Behavior

ion-popover element in popover has invalid aria-hidden attribute.

This issue does not happen in Firefox browsers but both Chrome & Edge show the following error in dev tools console when the popover is clicked off to close it:
Blocked aria-hidden on an element because its descendant retained focus. The focus must not be hidden from assistive technology users. Avoid using aria-hidden on a focused element or its ancestor. Consider using the inert attribute instead, which will also prevent focus. For more details, see the aria-hidden section of the WAI-ARIA specification at https://w3c.github.io/aria/#aria-hidden. Element with focus: ion-popover Ancestor with aria-hidden: <ion-popover>...</ion-popover>

We recently updated our project to ionic 8.4.0 to address the issue fixed in 29773 which did fix the aria-hidden error on the ion-backdrop element, but it's now present on the ion-popover element instead.

Expected Behavior

No aria-hidden error when closing popup

Steps to Reproduce

  1. On Chrome or Edge browser click on button to open popup
  2. Click on backdrop to dismiss popup
  3. Error thrown in dev tools console

Code Reproduction URL

https://stackblitz.com/edit/angular-t5gtgv

Ionic Info

Ionic:
Ionic CLI : 7.1.5
Ionic Framework : @ionic/angular 8.4.0
@angular-devkit/build-angular : 18.2.10
@angular-devkit/schematics : 17.3.11
@angular/cli : 18.2.6
@ionic/angular-toolkit : 11.0.1

Utility:
cordova-res : not installed globally
native-run : not installed globally

System:
NodeJS : v20.10.0
npm : 10.9.0
OS : Windows 10

Additional Information

No response

Activity

  1. pkunszt commented on Nov 27, 2024

    @pkunszt

    Actually this happens in many more elements. I am on 8.4.0, and it happens in ion-page, ion-modal as well. Obviously on a page and on a modal there are buttons and other focusable elements. Whenever the component is dismissed, the browser emits this error (blocking aria-hidden, because descendant retains focus).

  2. raykin commented on Dec 6, 2024

    @raykin

    @pkunszt do you have workaround to fix it? The error is really annoy cause it don't provide info for app development. And it seems only alert error in latest Chrome browser.

  3. WhoofWhoof commented on Dec 6, 2024

    @WhoofWhoof

    I am having the same issue on almost all modals I open

  4. marshall86 commented on Dec 9, 2024

    @marshall86

    the same issue is related to the ion-loader as well. I sorted it out adding an html attribute in the "htmlAttributes" loadingOptions of the loadingCtrl.create(loadingOptions);

  5. ingbioservicios commented on Dec 9, 2024

    @ingbioservicios

    @marshall86 Can you provide an example?

  6. marshall86 commented on Dec 10, 2024

    @marshall86

    @marshall86 Can you provide an example?

    i'm using ionic/angular 8.1.0 yet but I got that kind of warning only using ion-loading component. As a workaround, I found out that using the "htmlAttributes" inside the LoadingOptions that you pass to the loading controller create method.
    Here an example:

    const loadingOptions: LoadingOptions = { cssClass: 'custom-loader', message: 'Please wait...', htmlAttributes: [{ 'prevent-aria-hidden-error': true }], backdropDismiss: false }; const loader = await this.loadingCtrl.create(loadingOptions);

  7. pkunszt commented on Dec 11, 2024

    @pkunszt

    I can confirm that this htmlAttribute prevents the error for the loading controller, and probably also works for other controllers. However, it still persists for any page i am navigating from. And there the aria-hidden is inserted by the framework. So this is a bug that should be fixed.

  8. kristojorg commented on Jan 16, 2025

    @kristojorg

    This is also happening when navigating between pages in a stack using ionic framework with react. It happens because the link clicked to navigate retains focus until the next page is fully rendered, but ionic attempts to add aria-hidden to the prior page before that. Thus the initial page, which still has focus, is given aria-hidden, and it's blocked by the browser. This breaks accessibility, as all pages now appear "visible" at the same time, whether they are in the foreground or not. It also breaks out ability to test the app using Playwright, because Playwright (correctly) thinks all pages are visible all the time.

  9. kristojorg commented on Jan 16, 2025

    @kristojorg

    FWIW I am now manually applying aria-hidden to pages that are backgrounded using this hook:

    export function useIsHidden() {
      const location = useLocation();
    
      // Store the path that caused this page to initially mount
      const [initialPath] = useState(location.pathname);
    
      // Compare current path to initial path
      const isVisible = location.pathname === initialPath;
    
      return !isVisible;
    }
  10. rl-carno commented on Jan 17, 2025

    @rl-carno

    Also having this issue in ionic Angular V7 and V6, but only recently, its actually started occurring on older projects that have had no code changes made. It seems to be the result of recent ARIA spec updates as opposed to any new behaviour or changes in Ionic, This update means that any aria-hidden elements with descendents in focus, will have it's aria-hidden status ignored by the browser and an error log thrown.

    Ionic team need to make a change to blur focus on any element before adding aria-hidden to parent containers.

    document.activeElement .blur()

    This isn't something that developers should be expected to fix its a fundamental framework issue. This should be done before, opening a modal, changing the nav stack and any other things such as alerts, loaders, that trigger aria-hidden.

    They may want to store the previously focused element before launching a modal, so that once the modal closes ,the user returns to the focus of where they left off, or provide an interface for the developer to chose that.

  11. gasci commented on Jan 30, 2025

    @gasci

    Are there any plans to fix this problem? This is very annoying in the console.

  12. cchamorro commented on Feb 4, 2025

    @cchamorro

    Frankly, I never find satisfactory answers on Github forums. Pure waste of time.

  13. sebastien-closs commented on Feb 17, 2025

    @sebastien-closs

    and, sometimes, we don't call controller.create(), so we cannot give the options for htmlAttributes.
    Like, ion-modal used with [isOpen]

  14. csicky commented on Mar 17, 2025

    @csicky

    Here is a workaround while we wait for a fix:

    if (process.env.NODE_ENV === 'development') {
      const observer = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
          if (mutation.type === 'attributes' && mutation.attributeName === 'aria-hidden') {
            mutation.target.removeAttribute('aria-hidden')
          }
        })
        document.querySelectorAll('[aria-hidden]').forEach(el => {
          el.removeAttribute('aria-hidden')
        })
      })
    
      observer.observe(document.body, {
        attributes: true,
        childList: true,
        subtree: true,
        attributeFilter: ['aria-hidden']
      })
    }
    
  15. alex-moroz-plt commented on Mar 28, 2025

    @alex-moroz-plt

    This works for me

    export abstract class AriaFocusFixer implements ViewWillLeave {
    
      public ionViewWillLeave(): void {
        const activeElement = document.activeElement as HTMLElement;
        activeElement?.blur();
      }
    
    }
    
    export class SomePage extends AriaFocusFixer implements OnInit {
    
      constructor() {
        super();
      }
    }
    
  16. pkunszt commented on Mar 28, 2025

    @pkunszt

    @foreinger nice, but i also get the error directly on the router outlet, ie. when a modal is shown. I added this fix to the app.component as well, but it does not seem to do anything. i still get in the console the aria problem. you see below that there is a menu i am using - your fix is there, but the router is of a component i have no control over.

    Image

    And of course it still happens with all other ionic internal components like the ion-loading element that i create using the LoadingController. Also there I have no control and cannot add this fix. So this is a workaround for my own components, which is nice, but still: the ionic team really needs to follow protocol and adhere to the new aria flow for this console bug to properly go away.

  17. alex-moroz-plt commented on Apr 4, 2025

    @alex-moroz-plt

    @pkunszt
    Sorry for the late answer, indeed this fix doesn't work for overlay components like modal, popovers etc.
    But it works fine for pages involved in router navigation, just make sure that each page of your routes extends AriaFocusFixer.

    For fixing overlay components, I moved the blur call to the constructor for triggering it not after the presentation of the component but just after the class instance creation. It works for me. But still, there is a drawback that I need to extend this class for every page, modal, or popover component.

    export abstract class AriaFocusFixer {
    
      constructor() {
        AriaFocusFixer.blurActiveFocus();
      }
    
      static blurActiveFocus(): void {
        const activeElement = document.activeElement as HTMLElement;
        activeElement?.blur();
      }
    }
    
    

    For overlays without passing components inside I use blur between .create() and .present() calls.

      public async showAlert(): Promise<void> {
        const alert = await this.alertController.create();
        AriaFocusFixer.blurActiveFocus();
        await alert.present();
      }
    
  18. added
    type: buga confirmed bug report
    a11yAccessibility related issues
    and removed on May 25, 2025
  19. zura156 commented on Jul 12, 2025

    @zura156

    I am having same issue on navigation. (navCtrl.navigateRoot(url) works fine, but window view doesn't change when using navCtrl.navigateForward(url) or angular-router navigate() because of this warning).

  20. fabichacon commented on Jul 16, 2025

    @fabichacon

    Any progress here?

  21. aamsur commented on Aug 23, 2025

    @aamsur

    Here is a workaround while we wait for a fix:

    if (process.env.NODE_ENV === 'development') {
      const observer = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
          if (mutation.type === 'attributes' && mutation.attributeName === 'aria-hidden') {
            mutation.target.removeAttribute('aria-hidden')
          }
        })
        document.querySelectorAll('[aria-hidden]').forEach(el => {
          el.removeAttribute('aria-hidden')
        })
      })
    
      observer.observe(document.body, {
        attributes: true,
        childList: true,
        subtree: true,
        attributeFilter: ['aria-hidden']
      })
    }
    

    thanks bro, just linked it in main.ts for temporary fix

  22. added a commit that references this issue on Sep 3, 2025
    49f96d7
  23. ShaneK commented on Sep 17, 2025

    @ShaneK
    Member

    This fix is out now in 8.7.4 🚀

  24. ionitron-bot commented on Oct 17, 2025

    @ionitron-bot

    Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.

  25. locked and limited conversation to collaborators on Oct 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    a11yAccessibility related issuestype: buga confirmed bug report

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions