Conversation
The Expensify logo button and workspace list rows navigate to other pages
but were announced as buttons by screen readers. This changes them to use
CONST.ROLE.LINK to correctly convey link semantics per WCAG 4.1.2.
Co-authored-by: {"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#get-an-issue-comment","status":"404"} <{"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#get-an-issue-comment","status":"404"}@users.noreply.github.com>
Resolved merge conflict: main introduced a conditional role (isWide ? CONST.ROLE.ROW : CONST.ROLE.BUTTON) since the original commit. Updated to use CONST.ROLE.LINK in the narrow case while preserving CONST.ROLE.ROW for wide layout. Co-authored-by: Joel Davies <jjcoffee@users.noreply.github.com>
|
@MelvinBot mark this as ready for review. |
|
This PR has been marked as ready for review as requested by |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppandroid-app-2026-03-13_14.33.26.mp4Android: mWeb Chromeandroid-chrome-2026-03-13_14.35.53.mp4MacOS: Chrome / Safaridesktop-chrome-2026-03-13_15.59.08.mp4desktop-chrome-2026-03-13_14.11.48.mp4 |
|
@MelvinBot This has gone from announcing |
|
Good question — the change from Why the announcement changedThe root cause is how React Native Web maps roles to HTML elements:
What's expected vs. what needs fixing
Recommended fixAdd <ImageSVG
style={StyleUtils.getAvatarStyle(CONST.AVATAR_SIZE.DEFAULT)}
src={expensifyIcons.ExpensifyAppIcon}
pointerEvents="none"
aria-hidden={true}
/>This way the screen reader would see Would you like me to make this fix? |
|
@MelvinBot |
The SVG icon inside the link-role pressable was being announced as a separate "image" element by screen readers. Adding aria-hidden hides the decorative icon so only the link's aria-label is announced, giving a clean "Home, link" announcement. Co-authored-by: jjcoffee <pjcoffee@gmail.com> Co-authored-by: Joel Davies <jjcoffee@users.noreply.github.com>
|
Done — I've added
This should give screen readers a clean "Home, link" announcement instead of "link, image, Home". |
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
We did not find an internal engineer to review this PR, trying to assign a random engineer to #76938 as well as to this PR... Please reach out for help on Slack if no one gets assigned! |
|
🚧 @mollfpr has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚀 Deployed to staging by https://github.com/mollfpr in version: 9.3.39-0 🚀
|
|
This PR failing because of the issue #85618 |
|
🚀 Deployed to production by https://github.com/cristipaval in version: 9.3.39-3 🚀
|


Explanation of Change
The Expensify logo button in the wide-layout NavigationTabBar and workspace rows on the Workspaces page both navigate to other pages but were using
CONST.ROLE.BUTTON, causing screen readers to announce them as "Button" instead of "Link". Per WCAG 4.1.2 (Name, Role, Value), elements that perform navigation should use link semantics.This PR changes both to use
CONST.ROLE.LINK:NavigationTabBar/index.tsx— changed fromaccessibilityRole={CONST.ROLE.BUTTON}torole={CONST.ROLE.LINK}(also migrates from the deprecatedaccessibilityRoleprop to the modernroleprop, consistent with other items in the same component).WorkspacesListRow.tsx— changedrole={isWide ? CONST.ROLE.ROW : CONST.ROLE.BUTTON}torole={isWide ? CONST.ROLE.ROW : CONST.ROLE.LINK}. On wide layout,CONST.ROLE.ROWis preserved (correct for table row semantics); on narrow layout, the button role is replaced with link.BaseGenericPressablealready has built-in W3C APG Link Pattern keyboard handling forrole="link"(Enter key activation), so this change is well-supported.Fixed Issues
$ #76938
PROPOSAL: #76938 (comment)
Tests
https://new.expensify.com/settings/profileon a wide screen layoutOffline tests
No offline-specific behavior changes. The role attribute is a static accessibility property and does not depend on network state.
QA Steps
https://new.expensify.com/settings/profilePR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
N/A - accessibility role change only, no visual difference
Android: mWeb Chrome
N/A - accessibility role change only, no visual difference
iOS: Native
N/A - accessibility role change only, no visual difference
iOS: mWeb Safari
N/A - accessibility role change only, no visual difference
MacOS: Chrome / Safari
N/A - accessibility role change only, no visual difference. Verify with screen reader that elements announce as "link" instead of "button".