Skip to content

Conversation

@marissahuysentruyt
Copy link
Collaborator

@marissahuysentruyt marissahuysentruyt commented Nov 13, 2025

Description

Added aria-current attribute logic to sp-menu-item elements to improve accessibility for screen reader users. When a menu item with role="menuitem" is selected, it now sets aria-current="true". When not selected, the attribute is removed (not set to "false") following ARIA best practices.

Motivation and context

This change addresses a WCAG 4.1.2 (Name, Role, Value) accessibility violation discovered during an accessibility audit. Screen readers were not announcing the selected state for menu items with role="menuitem", causing confusion for users relying on assistive technology.

When a menu item was selected, screen readers would only announce "Deselect 1 of 6" without indicating the item's selected state. This particularly impacts users without vision, with limited vision, and those with cognitive disabilities who rely on complete state information for navigation. Now, the screen reader should announce "Deselect, current, 1 of 6."

Screenshot 2025-11-13 at 5 56 24 PM

Related issue(s)

  • Fixes SWC-1108

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

  • Screen reader announces selected state for menuitem

    1. Go to sp-tray documentation
    2. Navigate to the 'Anatomy' section, 'Dialog' tab, and select the 'Menu' tab
    3. Activate the "Toggle menu" button and navigate to a selected menu item using a screen reader (NVDA, JAWS, or VoiceOver)
      i. In the bug ticket, they are specifically using NVDA on a Windows machine, so I was using AssitivLabs during development.
    4. Expect the screen reader to announce the selected/current state (e.g., "Deselect, current, 1 of 6")
  • aria-current attribute is correctly applied

    1. Inspect a selected sp-menu-item with role="menuitem" in browser DevTools
    2. Verify it has aria-current="true" attribute
    3. Inspect a non-selected sp-menu-item with role="menuitem"
    4. Verify it does NOT have an aria-current attribute (not set to "false")
    5. Manually remove the selected attribute from the first item.
    6. Verify the corresponding aria-current attribute is removed.
  • Other menu item roles are unaffected

    1. Test menu items with role="menuitemradio" and role="menuitemcheckbox"
    2. Verify they still use aria-checked (not aria-current)
    3. Test menu items with role="option" in a listbox context
    4. Verify they still use aria-selected (not aria-current)
  • New unit tests pass in CI.

    1. "sets aria-current to true if the item has selected property"
    2. "removes aria-current if the item is deselected"
    3. "updates aria-current when the selected item changes"
  • New documentation for menu item is technically and grammatically accurate and follows Spectrum content standards.

Device review

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

@changeset-bot
Copy link

changeset-bot bot commented Nov 13, 2025

🦋 Changeset detected

Latest commit: 7b19176

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 78 packages
Name Type
@spectrum-web-components/menu Patch
@spectrum-web-components/breadcrumbs Patch
@spectrum-web-components/combobox Patch
@spectrum-web-components/picker Patch
@spectrum-web-components/bundle Patch
@spectrum-web-components/action-menu Patch
@spectrum-web-components/accordion Patch
@spectrum-web-components/action-bar Patch
@spectrum-web-components/action-button Patch
@spectrum-web-components/action-group Patch
@spectrum-web-components/alert-banner Patch
@spectrum-web-components/alert-dialog Patch
@spectrum-web-components/asset Patch
@spectrum-web-components/avatar Patch
@spectrum-web-components/badge Patch
@spectrum-web-components/button-group Patch
@spectrum-web-components/button Patch
@spectrum-web-components/card Patch
@spectrum-web-components/checkbox Patch
@spectrum-web-components/clear-button Patch
@spectrum-web-components/close-button Patch
@spectrum-web-components/coachmark Patch
@spectrum-web-components/color-area Patch
@spectrum-web-components/color-field Patch
@spectrum-web-components/color-handle Patch
@spectrum-web-components/color-loupe Patch
@spectrum-web-components/color-slider Patch
@spectrum-web-components/color-wheel Patch
@spectrum-web-components/contextual-help Patch
@spectrum-web-components/dialog Patch
@spectrum-web-components/divider Patch
@spectrum-web-components/dropzone Patch
@spectrum-web-components/field-group Patch
@spectrum-web-components/field-label Patch
@spectrum-web-components/help-text Patch
@spectrum-web-components/icon Patch
@spectrum-web-components/icons-ui Patch
@spectrum-web-components/icons-workflow Patch
@spectrum-web-components/icons Patch
@spectrum-web-components/iconset Patch
@spectrum-web-components/illustrated-message Patch
@spectrum-web-components/infield-button Patch
@spectrum-web-components/link Patch
@spectrum-web-components/meter Patch
@spectrum-web-components/modal Patch
@spectrum-web-components/number-field Patch
@spectrum-web-components/overlay Patch
@spectrum-web-components/picker-button Patch
@spectrum-web-components/popover Patch
@spectrum-web-components/progress-bar Patch
@spectrum-web-components/progress-circle Patch
@spectrum-web-components/radio Patch
@spectrum-web-components/search Patch
@spectrum-web-components/sidenav Patch
@spectrum-web-components/slider Patch
@spectrum-web-components/split-view Patch
@spectrum-web-components/status-light Patch
@spectrum-web-components/swatch Patch
@spectrum-web-components/switch Patch
@spectrum-web-components/table Patch
@spectrum-web-components/tabs Patch
@spectrum-web-components/tags Patch
@spectrum-web-components/textfield Patch
@spectrum-web-components/thumbnail Patch
@spectrum-web-components/toast Patch
@spectrum-web-components/tooltip Patch
@spectrum-web-components/top-nav Patch
@spectrum-web-components/tray Patch
@spectrum-web-components/underlay Patch
@spectrum-web-components/base Patch
@spectrum-web-components/grid Patch
@spectrum-web-components/opacity-checkerboard Patch
@spectrum-web-components/reactive-controllers Patch
@spectrum-web-components/shared Patch
@spectrum-web-components/styles Patch
@spectrum-web-components/theme Patch
@spectrum-web-components/truncated Patch
@spectrum-web-components/eslint-plugin Patch

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

