Skip to content

Modal: accessibility warning, aria-hidden #476

@pmlk

Description

@pmlk

The <Modal> component produces the following warning in Chrome when I open it:

Chrome console warning: '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: <dialog.modal>
Ancestor with aria-hidden: <dialog.modal>'

aria-hidden is still true even though the modal is in focus and visible to seeing people, so it should be accessible to people relying on assistive technologies which it wouldn't be if it wasn't blocked by the browser. aria-hidden should be false when the modal is open and / or in focus.

For the sake of this issue, I can reproduce this with almost an exact copy from the storybook https://react.daisyui.com/?path=/story/actions-modal--default

import { useCallback, useRef } from 'react';
import { Button, Modal } from 'react-daisyui';

export default function Foo() {
  const ref = useRef<HTMLDialogElement>(null);
  const handleShow = useCallback(() => {
    ref.current?.showModal();
  }, [ref]);

  return (
    <div>
      <Button onClick={handleShow}>Open</Button>
      <Modal ref={ref} backdrop={true}>
        <p>contents</p>
      </Modal>
    </div>
  );
}

Questions

Am I doing anything wrong or forgetting something? Should I handle this as a user of the modal component or should the component handle this internally? I would prefer the latter and I would be happy to create a pull request if that is the desired approach.

I found this commit which looks like it was made in an attempt to toggle the aria-hidden attribute based on the open prop. By my understanding this falls short in my instance, because I'm not passing in the open prop and controlling the modal through it, but rather by interacting with the underlying <dialog> node via the forwarded ref.

Current workaround

import { useCallback, useRef } from 'react';
import { Button, Modal } from 'react-daisyui';

export default function Foo() {
  const ref = useRef<HTMLDialogElement>(null);
  const handleShow = useCallback(() => {
    ref.current?.showModal();
  }, [ref]);

  const handleFocus = () => {
    if (ref.current) {
      ref.current.ariaHidden = String(!ref.current.open);
    }
  };

  return (
    <div>
      <Button onClick={handleShow}>Open</Button>
      <Modal ref={ref} backdrop={true} onFocus={handleFocus} onBlur={handleFocus}>
        <p>contents</p>
      </Modal>
    </div>
  );
}

I found this SO thread which may be useful for the implementation to handle this inside the modal component.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions