-
Couldn't load subscription status.
- Fork 235
fix(tray): add visually hidden dismiss buttons #5814
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
base: main
Are you sure you want to change the base?
fix(tray): add visually hidden dismiss buttons #5814
Conversation
🦋 Changeset detectedLatest commit: 661de4a The changes in this PR will be included in the next version bump. This PR includes changesets to release 84 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📚 Branch Preview🔍 Visual Regression Test ResultsWhen a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:
Deployed to Azure Blob Storage: If the changes are expected, update the |
Tachometer resultsChrometray permalinkbasic-test
Firefoxtray permalinkbasic-test
|
Pull Request Test Coverage Report for Build 18786444697Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
|
|
642619c to
a7363f4
Compare
a7363f4 to
225faf7
Compare
dd22eca to
a8be2ba
Compare
…pers we do not need to provide a close/visually hidden button for non-dismissible diaologs
…tons we do not need to provide a close/visually hidden button for non-dismissible dialogs
we do not need to provide a close/visually hidden button for non-dismissible dialogs
- adds several properties and methods to tray API to support more flexibile dismissible behavior. - queries for keyboard-accessible dismiss buttons in the tray's slot content - adds a state property to track if dismiss buttons are needed - adds manual override for dismissible behavior
a8be2ba to
542fb4d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I loved all the regressions tests and the descriptive PR with reviewer manual testing instructions. Don't forget to add keyboard testing of this button as screen reader users need to access this button via keyboard.
For example VoiceOver is reading the button on the example in the first tab here https://swcpreviews.z13.web.core.windows.net/pr-5814/docs/components/tray/#accessibility, but I can't access the button via keyboard, so I still can't dismiss the tray.
packages/tray/src/Tray.ts
Outdated
| return html` | ||
| <div class="visually-hidden"> | ||
| <button | ||
| tabindex="-1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Screenreader users need to be able to access this via keyboard, so a tabindex should not exist here.
| tabindex="-1" |
| }); | ||
|
|
||
| describe('Dismiss helpers', () => { | ||
| it('renders visually-hidden dismiss helpers when no buttons detected', async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also text that this button can be accessed via keyboard to dismiss the tray.
|
|
||
| expect(el.open).to.be.false; | ||
| }); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love all the regression tests that were added here. :chefs_kiss:
| ></sp-underlay> | ||
| <div | ||
| class="tray modal" | ||
| tabindex="-1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if I'm still missing anything here. Without removing the tabindex="-1" on the .tray.modal, I can still tab to the buttons.
Screen.Recording.2025-10-24.at.2.11.49.PM.mov
Did we want some sort of focus indicator? What's the expectation if the button is visually hidden, but keyboard accessible? 🤔

| ); | ||
| expect(buttons.length).to.equal(2); | ||
| expect(buttons[0].getAttribute('aria-label')).to.equal('Dismiss'); | ||
| expect(buttons[0].getAttribute('tabindex')).to.be.null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's test this by tabbing to the button, pressing Enter and checking if the tray closes. It is a more accurate way to test if this works.
In rthe PR description, we should also ask reviews to test with a keyboard as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description should be updated (sorry I missed that earlier!).
I also have 2 more tests now- one to make sure the button is tabbable, and the other to make sure enter closes the tray once the button is tabbed to. Thanks for that!

Description
This PR implements visually hidden dismiss buttons in the
<sp-tray>component to improve mobile screen reader accessibility, particularly for VoiceOver on iOS. The dismiss buttons are automatically rendered before and after the tray's slotted content, allowing mobile screen reader users to easily dismiss the overlay from either end without requiring manual implementation by developers.Changes include:
dismissHelpergetter toTray.tsthat returns a visually hidden button template.visually-hiddenclass for shadow DOM contentThe implementation follows the same pattern used in the
<sp-picker>component, ensuring consistency across the design system.Motivation and context
Mobile screen reader users, particularly those using VoiceOver on iOS, navigate through interactive elements sequentially (by swiping). Without dismiss buttons at the beginning and end of tray content, users who navigate past all menu items have difficulty discovering how to close the overlay - they must either navigate backward through all items or close the app entirely.
This gap affects any tray usage with menu content that lacks a built-in dismiss mechanism.
Related issue(s)
Screenshots (if appropriate)
Author's checklist
Reviewer's checklist
patch,minor, ormajorfeaturesManual review test cases
Verify dismiss buttons are accessible to VoiceOver on Mac [@cdransf]
Cmd + F5or from within the Accessibility/VoiceOver menus in System Settings)Control + Option + Right ArrowControl + Option + SpaceVerify dismiss buttons don't interfere with keyboard navigation [@cdransf]1. Open a tray with menu content2. Press Tab key to navigate through interactive elements3. Expect: Tab navigation skips the visually hidden dismiss buttons (due totabindex="-1")4. Expect: Tab moves directly from trigger to menu itemsVerify dismiss buttons are keyboard accessible
Verify auto-detection correctly identifies existing buttons
Verify manual override property works correctly
Verify dismiss buttons work with dialog content [@cdransf]
Verify dismiss buttons are visually hidden [@cdransf]
.visually-hiddenclassDevice review
Note: Primary testing should be done with VoiceOver on iOS/iPadOS devices or simulators where the original accessibility issue was discovered.