Skip to content

fix: improve accessibility of @ context menu for screen readers (#3186) #5918

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

roomote[bot]
Copy link

@roomote roomote bot commented Jul 18, 2025

Related GitHub Issue

Closes: #3186

Roo Code Task Context (Optional)

No Roo Code task context for this PR

Description

This PR implements comprehensive accessibility improvements for the @ context menu to make it fully accessible to screen readers. The issue reported that when users type '@' to trigger the file insertion context menu, the menu appears visually but is not announced by screen readers, making it inaccessible to users with visual impairments.

Key implementation details:

  • Added proper ARIA roles (role="listbox" for menu, role="option" for items)
  • Implemented ARIA states (aria-expanded, aria-selected, aria-activedescendant)
  • Added live region for real-time announcements to screen readers
  • Enhanced keyboard navigation with proper focus management
  • Added descriptive labels and instructions for screen reader users

Design choices:

  • Used aria-live="polite" to avoid interrupting screen reader flow
  • Positioned live region off-screen using standard screen reader techniques
  • Maintained existing visual design while adding semantic accessibility
  • Ensured announcements are contextual and informative

Test Procedure

Manual testing with screen readers:

  1. Open VSCode with a screen reader (VoiceOver, NVDA, or JAWS)
  2. Focus on the chat input field
  3. Type '@' to trigger the context menu
  4. Verify screen reader announces: "File insertion menu opened"
  5. Use arrow keys to navigate menu items
  6. Verify each item is announced with position info (e.g., "File: example.txt, 1 of 5")
  7. Press Escape to close menu
  8. Verify screen reader announces: "File insertion menu closed"

Keyboard navigation testing:

  • Arrow keys should navigate through selectable options
  • Enter/Tab should select the highlighted option
  • Escape should close the menu and return focus to textarea
  • Menu should maintain proper focus management

Pre-Submission Checklist

  • Issue Linked: This PR is linked to an approved GitHub Issue (see "Related GitHub Issue" above).
  • Scope: My changes are focused on the linked issue (one major feature/fix per PR).
  • Self-Review: I have performed a thorough self-review of my code.
  • Testing: New and/or updated tests have been added to cover my changes (if applicable).
  • Documentation Impact: I have considered if my changes require documentation updates (see "Documentation Updates" section below).
  • Contribution Guidelines: I have read and agree to the Contributor Guidelines.

Screenshots / Videos

No UI changes in this PR - accessibility improvements are semantic and announced by screen readers

Documentation Updates

  • No documentation updates are required.

Additional Notes

Accessibility standards compliance:

  • Follows WCAG 2.1 AA guidelines for keyboard navigation and screen reader support
  • Implements WAI-ARIA best practices for listbox pattern
  • Uses semantic HTML and ARIA attributes appropriately

Technical considerations:

  • Changes are backward compatible and don't affect existing functionality
  • Live region announcements are non-intrusive and contextual
  • Implementation follows existing code patterns and conventions

Get in Touch

@roomote-agent


Important

Improves accessibility of the @ context menu for screen readers by adding ARIA roles, states, live regions, and enhancing keyboard navigation in ChatTextArea.tsx and ContextMenu.tsx.

  • Accessibility Enhancements:
    • Added ARIA roles (role="listbox", role="option") and states (aria-expanded, aria-selected, aria-activedescendant) in ChatTextArea.tsx and ContextMenu.tsx.
    • Implemented live region for screen reader announcements in ChatTextArea.tsx.
    • Enhanced keyboard navigation with focus management in ChatTextArea.tsx.
    • Added descriptive labels and instructions for screen reader users in chat.json.
  • Behavior Changes:
    • Screen readers announce menu state changes and selected items in ChatTextArea.tsx.
    • Context menu items are announced with position information.
  • Localization:
    • Updated chat.json with new strings for screen reader announcements and instructions.

This description was created by Ellipsis for 7e5c486. You can customize this summary. It will automatically update as commits are pushed.

- Add proper ARIA roles and properties to ContextMenu component
- Add role="listbox" and aria-label to menu container
- Add role="option" and aria-selected to menu items
- Add aria-expanded, aria-haspopup, and aria-controls to textarea
- Add live region for screen reader announcements
- Announce menu state changes (open/close)
- Announce selected menu items with position info
- Add instructions for screen reader users
- Improve keyboard navigation accessibility

Fixes #3186
@roomote roomote bot requested review from mrubens, cte and jr as code owners July 18, 2025 18:56
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. UI/UX UI/UX related or focused labels Jul 18, 2025
setShowContextMenu(showMenu)

// Announce menu state changes for screen readers
if (showMenu && !wasMenuVisible) {
setScreenReaderAnnouncement("File insertion menu opened")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider localizing the screen reader announcement strings (e.g., "File insertion menu opened" and "closed") using the translation function instead of hardcoding English messages.

This comment was generated because it violated a code review rule: irule_C0ez7Rji6ANcGkkX.

@@ -559,6 +568,48 @@
}
}, [showContextMenu])