@marissahuysentruyt marissahuysentruyt changed the title fix(menuitem): add aria-current logic fix(menuitem): improve a11y of selected items with aria-current Nov 13, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 13, 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-5888

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 self-assigned this Nov 13, 2025
@marissahuysentruyt marissahuysentruyt added 1.0.0 Issues that should be addressed for a 1.0 release! a11y Issues or PRs related to accessibility SEV 2 significant problem re: design, usability, or functionality of the system/components for some users labels Nov 13, 2025
@marissahuysentruyt marissahuysentruyt marked this pull request as ready for review November 13, 2025 22:51
@marissahuysentruyt marissahuysentruyt requested a review from a team as a code owner November 13, 2025 22:51
Copy link
Contributor

@caseyisonit caseyisonit left a comment

Choose a reason for hiding this comment

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

Gorg

Copy link
Contributor

@Rajdeepc Rajdeepc left a comment

Choose a reason for hiding this comment

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

Good fix!! One thing was to test that updateAriaSelected() is also called whenever the selected property changes which I see it is happening on

if (changes.has('selected')) {
    this.updateAriaSelected();
}

@Rajdeepc Rajdeepc added the Status: Ready for merge PR has 2 approvals, all tests pass, and is ready to merge label Nov 14, 2025

#### Roles and ARIA attributes

When a menu item with `role="menuitem"` is selected, the `aria-current="true"` attribute is automatically applied to indicate the current item to screen reader users. This ensures assistive technology properly announces the selected state of menu items.
Copy link
Contributor

Choose a reason for hiding this comment

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

The appropriate way to indicate a selected state for an item in a menu is to use either role="menuitemcheckbox" for multi-selectable items, or role="menuitemradio" for mutually exclusive selection, with the aria-checked attribute to indicate the selected state. An element with role="menuitem" does not support the aria-checked or aria-selected property. See the supported states and properties for a menu item: https://w3c.github.io/aria/#menuitem.

An menuitem can support aria-current, but only in an appropriate context. See https://w3c.github.io/aria/#aria-current, which explains appropriate usage of aria-current:

aria-current state
Indicates the element that represents the current item within a container or set of related elements.

The aria-current attribute is a token type. Any value not included in the list of allowed values SHOULD be treated by assistive technologies as if the value true had been provided. If the attribute is not present or its value is the empty string or undefined, the default value of false applies and the aria-current state MUST NOT be exposed by user agents or assistive technologies.

The aria-current attribute is used when an element within a set of related elements is visually styled to indicate it is the current item in the set. For example:

  • A page token used to indicate a page within a set of pages, where the element is visually styled to represent the current page.
  • A step token used to indicate a step within a step-based process, where the element is visually styled to represent the current step.
  • A location token used to indicate the element that is visually styled as the current component, such as within a flow chart.
  • A date token used to indicate the current date within a calendar or other date collection.
  • A time token used to indicate the current time within a timetable or other time collection.
    Authors SHOULD only mark one element in a set of elements as current with aria-current.

Authors SHOULD NOT use the aria-current attribute as a substitute for aria-selected in widgets where aria-selected has the same meaning. For example, in a tablist, aria-selected is used on a tab to indicate the currently-displayed tabpanel.

Note how the WAI-ARIA spec explicitly states that "Authors SHOULD NOT use the aria-current attribute as a substitute for aria-selected in widgets where aria-selected has the same meaning."

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Let me rework this to come up with a solution that better fits how you're seeing the solution!

Comment on lines +722 to +728
if (role === 'menuitem') {
if (this.selected) {
this.setAttribute('aria-current', 'true');
} else {
this.removeAttribute('aria-current');
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd prefer to keep aria-current as a separate concern from selection.

role is determined by either the role on the containing sp-menu or the selects property on the parent sp-menu or sp-menu-group. If the selected state is indicated on a menuitem without selects="multiple", selects="single" or selects="inherit" on the parent sp-menu or sp-menu-group, we should maybe change the role for the sp-menu-item to menuitemcheckbox. This gets messy though, because then the question becomes, what then is the role for the unselected state, and how do we indicate that an unselected item is selectable?

Copy link
Collaborator Author

@marissahuysentruyt marissahuysentruyt Nov 14, 2025

Choose a reason for hiding this comment

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

@majornista ah- thank you! I did see the note in the W3C docs about not substituting aria-current for aria-selected, but the ticket specifically called out aria-current as the solution, which is why I went this route.

- for role="menuitem", set aria-current="true" if selected, but do not
add aria-current="false" if not selected as it's unnecessary
@marissahuysentruyt marissahuysentruyt force-pushed the marissahuysentruyt/swc-1108-aria-current-on-selected-menu-item branch from c9506a5 to 7b19176 Compare November 14, 2025 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.0.0 Issues that should be addressed for a 1.0 release! a11y Issues or PRs related to accessibility SEV 2 significant problem re: design, usability, or functionality of the system/components for some users Status: Ready for merge PR has 2 approvals, all tests pass, and is ready to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants