-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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 Menu component ARIA pattern #67078
Comments
I created ariakit/ariakit#4270 to track this issue upstream. |
I'd like to suggest some workarounds that Gutenberg can use to address these issues before they're fixed upstream, and I'd appreciate your feedback.
Ariakit automatically renders a "Dismiss popup" button within modals as a fallback if the developer doesn't render one themselves. This is essential because mobile screen reader users lack other methods to close a modal. They can't press Esc or click outside. Without such an option, these users would be stuck in the menu once it's open, forcing them to refresh the page or find another way to navigate back. As stated in the docs:
If removing the <Ariakit.MenuDismiss>
{__("Dismiss popup")}
</Ariakit.MenuDismiss> This isn't an option for Ariakit because it involves design decisions beyond the library's scope. However, we can definitely add a console warning about that. To fix the accessibility tree, pass <Ariakit.Menu modal role="dialog">
<Ariakit.MenuDismiss>
{__("Dismiss popup")}
</Ariakit.MenuDismiss>
<div role="menu" aria-labelledby="...">
{/* menu items */} Alternatively, if the design allows, the dismiss button could be a regular menu item accessible to everyone: <Ariakit.Menu modal>
{children}
<Ariakit.MenuItem render={<Ariakit.MenuDismiss />}>
{__("Close")}
</Ariakit.MenuItem>
</Ariakit.Menu>
Again, if removing the Alternatively, the menu button can be made accessible with the <Ariakit.Menu
modal
getPersistentElements={() => {
// Adds the menu button to the "modal context"
const { disclosureElement } = menuStore.getState();
if (!disclosureElement) return [];
return [disclosureElement];
}}
>
{/* Prevents Ariakit from rendering a visually hidden dismiss button */}
<Ariakit.MenuDismiss hidden /> This would address both (1) and (2) because all users could interact with the menu button and close the modal, making a dismiss button inside the menu not required.
Are any of those related to Ariakit usage? If so, could you please tag me or link them here so I can take a closer look? |
I'm not sure they are specifically related to Ariakit. Anyways one of the discussions about |
Thank you @afercia for reporting, and @diegohaz for looking into the matter so swiftly! Here are some additional considerations:
cc @WordPress/gutenberg-components |
Regarding the To recap:
Regardless whether it's a Chrome bug or a gray area in the specs. the point is the labeling of the menu doesn't work in the most used browser. We should fix it or make sure the Chrome is aware of this problem to potentially consider a fix. |
Description
Spotted while inspecting the new 'Preview' menu introduced in the global styles after #66376
The Menu component is based on Ariakit. At a very first inspection, it appears there's at least two accessibility failures:
1
The menu may render a visually hidden button labeled 'Dismiss popup'. This breaks the ARIA menu pattern.
A menu should only contain menuitems, their variant menuitemradio/checkbox, and groups or separators.
The 'Dismiss popup' button can't be tabbed to or focused by using arrows anyways. However, screen reader users can still move to it.
I doubt the button label 'Dismiss popup' is translatable.
Regardless, this button shouldn't be there in the first place.
Screenshots, where I'm also revealing the visually hidden button by removing some CSS:
It appears the Ariakit Menu uses the Ariakit Dialog internally, and this button rendering is controlled by the
modal
prop.I'm not into discussing the Ariakit internal implementation, which I find arguable, but this is not OK. This button should be removed.
At the very least, the Gutenberg implementation should explore to set the modal prop to
false
. However, I'm not sure about potential side effects when changing this prop value.Alternatively, the implementation should be fixed upstream.
2
The Menu may rander an
aria-labelledby
attribute on the element with role=menu that apparently points to an invalid ID. Actually, the referenced ID is in the DOM so that the labeling is supposed to work, theoretically. This was interesting to debug and it took some time.By default, the
aria-labelledby
of the element withrole=menu
points to the trigger button that opens the menu. As such, the menu is supposed to be labeled with whatever the accessible name of the trigger button is. It's a sensible default.However, when the
modal
prop istrue
, when the Ariakit menu opens it adds (via the internal DIalog component) aninert
attribute to the sibling elements, which in WordPress happens to be:<div id="wpwrap" inert>
.This triggers an inconsistent behavior between menus with a trigger button that uses an icon+aria-label and the ones that use a trigger button with visible text. The inconsistency appears to related to browsers behavior as well. I was able to reproduce this bug with Chrome but wasn't with Firefox.
When inspecting the DOM via the Chrome dev tools > Elements > Accessibility panel:
Screenshot:
This isn't surprising because the trigger button, which is responsible to provide the labeling for the menu, is actually inside an
inert
part of the DOM. Browsers are supposed to make anyinert
part not perceivable. As such, at least in Chrome, thearia-labelledby
points to an element that is in an inert area. I guess this explains theinvalid source
warning in Chrome.Why it works for menus with an icon trigger button then? I can only guess that icon buttons have a tooltip that renders outside the inert part of the DOM. It appears this helps Chrome locate the referenced element and compute the accessible name.
I'm not sure what is the expected browsers behavior regarding referenced elements that live within an
inert
area. Historically, even if a referenced element is visually hidden with CSS or thehidden
attribute, browsers are expected to be able to reference them for things like aria-labelledby or aria-describedby. However, theinert
case seems ot be different, at least in Chrome.Regardless, this appears to be one more occurrences of untested usage of the
inert
attribute. In previous issues and PRs, there have been already reports that theinert
attribute is problematic for accessibility and should be used with extreme care. In all cases, its usage should be thoughtfully tested which doesn't appear to be the case for the Ariakit implementation.Since the
inert
attribute is only added when themodal
prop is true, I suggest to reconsider the usage of this prop or implementa more safe mechanism to make the rest of the page not preceivable. The Gutenberg Modal component uses the aria-hidden attribute for a reason.Step-by-step reproduction instructions
1
role=menu
contains a visually hidden button labeled 'Dismiss popup`.2
role=menu
does have anaria-labelledby
attribute that correctly points to the trigger button.Screenshots, screen recording, code snippet
No response
Environment info
No response
Please confirm that you have searched existing issues in the repo.
Please confirm that you have tested with all plugins deactivated except Gutenberg.
Please confirm which theme type you used for testing.
The text was updated successfully, but these errors were encountered: