Accessibility
ChatInput, ChatInputLayout, hasSendableText from @prepared911/ui/chat-input.
The typed area is a textbox with aria-multiline. ChatInput renders no label; the screen names it with aria-label or aria-labelledby. The placeholder is exposed as aria-placeholder, a hint and not a name. The add, Send and Stop controls are icon-only Buttons named by addLabel, sendLabel and stopLabel, each also shown as a tooltip. The context chips and the suggestions are lists named by chipsLabel and suggestionsLabel.
enterKeyHint="send" labels the Enter key on mobile keyboards.removedAnnouncement politely; a removal by cut or select-all announces nothing, since the person made that edit on purpose. Context chips are in a named list above the text.
A disabled Send cannot be focused, so sendDisabledAnnouncement is announced politely when the person empties the text and when they press Enter in an empty composer. While a response generates and there is no Stop, Send shows a spinner, sets aria-busy, stays focusable, and announces its pending name. It is not disabled.
aria-disabled, and disables every control (ADR 0015), including Stop and Send while a response generates: a disabled composer shows Send disabled, not pending.tabindex="0", aria-readonly) and never sends. While a response generates, Stop stays usable (stopping is not editing the text) and a pending Send stays focusable and ignores presses.aria-invalid; the surface edge changes weight and, under forced colors, color. The stacked layout has no border, so it draws the same strong edge on the rule under the typed row. The error text belongs to Field or the screen, wired with aria-describedby.forced-colors: active the surface border stays, the focused surface takes Highlight, and the invalid state adds a strong Mark border.
ChatInput adds none. The controls keep their own: Send swaps its arrow and spinner at once under reduced motion, and the menu and tooltips follow the preference. The height follows the text with no transition.