What a screen reader hears.
Open most mention libraries in DevTools and you'll see the role mutate — textarea becomes combobox only when the popover opens, then back. This lib doesn't do that. The textarea keeps role="combobox" the entire time; what changes is aria-expanded and aria-activedescendant. The substring after @ is the active query; the textarea is always the combobox.
Below, every ARIA attribute the contract touches is exposed live as you type.
<textarea
role=""
aria-controls=""
aria-expanded=""
aria-activedescendant=""
/>
<!-- listbox not mounted (popover closed) -->
What the contract refuses to do.
- No role mutation.
- Changing
roleat runtime triggers re-announcement in NVDA and JAWS. The user starts typing in a "text area" and suddenly hears "combobox" — the field they thought they were in just changed identity. The lib's textarea is a combobox from first paint to unmount. - No second tab stop.
- A popover with its own focus management means users can'tTabback to their text without losing the popover. Worse, if focus moves to the listbox, the textarea's caret position vanishes — fatal for chat composers where the user needs their cursor exactly where they left it.
- No focus shift.
- Focus stays on the textarea the whole time; the listbox is a virtual focus target via
aria-activedescendant. The user can keep typing after committing a selection, and arrow keys move the highlight without ever leaving the input. Standard APG combobox behavior, applied to a substring rather than a full input value.
Base UI's Combobox primitive was evaluated and rejected on contract-mismatch grounds — the headline reason was role mutation at runtime.