Toast.Root sets aria-hidden on a focusable element when priority is "high"
Base UI version: 1.8.0 React 19, Next.js 16.3.4
A toast raised with priority: "high" renders as a focusable element that is hidden from the accessibility tree. axe reports it as aria-hidden-focus at serious severity. A keyboard user can Tab onto the toast, and a screen reader will not describe what they have landed on.
The two attributes come from Toast.Root itself, in toast/root/ToastRoot.js:
- line 423 sets
roletoalertdialogwhen the toast is high priority, anddialogotherwise - line 424 sets
tabIndex: 0unconditionally - line 428 sets
aria-hiddentotruewhen the toast is high priority and the viewport is not focused
So the combination only appears for high priority toasts, and only while the viewport does not have focus.
Reproduction
The unfocused condition matters, so it is worth stating up front. If you focus the viewport, aria-hidden is removed and the violation goes away. The state that needs testing is the ordinary one, where a toast has appeared and nobody has tabbed to it yet.
- Render
Toast.Provider,Toast.PortalandToast.Viewportin the standard arrangement, mapping toasts toToast.Root. - Add a toast with
priority: "high". - Do not focus the viewport.
- Inspect the rendered
Toast.Root. It carriesrole="alertdialog",tabIndex="0"andaria-hidden="true"together. - Run axe against the page. It reports
aria-hidden-focus.
What we expected
An element that is reachable by keyboard should not be hidden from assistive technology. Either the element is exposed while it can be focused, or it is not focusable while it is hidden.
Notes that may save you time
The message itself is still announced. Base UI renders sibling live regions that carry the toast text, so this is a stray tab stop rather than a silent error. That caps how bad it is, and it is why we are reporting it rather than pinning an older version.
It is not a transient state during the entering animation. The attributes are still present after a 1.5 second settle.
Passing tabIndex={-1} to Toast.Root does not help, because Toast.Root sets the attribute itself. That is the documented workaround for the same defect in Radix (radix-ui/primitives#2584), and it does not apply here.
One consequence worth flagging for other users
Because the root is aria-hidden while unfocused, getByRole in Playwright and Testing Library cannot see a high priority toast at all. Both resolve against the accessibility tree. Any test written as getByRole("alertdialog") will fail for a reason that has nothing to do with the behaviour under test. Addressing the toast by CSS or by text works.
We lost time to this before understanding the cause, so it may be worth a line in the docs whatever happens to the underlying issue.
Related
This looks like the same shape as #5528 (combobox) and #4678 (dialog), both open and both aria-hidden applied to content that stays in the tab order. Filing separately because the source here is Toast.Root's own props rather than an outside-content or background treatment, but they may share a fix.
Source: mui/base-ui