typeahead: role="option" items are missing aria-selected, so the active option's selection state isn't conveyed

Author: manichandraCreated Jul 10, 2026Updated Jul 10, 2026

In the typeahead dropdown, the result items are rendered as role="option" inside a role="listbox" host, and the currently-highlighted item is tracked with activeIdx / [class.active]. However, the options never set aria-selected. As a result, when the active option changes (arrow keys), assistive technology has no programmatic selection state to announce — the highlight is conveyed visually (.active) but not in the accessibility tree.

Where src/typeahead/typeahead-window.ts — the option button in the template:

@for (result of results; track $index) {
  <button
    type="button"
    class="dropdown-item"
    role="option"
    [id]="id + '-' + $index"
    [class.active]="$index === activeIdx"   // visual-only selection state
    (mouseenter)="markActive($index)"
    (click)="select(result)"
  >

The host is role="listbox", and the combobox input references the active option via aria-activedescendant. Per the WAI-ARIA Authoring Practices Combobox (with listbox popup) pattern and WAI-ARIA 1.2 aria-selected, an option that is the active descendant in a single-select listbox should expose aria-selected="true"; the others should be false (or, for a single-select listbox, at minimum the active one should be true). WCAG 2.1 SC 4.1.2 (Name, Role, Value) covers programmatic state.

Suggested fix Bind aria-selected to the same condition that drives .active:

[class.active]="$index === activeIdx"
[attr.aria-selected]="$index === activeIdx"

Notes Found with aria-reach (rule option-missing-aria-selected), an open-source ARIA anti-pattern scanner; this is a heuristic static finding I verified by hand against current master. Happy to be corrected if the intent here is to rely solely on aria-activedescendant + visual styling. I'm glad to open a PR with the one-line change + a spec assertion if you'd accept it.

Source: ng-bootstrap/ng-bootstrap