Skip to content

[Stepper][MenuList][Tabs] Improve accessibility#47687

Merged
silviuaavram merged 76 commits intomui:masterfrom
silviuaavram:feat/stepper-accessibility
Mar 12, 2026
Merged

[Stepper][MenuList][Tabs] Improve accessibility#47687
silviuaavram merged 76 commits intomui:masterfrom
silviuaavram:feat/stepper-accessibility

Conversation

@silviuaavram
Copy link
Member

@silviuaavram silviuaavram commented Jan 27, 2026

Fixes #43689.
Fixes #32826.

Stepper:

  • improve markup for stepper and step, from divs to ol and li.
  • implement roving tabindex focus for the step buttons.
  • step buttons also receive the role of tab, aria-posinset, aria-setsize and aria-selected instead of aria-current.
  • stepper that has step buttons will have the role of tablist and aria-orientation
  • step that has step buttons will have the role of presentation.

Technical:

  • roving tabindex has a separate hook that keeps track of the current focusable item (by index).
  • it exports 2 prop getters, one for container, one for item, and a focusNext function
  • one source of truth for tabindex values -> the hook.
  • update the Tabs and MenuList to also use the hook instead of their own logic
  • exported focusNext from the hook, for MenuList to use it for character key focus, in order to keep the single source of truth for the tabindex.
  • using refs instead of parsing DOM directly for getting the focusable siblings

UX for roving tabindex:

  • tabindex now follows focus. before, it stayed on the selected item, even as you moved away with arrow keys. this created issues:
    • if you tabbed from and back, the previously focused item was forgotten, you would've ended up on the selected item
    • if you moved selection from selected item with arrows, then tabbed / shift tabbed, you could have ended up with the selected item focused, which should not have happened
  • this applies to Stepper, Menu / MenuList, Tabs.

Copilot AI review requested due to automatic review settings January 27, 2026 08:27
@mui-bot
Copy link

mui-bot commented Jan 27, 2026

Netlify deploy preview

Bundle size report

Bundle Parsed size Gzip size
@mui/material 🔺+1.61KB(+0.31%) 🔺+678B(+0.45%)
@mui/lab 0B(0.00%) 0B(0.00%)
@mui/system 0B(0.00%) 0B(0.00%)
@mui/utils 🔺+1.94KB(+15.10%) 🔺+761B(+15.11%)

Details of bundle changes

Generated by 🚫 dangerJS against 43d0bf6

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request improves the accessibility of the Stepper component by implementing semantic HTML markup and keyboard navigation following ARIA best practices.

Changes:

  • Replaced div elements with semantic ol/li elements for Stepper and Step components
  • Implemented roving tabindex focus management with arrow key navigation for step buttons
  • Added ARIA attributes (aria-posinset, aria-setsize, aria-orientation) to improve screen reader support
  • Refactored StepperContext to export only useStepperContext hook and StepperContextProvider, removing the default export

Reviewed changes

Copilot reviewed 13 out of 15 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
packages/mui-material/src/Stepper/utils/useRovingTabIndexFocus.ts New hook implementing circular keyboard navigation with arrow keys, skipping disabled steps
packages/mui-material/src/Stepper/utils/useRovingTabIndexFocus.test.tsx Comprehensive unit tests for the roving tabindex hook
packages/mui-material/src/Stepper/index.js Removed default export of StepperContext, keeping named exports
packages/mui-material/src/Stepper/index.d.ts Updated TypeScript exports to match JavaScript changes
packages/mui-material/src/Stepper/StepperContext.ts Added new context properties for focus management, marked as @internal, exported StepperContextProvider
packages/mui-material/src/Stepper/Stepper.test.tsx Updated test to expect HTMLOListElement instead of HTMLDivElement
packages/mui-material/src/Stepper/Stepper.js Changed root element from div to ol, integrated roving tabindex, added aria-orientation
packages/mui-material/src/StepLabel/StepLabel.js Updated to use useStepperContext hook instead of direct context import
packages/mui-material/src/StepContent/StepContent.js Updated to use useStepperContext hook instead of direct context import
packages/mui-material/src/StepConnector/StepConnector.js Updated to use useStepperContext hook instead of direct context import
packages/mui-material/src/StepButton/StepButton.test.js Added StepperContextProvider wrapper to all test cases
packages/mui-material/src/StepButton/StepButton.js Integrated roving tabindex, added ARIA attributes, implemented keyboard and click handlers
packages/mui-material/src/StepButton/StepButton.d.ts Added TypeScript definitions for onClick and onKeyDown props
packages/mui-material/src/Step/Step.test.js Updated tests for li element and added StepperContextProvider wrappers
packages/mui-material/src/Step/Step.js Changed root element from div to li, updated to use useStepperContext hook

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@silviuaavram silviuaavram marked this pull request as draft January 27, 2026 08:41
@silviuaavram silviuaavram force-pushed the feat/stepper-accessibility branch from 2efcaec to a84bfad Compare January 27, 2026 17:44
@silviuaavram silviuaavram marked this pull request as ready for review January 27, 2026 17:56
@zannager zannager added the scope: stepper Changes related to the stepper. label Jan 29, 2026
@zannager zannager requested a review from mj12albert January 29, 2026 16:15
Copy link
Member

