Skip to content

Conversation

marissahuysentruyt
Copy link
Collaborator

@marissahuysentruyt marissahuysentruyt commented Oct 17, 2025

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:

  1. Added dismissHelper getter to Tray.ts that returns a visually hidden button template
  2. Updated render method to wrap slotted content with dismiss helpers
  3. Enhanced CSS to support .visually-hidden class for shadow DOM content
  4. Comprehensive documentation in README with explanations and tabbed examples

The 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 accessibility gap was identified during an accessibility audit and affects any tray usage with menu content that lacks a built-in dismiss mechanism.

Related issue(s)

Screenshots (if appropriate)

N/A - This is a non-visual accessibility enhancement. The dismiss buttons are visually hidden but accessible to screen readers.


Author's checklist

  • I have read the CONTRIBUTING and PULL_REQUESTS documents.
  • I have reviewed the Accessibility Practices for this feature, see: Aria Practices
  • I have added automated tests to cover my changes.
  • I have included a well-written changeset if my change needs to be published.
  • I have included updated documentation if my change required it.

Reviewer's checklist

  • Includes a Github Issue with appropriate flag or Jira ticket number without a link
  • Includes thoughtfully written changeset if changes suggested include patch, minor, or major features
  • Automated tests cover all use cases and follow best practices for writing
  • Validated on all supported browsers
  • All VRTs are approved before the author can update Golden Hash

Manual review test cases

  • Verify dismiss buttons are accessible to VoiceOver on Mac

    1. Enable VoiceOver (Cmd + F5)
    2. Open a tray with menu content using the examples in the documentation
    3. Navigate forward with Control + Option + Right Arrow
    4. Expect: VoiceOver announces "Dismiss, button" before the first menu item
    5. Continue navigating through all menu items
    6. Expect: VoiceOver announces "Dismiss, button" after the last menu item
    7. Activate either dismiss button with Control + Option + Space
    8. Expect: Tray closes successfully
  • Verify dismiss buttons don't interfere with keyboard navigation

    1. Open a tray with menu content
    2. Press Tab key to navigate through interactive elements
    3. Expect: Tab navigation skips the visually hidden dismiss buttons (due to tabindex="-1")
    4. Expect: Tab moves directly from trigger to menu items
  • Verify dismiss buttons work with dialog content

    1. Open a tray with dismissable dialog content
    2. Enable VoiceOver
    3. Navigate through the tray content
    4. Expect: Both the tray's dismiss helpers and dialog's close button are accessible
    5. Verify either method successfully closes the overlay
  • Verify dismiss buttons are visually hidden

    1. Open a tray with menu content in a browser
    2. Inspect the tray element
    3. Expect: Dismiss button divs exist in DOM with .visually-hidden class
    4. Expect: No visual indication of these buttons (1px size, positioned absolutely, clipped)

Device review

  • Did it pass in Desktop?
  • Did it pass in (emulated) Mobile?
  • Did it pass in (emulated) iPad?

Note: Primary testing should be done with VoiceOver on iOS/iPadOS devices or simulators where the original accessibility issue was discovered.

@marissahuysentruyt marissahuysentruyt self-assigned this Oct 17, 2025
Copy link

changeset-bot bot commented Oct 17, 2025

⚠️ No Changeset found

Latest commit: 642619c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

github-actions bot commented Oct 17, 2025

📚 Branch Preview

🔍 Visual Regression Test Results

When 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: pr-5814

If the changes are expected, update the current_golden_images_cache hash in the circleci config to accept the new images. Instructions are included in that file.
If the changes are unexpected, you can investigate the cause of the differences and update the code accordingly.

@marissahuysentruyt marissahuysentruyt added bug Something isn't working a11y Issues related to accessibility Component: Tray labels Oct 17, 2025
Copy link
Contributor

Tachometer results

Currently, no packages are changed by this PR...

@coveralls
Copy link
Collaborator

coveralls commented Oct 17, 2025

Pull Request Test Coverage Report for Build 18664268679

Details

  • 38 of 38 (100.0%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.002%) to 97.959%

Totals Coverage Status
Change from base Build 18535024849: 0.002%
Covered Lines: 34220
Relevant Lines: 34754

💛 - Coveralls

@marissahuysentruyt
Copy link
Collaborator Author

marissahuysentruyt commented Oct 20, 2025

TODO questions:

  • Should the visually-hidden dismiss buttons extend past just mobile trays? (like not just for small screens, but at all screen sizes) edit: trays are intended for small screens. i think sp-dialog-wrapper is the large screen equivalent.
  • Should the dismiss buttons be tied to any dismissible property? For instance, if there's a visible & accessible close button in the dialog content, do we still or should we still have visually-hidden buttons before and after the rest of the content?
  • If DialogWrapper also doesn't support visually-hidden dismiss buttons but it should- should those buttons go on DialogBase since DialogWrapper extends DialogBase? What about regular Dialog without the dismissible property? (edit: adding the dismissHelper to sp-dialog itself/Dialog.ts seems to flow how I am expecting.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y Issues related to accessibility bug Something isn't working Component: Tray

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug][a11y]: Tray component lacks visually hidden dismiss button to close the overlay on mobile

2 participants