mui / mui/base-ui

Toast.Root sets aria-hidden on a focusable element when priority is "high"

Open Beginner friendly
#5,659 0 comments 0 reactions 0 assignees View on GitHub
component: toast status: waiting for maintainer
Dominant language
TypeScript
Stars
10.9k
Forks
543
Avg merge
1d 20h
Merged PRs (30d)
101

Description

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 `role` to `alertdialog` when the toast is high priority, and `dialog` otherwise
- line 424 sets `tabIndex: 0` unconditionally
- line 428 sets `aria-hidden` to `true` when 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.

1. Render `Toast.Provider`, `Toast.Portal` and `Toast.Viewport` in the standard arrangement, mapping toasts to `Toast.Root`.
2. Add a toast with `priority: "high"`.
3. Do not focus the viewport.
4. Inspect the rendered `Toast.Root`. It carries `role="alertdialog"`, `tabIndex="0"` and `aria-hidden="true"` together.
5. 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.

Contributor guide

Open the contributing guide

Research direction

Start in toast/root/ToastRoot.js around lines 423-428 and reproduce the unfocused high-priority toast state described in the issue. Run axe against the standard Toast.Provider, Toast.Portal, and Toast.Viewport arrangement. Done when the high-priority Toast.Root no longer combines keyboard focusability with aria-hidden and the aria-hidden-focus violation is gone.

Written by the indexing model from the issue text.

Assessment

Tech stack
next.js, react, typescript
Domain
accessibility, frontend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
82/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.