Is this a regression?
The previous version in which this bug was not present was
No response
Description
When a mat-chip-listbox with mat-chip-option children is navigated with NVDA in Chrome or Edge, NVDA never enters "focus mode" for the listbox, so arrow keys do nothing: Up/Down read the surrounding page text and Left/Right spell out the focused chip's label character by character. Focus never actually moves between chip options via the keyboard — only Tab (which exits the listbox) or a mouse click works. This reproduces on the official chips demo at https://material.angular.dev/components/chips/examples#chips-stacked. Firefox + NVDA is unaffected — arrow key navigation works there.
Confirmed reproducible across every Angular Material version from v15 through v22.
Root cause
MatChipOption's compiled template renders:
<mat-chip-option role="presentation"> <!-- host: MatChipOption.ngOnInit() forces role="presentation" -->
<span class="mdc-evolution-chip__cell mdc-evolution-chip__cell--primary"> <!-- no role at all -->
<button matChipAction role="option">…</button>
</span>
</mat-chip-option>
Only one of those two wrapper layers is actually a problem: the outer role="presentation" host is exactly what the ARIA spec expects to be transparent when resolving an element's required context role (here, option's required listbox ancestor), and on its own it doesn't break anything. The .mdc-evolution-chip__cell span is the one non-transparent layer — it has no role at all, and Chromium fails to see through it, so the option no longer reads as a descendant of the listbox.
Isolated via a minimal structural repro:
role="option" directly under role="listbox" — works.
role="option" under a single role="presentation" wrapper — still works.
role="option" under a plain, roleless <span> (itself inside the role="presentation" host) — breaks. This is exactly MatChipOption's real shape.
- Giving that same intermediate
<span> an explicit role="presentation" too (so the chain becomes presentation → presentation → option instead of presentation → generic → option) restores focus mode.
.mdc-evolution-chip__cell carries no semantic meaning — it's a layout-only wrapper — so marking it role="presentation" in the MatChip/MatChipOption/MatChipRow templates should fix this.
Reproduction
- Open the chips listbox demo (or any
mat-chip-listbox with several mat-chip-option children).
- Start NVDA on Windows, using Chrome or Edge.
- Tab focus into the chip listbox.
- Press the Left/Right (or Up/Down) arrow keys.
Expected Behavior
NVDA enters focus mode and forwards arrow keys to the listbox, moving real focus between chip options — same as Firefox + NVDA today.
Actual Behavior
NVDA stays in browse mode; arrow keys are consumed by NVDA instead of reaching the listbox, and focus never moves between chips via keyboard.
Environment
- Angular Material: reproducible on v15 through v22 (tested)
- Browsers affected: Chrome, Edge (Chromium-based)
- Browser unaffected: Firefox
- Screen reader: NVDA
- OS: Windows
Is this a regression?
The previous version in which this bug was not present was
No response
Description
When a
mat-chip-listboxwithmat-chip-optionchildren is navigated with NVDA in Chrome or Edge, NVDA never enters "focus mode" for the listbox, so arrow keys do nothing: Up/Down read the surrounding page text and Left/Right spell out the focused chip's label character by character. Focus never actually moves between chip options via the keyboard — only Tab (which exits the listbox) or a mouse click works. This reproduces on the official chips demo at https://material.angular.dev/components/chips/examples#chips-stacked. Firefox + NVDA is unaffected — arrow key navigation works there.Confirmed reproducible across every Angular Material version from v15 through v22.
Root cause
MatChipOption's compiled template renders:Only one of those two wrapper layers is actually a problem: the outer
role="presentation"host is exactly what the ARIA spec expects to be transparent when resolving an element's required context role (here,option's requiredlistboxancestor), and on its own it doesn't break anything. The.mdc-evolution-chip__cellspan is the one non-transparent layer — it has no role at all, and Chromium fails to see through it, so theoptionno longer reads as a descendant of thelistbox.Isolated via a minimal structural repro:
role="option"directly underrole="listbox"— works.role="option"under a singlerole="presentation"wrapper — still works.role="option"under a plain, roleless<span>(itself inside therole="presentation"host) — breaks. This is exactlyMatChipOption's real shape.<span>an explicitrole="presentation"too (so the chain becomespresentation → presentation → optioninstead ofpresentation → generic → option) restores focus mode..mdc-evolution-chip__cellcarries no semantic meaning — it's a layout-only wrapper — so marking itrole="presentation"in theMatChip/MatChipOption/MatChipRowtemplates should fix this.Reproduction
mat-chip-listboxwith severalmat-chip-optionchildren).Expected Behavior
NVDA enters focus mode and forwards arrow keys to the listbox, moving real focus between chip options — same as Firefox + NVDA today.
Actual Behavior
NVDA stays in browse mode; arrow keys are consumed by NVDA instead of reaching the listbox, and focus never moves between chips via keyboard.
Environment