Your chat UI streams beautifully. A screen reader hears nothing.
Marcus Osei
A sighted user types a prompt and watches the reply roll in, word after word. A screen reader user types the same prompt and gets silence. Nothing told their software that anything arrived. Eric Bailey's new piece in Smashing Magazine starts from that gap, and his framing is one I agree with completely: "There is nothing special about chat-based UIs when viewed via the lens of the Web Content Accessibility Guidelines."
So this is not a new discipline. It is the same toolkit (live regions, headings, landmarks, focus management) applied to an interface that has spread fast and mostly been designed for the eye. Here is how each piece fits together.
One live region, announced once
The first mistake is the obvious one: putting aria-live on every message. Bailey's recommendation is a single dedicated live region placed at the end of the DOM, set to aria-live="assertive", which your code fills when there is something to say. He points to TetraLogical's write-up on the technique for the implementation details.
Timing matters as much as placement. Announcing the reply while it streams "may create a juddering and confusing experience that's difficult for someone to parse," he writes. Wait until streaming is done, then announce the whole message.
The wait before a reply needs handling too. If the screen shows text such as "Thinking", announce that exact text. If it shows only an animation, Bailey suggests announcing "Loading" five seconds after the prompt is sent and repeating every five seconds, varying the wording ("Researching", "Planning") so the person knows work is still happening. He says the five-second figure comes from the inclusive user research he evaluated.
What about ariaNotify()? Bailey notes that Baseline lists the method as having cross-browser availability, and he calls it a much-needed addition. He still advises holding off for now. People who rely on assistive technology tend to be slow to upgrade, for good reason: breaking the tool you use to operate your computer is a serious risk. He also has doubts about how mature the polyfills are.
A heading for every turn
Headings are the most popular way screen reader users move through a page, according to Bailey, so he gives each message one. It is visually hidden, and it names the speaker:
<article>
<hX
class="sr-only"
aria-label="You said/{agent name} said"
tabindex="-1">
You said:/{agent name} said:
</hX>
<div>
{message body}
</div>
</article>
hX stands for whatever heading level fits your document. The prefix does in text what left and right alignment does on screen: it tells you who is talking. Bailey adds the aria-label as a fallback for screen reader bugs, and tabindex="-1" lets script move focus to the heading.
Landmarks: a short list, used with restraint
Bailey's set is small. nav holds the session list, main holds the conversation, form wraps the prompt input, and aside holds any sidebar of generated content.
The anti-pattern is wrapping each message in a section or another landmark. In his words, they "pollute" the list of landmarks a screen reader can generate, and in a long session that list becomes something you have to wade through. Use headings to move between messages and save landmarks for the major regions.
The same reasoning rules out the ARIA feed pattern. Bailey's warning is that focus-management corner cases and the expectations of keyboard-only users "work against you" there. article plus a heading is simpler, and there is no need to make it harder than that.
Getting around without a mouse
Put skip links before anything visible: one to the conversation, and think about others for the prompt input and the settings area. Make the conversation list a composite widget, which means one tab stop with arrow keys and Home/End to move through the items, so keyboard and motor-impaired users are not tabbing through dozens of entries.
Then there is F6. Bailey describes F6/Shift+F6 as an older technique some Windows screen reader users rely on to hop between areas of desktop apps. For a web app he suggests Control+F6 and Control+Shift+F6; for a native app built with web technologies, plain F6 and Shift+F6. The mechanics:
- Give each landmark an
idandtabindex="-1", plus an accessible name viaaria-labeloraria-labelledbyfor every landmark other thanmain. - Add an event listener that cycles focus through them.
- When the prompt region is the target, focus the input itself, not its parent
form. - When the conversation is the target, focus the hidden heading of the latest message. Bailey marks it with a
data-last-messageattribute.
Let people tune it
His preferences list is where the piece moves past compliance. Let people switch off announcements for interim steps such as tool calls and code searches while keeping that content on the page. Offer a toggle that cuts animation time to zero and defaults to off when prefers-reduced-motion is detected. Let people mute sound effects, and make keyboard shortcuts re-bindable. He also asks you to test at larger browser font sizes, because many of these interfaces ship small text.
For tables and code blocks, he suggests the live region announce a summary rather than the content: a table's subject with its row and column counts, or a code block's filename and line count. The full markup stays in the transcript for anyone who wants to read it properly.
My verdict
None of this is exotic, and that is the strength of Bailey's guide. The chat transcript is a document, and the elements built for documents already handle it. Before your next chat-UI PR merges, check that:
- one live region announces each finished reply, never the stream
- every message has a hidden heading that names the speaker
- landmarks mark the major regions only, never individual messages
- a keyboard user can reach the prompt, the transcript and the session list without tabbing through everything
His last recommendation is the one I would put first: ask disabled people what they think, and treat them as the experts on their own experience.
Source: Smashing Magazine (smashingmagazine.com)