// Announce selected menu item for screen readers
useEffect(() => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The useEffect that announces the selected menu item builds its messages with hardcoded strings. Consider using the translation function (t) to ensure these announcements are localized.

This comment was generated because it violated a code review rule: irule_C0ez7Rji6ANcGkkX.

ref={menuRef}
role="listbox"
aria-label="File insertion menu"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ARIA label for the menu ('File insertion menu') is hardcoded. To support internationalization, consider using a translation function for this label.

Suggested change
aria-label="File insertion menu"
aria-label={t('File insertion menu')}

This comment was generated because it violated a code review rule: irule_C0ez7Rji6ANcGkkX.

@hannesrudolph hannesrudolph added the Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. label Jul 18, 2025
@daniel-lxs daniel-lxs moved this from Triage to PR [Needs Prelim Review] in Roo Code Roadmap Jul 21, 2025
@hannesrudolph hannesrudolph added PR - Needs Preliminary Review and removed Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. labels Jul 21, 2025
Copy link
Collaborator

@daniel-lxs daniel-lxs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @MuriloFP , I left points that need to be fixed for this PR, also let me know if you can test it

switch (selectedOption.type) {
case ContextMenuOptionType.File:
case ContextMenuOptionType.OpenedFile:
announcement = `File: ${selectedOption.value || selectedOption.label}, ${selectedMenuIndex + 1} of ${options.length}`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these announcement strings need to be translated. Consider creating a helper function that uses the translation system:

const getAnnouncementText = (option: ContextMenuQueryItem, index: number, total: number) => {
  const position = t("chat:contextMenu.position", { current: index + 1, total });
  
  switch (option.type) {
    case ContextMenuOptionType.File:
    case ContextMenuOptionType.OpenedFile:
      return t("chat:contextMenu.announceFile", { 
        name: option.value || option.label, 
        position 
      });
    // ... other cases
  }
};


{/* Instructions for screen readers */}
<div id="context-menu-instructions" className="sr-only">
Type @ to open file insertion menu. Use arrow keys to navigate, Enter to select, Escape to close.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This instruction text should also be translated:

<div id="context-menu-instructions" className="sr-only">
  {t("chat:contextMenu.instructions")}
</div>

@@ -559,6 +568,48 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
}
}, [showContextMenu])

// Announce selected menu item for screen readers
useEffect(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This useEffect runs on every selectedMenuIndex change during keyboard navigation, which could impact performance. Consider debouncing the announcements or only announcing when navigation pauses:

useEffect(() => {
  if (!showContextMenu || selectedMenuIndex < 0) return;
  
  const timeoutId = setTimeout(() => {
    // announcement logic here
  }, 100); // Small delay to avoid rapid announcements
  
  return () => clearTimeout(timeoutId);
}, [showContextMenu, selectedMenuIndex, /* other deps */]);

aria-live="polite"
aria-atomic="true"
className="sr-only"
style={{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The live region implementation uses inline styles. For consistency with the rest of the codebase, consider using Tailwind utility classes:

<div
  aria-live="polite"
  aria-atomic="true"
  className="sr-only absolute -left-[10000px] w-px h-px overflow-hidden"
>
  {screenReaderAnnouncement}
</div>

Note: The sr-only class might already provide the necessary styling for screen reader-only content.

@daniel-lxs
Copy link
Collaborator

@roomote-agent Check the reviews left on this PR and address them by using the switch_mode tool and switching to pr-fixer mode.
Commit all your changes to this PR, do not create a new one. Mention me when you finish.

Copy link
Author

roomote bot commented Jul 22, 2025

@daniel-lxs I have reviewed the feedback on this PR. I will now switch to pr-fixer mode to address all the review comments:

  1. Add translations for announcement strings using a helper function
  2. Translate the instruction text
  3. Add debouncing to the useEffect for better performance during keyboard navigation
  4. Replace inline styles with Tailwind classes for the live region

I will implement these changes and push them to this PR branch.

- Add translations for all announcement strings using helper function
- Translate instruction text for screen readers
- Add debouncing to useEffect to improve performance during keyboard navigation
- Replace inline styles with Tailwind classes for live region
- Import missing ContextMenuQueryItem type
- Fix ESLint warning by adding t to dependency array
@daniel-lxs
Copy link
Collaborator

@roomote-agent fix the failing unit tests by switching (switch_tool) to pr-fixer mode and fix the failing tests please, commit the changes to this PR, do not create a new one.

Copy link
Author

roomote bot commented Jul 22, 2025

Hi @daniel-lxs! I see your request. I will switch to pr-fixer mode to address the review comments and fix the failing tests. I'll work on this PR directly without creating a new one.

@RooCodeInc RooCodeInc deleted a comment from roomote bot Jul 22, 2025
@daniel-lxs daniel-lxs moved this from PR [Needs Prelim Review] to PR [Changes Requested] in Roo Code Roadmap Jul 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR - Changes Requested size:L This PR changes 100-499 lines, ignoring generated files. UI/UX UI/UX related or focused
Projects
Status: PR [Changes Requested]
Development

Successfully merging this pull request may close these issues.

Accessibility of @ context menu for screenreaders
3 participants