@ZeeshanTamboli ZeeshanTamboli left a comment

Choose a reason for hiding this comment

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

@silviuaavram Tested it initially with NVDA screen reader. Nice improvements.

  1. With NVDA enabled, the Left/Right arrow keys don't work on the non-linear stepper:
    https://deploy-preview-47687--material-ui.netlify.app/material-ui/react-stepper/#non-linear and nothing is announced as well. When NVDA is off, arrow-key navigation works as expected.
  2. Is it expected that Tab key should move focus to the next step? Since this isn't a tablist, I'm not sure. On the docs site (https://mui.com/material-ui/react-stepper/#non-linear), Tab moves between steps, but in this PR it jumps directly to the "Next" button.
  3. If useStepperContext is intended to be a public API, I think we should document how to use it with a custom stepper demo.

@ZeeshanTamboli ZeeshanTamboli added accessibility a11y type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. labels Jan 31, 2026
@silviuaavram
Copy link
Member Author

silviuaavram commented Feb 2, 2026

Hey @ZeeshanTamboli thanks for your input!

  1. I don't know what to say, especially that I don't have a windows setup at the moment. For example, does this stepper work with NVDA?
  2. I expect Tab to only work on one step at a time. Focusing other steppers with keyboard should work with arrows instead.
  3. useStepperContext should not be public, as StepperContext wasn't public either, right? The hook is meant be used internally, instead of doing useContext(StepperContext).

@ZeeshanTamboli
Copy link
Member

Hey @ZeeshanTamboli thanks for your input!

  1. I don't know what to say, especially that I don't have a windows setup at the moment. For example, does this stepper work with NVDA?

Yes, it works. I am able to navigate with left/right arrow keys with NVDA enabled in this demo but not in this PR's demo.

  1. I expect Tab to only work on one step at a time. Focusing other steppers with keyboard should work with arrows instead.

Ok, so you mean, pressing Tab should focus only on the last focused step in the Stepper and not move to the next step like in production?

  1. useStepperContext should not be public, as StepperContext wasn't public either, right? The hook is meant be used internally, instead of doing useContext(StepperContext).

I read this only export useStepperContext in order to control the usage better. in the PR description, so I thought it is exported.

@silviuaavram silviuaavram force-pushed the feat/stepper-accessibility branch from d598657 to be10951 Compare February 10, 2026 13:38
@silviuaavram silviuaavram marked this pull request as draft February 18, 2026 09:10
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Feb 19, 2026
@silviuaavram silviuaavram force-pushed the feat/stepper-accessibility branch from 1f0ac0d to 353af74 Compare February 19, 2026 14:50
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label Feb 19, 2026
@silviuaavram silviuaavram force-pushed the feat/stepper-accessibility branch 5 times, most recently from 2c39c92 to 179c988 Compare February 26, 2026 09:50
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Feb 26, 2026
@silviuaavram silviuaavram marked this pull request as ready for review February 26, 2026 11:54
@silviuaavram silviuaavram force-pushed the feat/stepper-accessibility branch from c95b272 to 917952a Compare February 26, 2026 11:56
@silviuaavram silviuaavram force-pushed the feat/stepper-accessibility branch from ef56fe4 to 43d0bf6 Compare March 12, 2026 08:02
@silviuaavram silviuaavram merged commit da5b514 into mui:master Mar 12, 2026
22 checks passed
@silviuaavram silviuaavram deleted the feat/stepper-accessibility branch March 12, 2026 08:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

accessibility a11y breaking change Introduces changes that are not backward compatible. scope: stepper Changes related to the stepper. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Stepper] Lacks accessibility Accessibility | Narrator is not announcing the Stepper headings as "selected"

8 participants