instructure/instructure-ui

Alerts rendered after closing a Modal aren't read by VoiceOver

Hawkbawk opened this issue · 2 comments

Background Information

Package Version(s):
ui-modal v7.14.0 (the version currently used by Canvas)

Browser:
Safari v15.4
Firefox v100.0

OS:
macOS v12.3.1

Device:
Macbook Pro 16"

Component:
Modal

Screen Reader:
VoiceOver

Describe the Issue

If you try and show an alert after closing a Modal, the alert will not be read out by VoiceOver. Instead, the screenreader will read out a description of the element that opened the Modal. This happens regardless of what methodology you use to create the alert, whether that's using the Alert component, or just creating your own custom aria-live region.

After some experimentation and discussion, my current working theory (along with some other engineers, namely Ed Schiebel) is that the Modal is “steamrolling” the alert by returning focus to the element that opened the Modal. Basically, after the Modal has closed, VoiceOver tries to read out the alert, but the Modal changes the focused element so fast that VoiceOver skips over the alert, reading out a description of the element that opened the Modal instead.

I've tried rendering the alerts along with my other components at a top-level component, rendering them in the onClosing callback, and finally using setTimeout to force them to be rendered after the Modal has finished closing and messing with focus. Out of all these attempted solutions, only the setTimeout solution worked.

This issue was discovered by myself while working on an a11y issue discovered in Canvas related to some of my team's alerts not being read out. I haven't tested on all browsers or on Windows with JAWS, but I suspect the issue is likely present there as well.

Steps To Reproduce

I've created a quick example app at https://github.com/Hawkbawk/modal-a11y-poc/. I've outlined the steps necessary to reproduce the issue. Basically, clone the repo, install the dependencies, start the react-app, open the app in Safari with VoiceOver enabled and notice that alerts that are rendered after the Modal closes aren't being read out.

Expected Behavior

Screenreaders should read out alerts that are rendered after a Modal is closed. Really, anything marked "aria-live=assertive" should be read out, rather than being steamrolled.

Screenshots

N/A

Additional Information

Current Workaround(s):

The workaround that we implemented in Canvas was to render the alert roughly two seconds after the Modal closes using setTimeout. This workaround is really not ideal, as it might not even solve the issue for users on slower hardware, it makes testing difficult, and it negatively impacts user experience for all users.

Products Affected:

Currently, basically anywhere in Canvas that tries to render an Alert after a Modal closes is affected. This seems like a pretty common use case, as telling the user whether their changes were made successfully is pretty common.

Are you willing to submit a PR to fix?

  • Yes, I'm willing to submit a PR

If truly necessary, I could try making a PR for this, but it seems like it will be a finicky issue that might require some discussion on what the best solution might be.

Requested Priority:

High/Normal

Within Instructure, I know we normally mark a11y issues as Pressing priority, as a11y is a very big deal for us, so High/Normal seems appropriate.

I'm a fellow engineer at Instructure, so if there are any questions that you don't want on Github, feel free to send me a message on Slack and I can answer any more questions or provide more information.

Thank you for the very detailed issue report Ryan!
We will look into this asap and will reach out to you if we have questions/updates.