A working catalog · 26 patterns · 18 anti-patterns

Agent Accessibility Patterns

Agent interfaces stream, pause, act and ask. Most of that happens in ways a screen reader, a keyboard, a magnifier or a tired brain can't follow. These are the patterns that close the gap.

Teams building agents spend real effort on showing the work: streaming responses, activity trails, approval steps. Almost all of that effort is visual. WCAG 2.2 has no success criterion that mentions streaming or non-deterministic output, so it rarely gets flagged in a normal audit, and it ships inaccessible by default.

This catalog names the patterns that fix that, and the anti-patterns that cause it. Each entry has a use case, what it buys, where it strains if you overdo it, how to build it on the web, iOS and Android, and the WCAG criteria it maps to, so you can test it instead of debating it.

How to use it: take a group into a design review and ask which ones your product already does, which it should, and which would be noise. Or start with the anti-patterns and see how many you've shipped. Some patterns have a live demo in the gallery you can tab through with a screen reader running.

Most of these help more people than they were written for. Announced state changes help anyone who switched tabs during a long run. Untimed approval helps anyone reading carefully before they commit money.

Streaming and status

Agent interfaces move. Text streams in, steps appear, a spinner turns into a result. Almost none of that is perceivable to a screen reader user unless you say it out loud, and some of it is hard to follow for anyone who can't track motion.

Announced State Changes#

Working, finished and failed are announced once each through a status region. The streaming text itself is never a live region.

Use case. An agent takes twenty seconds to pull three accounts and check a limit. A sighted user watches steps appear. A screen reader user hears nothing, then a wall of text, with no idea whether the system worked or stalled.

What it buys. The user knows whether the system is working, done, or stuck without watching it. That includes anyone who tabbed away mid-run.

Where it strains. Put aria-live on the node that streams and you get the Live-Region Firehose: every chunk re-announced, or chunks dropped mid-sentence, depending on the screen reader. Update the visible text silently and announce milestones in a separate polite region.

Build it

Web
A visually hidden role="status" element that exists in the DOM before you write to it (regions injected with content already inside are often missed). ariaNotify() is a cleaner fit where it ships, currently Chromium and Firefox, not Safari.
iOS
AccessibilityNotification.Announcement("Response ready").post(). Set accessibilitySpeechAnnouncementPriority on an AttributedString so announcements queue instead of cutting each other off.
Android
Modifier.semantics { liveRegion = LiveRegionMode.Polite } on a status composable. announceForAccessibility is deprecated as of Android 16.

WCAG 2.2: 4.1.3 Status Messages (AA)
See the demo

Stall Signal#

When a step runs long, the interface says so, so waiting doesn't look like a crash.

Use case. A tool call hangs for forty seconds. The spinner keeps spinning. A sighted user sees the elapsed time on the step, and a screen reader user hears the same "Working" they heard at the start. They can't tell a slow system from a dead one, so they reload the page and lose the run.

What it buys. A reason to wait, or a reason to stop. Both are better than guessing.

Where it strains. Don't announce a heartbeat every few seconds. One message when a step passes its expected time, and another when it recovers or fails, is enough.

Build it

All platforms
Track time since the last milestone. Past a threshold, update the status region once: "Still working. Checking the contribution limit is taking longer than usual." Pair it with the Stop Control.

WCAG 2.2: 4.1.3 Status Messages (AA)
No established name found. Proposed here.
See the demo

Stop Control#

A long generation or run can be stopped from the keyboard and by assistive tech, and stopping keeps what already arrived.

Use case. A response starts streaming nine hundred words the user didn't need. The Stop button only appears on hover over the message, so a keyboard or switch user sits through all of it, with the screen reader reading it as it lands.

What it buys. Control over the pace. It's also how anyone stops an agent that has gone the wrong way before it writes something.

Where it strains. Swapping Send for Stop in the same spot can drop focus when the button re-renders. Keep a stable, named control, and announce "Stopped. Partial response kept."

Build it

Web
A real <button> in the tab order while streaming, plus Escape from the composer.
iOS
.accessibilityAction(.magicTap), the two-finger double tap, is built for exactly this kind of play/pause action. Wire it to stop.
Android
A persistent Stop button, and a custom accessibility action on the streaming message so TalkBack users can stop from where they are.

WCAG 2.2: 2.2.2 Pause, Stop, Hide (A), 2.1.1 Keyboard (A)
See the demo

Reduced-Motion Streaming#

Typing effects and progress animation respect the user's motion preference.

Use case. A user with vestibular sensitivity opens a financial guidance chat. The typing animation and pulsing progress indicator make a five-minute session physically unpleasant, and nothing in the product turns either off.

What it buys. Sessions people can sit through. It also gets the response on screen faster for anyone who reads quicker than the animation runs.

Where it strains. You lose the perceived-speed benefit of streaming for those users. Render in one pass with a static status line, which is usually faster to read anyway.

Build it

Web
matchMedia('(prefers-reduced-motion: reduce)') decides whether to type out or render in one pass.
iOS
@Environment(\.accessibilityReduceMotion).
Android
Treat an animator duration scale of 0 (Settings > Accessibility > Remove animations) as reduced motion.

WCAG 2.2: 2.3.3 Animation from Interactions (AAA)
See the demo

Scroll Stays Put#

The transcript only follows new content if the user is already at the bottom, and a sticky composer never covers what has focus.

Use case. A low-vision user at 300% zoom scrolls up to reread a number. The next chunk arrives and the view jumps to the bottom. They scroll up again. It jumps again.

What it buys. The ability to read while the agent works, which is the whole point of streaming.

Where it strains. Users at the bottom do want auto-follow. Detect the position and offer a "New content below" button when they've scrolled away, instead of choosing for them.

Build it

Web
Only pin to the bottom if scrollHeight - scrollTop - clientHeight is under a small threshold. Give sticky composers scroll-padding-bottom so focused items aren't hidden behind them. A scrollable transcript needs tabindex="0" and a name so keyboard users can scroll it.
iOS / Android
Same rule in ScrollViewReader or LazyListState: auto-scroll only if the last item is visible.

WCAG 2.2: 2.2.2 Pause, Stop, Hide (A), 2.4.11 Focus Not Obscured (Minimum) (AA)
See the demo

Durable Errors#

A failure stays on the page, next to what failed, with a real Retry. It doesn't flash by in a toast.

Use case. A request fails. A red toast says "Something went wrong" for four seconds and vanishes. A screen reader user hears half of it. A magnifier user never sees it. The message they typed is gone too.

What it buys. Something to act on. The user can find the error again, retry, and keep going without retyping.

Where it strains. Errors that stay around pile up. Replace an error when its retry succeeds rather than stacking them.

Build it

All platforms
Render the error inline in the transcript, announce it once (assertive is justified here), keep the user's message in the composer, and put a named Retry button next to it.

WCAG 2.2: 3.3.1 Error Identification (A), 4.1.3 Status Messages (AA)
See the demo

Reading the response

Once the text lands, it has to work as a document: navigable by structure, complete, and readable at any zoom, in any modality.

Speaker Labels#

Every message says who said it in the markup, not only with alignment or bubble color.

Use case. Right-aligned blue bubbles are the user, left-aligned gray ones are the agent. Read linearly by a screen reader, the conversation is one undifferentiated stream of text.

What it buys. A transcript that makes sense read top to bottom, and messages that can be jumped between by heading.

Where it strains. "You said" before every short message gets repetitive. A short heading per message ("You", "Assistant") is enough, and doubles as a navigation landmark.

Build it

Web
Each message is a list item with a short heading. The transcript is an <ol> or role="log".
iOS
Mark the speaker line with .accessibilityAddTraits(.isHeader). Don't wrap a long message in .accessibilityElement(children: .combine). See One Giant Element.
Android
Modifier.semantics { heading() } on the speaker line.

WCAG 2.2: 1.3.1 Info and Relationships (A)
See the demo

Semantic Rendering#

Model output is rendered as real headings, lists, tables and code blocks, not as styled text or raw markdown.

Use case. The model returns a markdown table comparing three funds. The renderer turns it into styled divs. A screen reader user hears thirty cells in a row with no idea which column they're in. Another product skips rendering entirely and reads out "asterisk asterisk".

What it buys. Structure the user can navigate: jump by heading, skip a list, read a table by row and column. The model already wrote the structure, so throwing it away is pure loss.

Where it strains. Generated structure is sometimes wrong: headings that skip levels, tables with no header row. Normalize heading levels to fit under the message heading, and sanitize before insertion.

Build it

Web
Markdown to sanitized HTML, with <th scope> on tables and a language label on code blocks.
iOS / Android
Don't flatten to one Text. Map headings to header traits and lists to separate elements, and give tables a row-by-row reading order.

WCAG 2.2: 1.3.1 Info and Relationships (A), 2.4.6 Headings and Labels (AA)
See the demo

Summary First#

The summary comes before the detail in the DOM, matching the visual order.

Use case. A result screen leads visually with the recommendation and puts the reasoning below it. In the markup, forty lines of trail come first, so the screen reader user hears the process before the point.

What it buys. The point, first, for everyone. It's also the structure that makes a response usable on a small screen and in a notification.

Where it strains. Sometimes it means fighting your own layout. Fix it in the markup. Reordering with CSS order or flex-direction: column-reverse only moves things visually and produces the Visual-Only Reorder, where the reading and focus order no longer match what's on screen.

Build it

All platforms
Source order is reading order. On iOS, .accessibilitySortPriority is a last resort, not a layout tool.

WCAG 2.2: 1.3.2 Meaningful Sequence (A), 2.4.3 Focus Order (A)

Post-Stream Readability#

After streaming stops, the whole response is on the page, complete, and readable from the top by any means.

Use case. Two real failures. One product streams its reasoning, then collapses it into nothing when the answer lands. Another shows the full answer on screen but hands the screen reader a truncated copy: ask for all fifty states and you hear twenty-seven.

What it buys. An answer that survives the moment it was produced. It can be reread, shared and audited, which is the difference between a chat log and a record.

Where it strains. The transcript grows. Collapse rather than delete, and make the collapsed state clearly expandable. Test that what the accessibility tree exposes matches what's on screen, word for word.

Build it

All platforms
Never build a separate, shorter string for the accessibility label of a response. Expose the rendered content itself.

WCAG 2.2: 1.3.1 Info and Relationships (A), 4.1.2 Name, Role, Value (A), 3.2.5 Change on Request (AAA)
The truncation case is documented in Windows Copilot Serves At Best Half an Answer to Screen Reading Users.
See the demo

Reflowable Output#

Tables, code and wide content in a response work at 320 CSS pixels and 400% zoom without scrolling the whole page sideways.

Use case. A low-vision user zooms to 400%. The response has a comparison table and a code snippet, both fixed width. The whole chat now scrolls horizontally, and they read each line by panning left and right.

What it buys. Responses that hold up when magnified, on a phone, or in a narrow side panel, which is where a lot of agents live.

Where it strains. Code and data tables legitimately need two dimensions, and WCAG allows that. Let those scroll inside their own focusable, labeled container rather than the page.

Build it

Web
overflow-x: auto on a wrapper around <pre> and <table>, with tabindex="0" and an accessible name. overflow-wrap: anywhere for long URLs.
iOS / Android
Test at the largest accessibility text sizes. ViewThatFits on iOS and fontScale-aware layouts in Compose let a table become a stacked list.

WCAG 2.2: 1.4.10 Reflow (AA)
See the demo

Described Media#

Charts, images and diagrams the agent generates come with a text alternative that carries the same information.

Use case. The agent answers "How has my balance changed?" with a line chart and nothing else. The one answer to the question is in the pixels.

What it buys. The answer, in every modality. The data behind a chart is usually already in the agent's context, so writing a sentence and a small table costs almost nothing.

Where it strains. Generated alt text can be confidently wrong. Build the description from the same data as the chart, not from a vision model looking at the rendered image.

Build it

All platforms
Lead with a one-sentence takeaway ("Up 12% since January, with a dip in April"), then offer the data as a real table.

WCAG 2.2: 1.1.1 Non-text Content (A)

Spoken Uncertainty#

Confidence, missing data and caveats are stated in the text of the answer, not only in a badge, icon or color.

Use case. A small amber dot next to an answer means "low confidence". Screen reader users never hear it, and for a blind user who can't cross-check the source, confidence is the thing they need most.

What it buys. Calibrated trust for the users with the fewest ways to verify. It also survives copy and paste, which a badge doesn't.

Where it strains. Hedging every sentence makes the answer useless. Say it where it would change what the user does.

Build it

All platforms
Put the caveat in the response body ("I couldn't reach one of your four accounts, so this total may be low"). If you also show a badge, give it a text label.

WCAG 2.2: 1.4.1 Use of Color (A), 1.3.1 Info and Relationships (A)
Silent failures and overconfident answers are two of the four failure modes in Assistive Agents Need Accessibility Alignment (ICML 2026).

Verbosity Control#

Users can set response length and reading level once and have it stick.

Use case. A user with dyslexia turned to an assistant to reduce reading load. Every answer is five paragraphs. They type "shorter please" at the start of every conversation.

What it buys. Less effort for the people most likely to be using an agent to save effort. It helps screen reader users too, since every extra word is listened to in order.

Where it strains. A short answer can drop the caveat that mattered. Keep the caveats and cut the preamble.

Build it

All platforms
A persistent Brief / Standard / Detailed setting, and a plain-language option, applied through the system prompt or output schema rather than re-requested each time.

WCAG 2.2: 3.1.5 Reading Level (AAA)
See W3C's Cognitive Accessibility research module on voice and conversational interfaces.

Operating it

Keyboard, switch, voice control and screen reader users operate the interface through focus and structure, so both have to hold still while the agent works.

Focus Stays Put#

Focus moves only when the user moves it. New messages don't pull focus away from the composer or from whatever the user is reading.

Use case. Every time the agent replies, focus jumps to the new message. A screen reader user halfway through composing a follow-up is yanked out of the text field, and a keyboard user loses their place in a long answer.

What it buys. A predictable interface. The user decides when to go and read the answer. The announcement tells them it's there.

Where it strains. Some users do want to jump straight to the answer. Give them a one-key way to do it (a "Go to response" button, or a heading to navigate to) rather than making that choice for everyone.

Build it

Web
After Send, keep focus in the composer. Offer a control that moves focus to the latest response's heading on request.
iOS
Use @AccessibilityFocusState only in response to a user action, never on content arrival.
Android
Avoid FocusRequester.requestFocus() on new messages. Let the live region do the telling.

WCAG 2.2: 2.4.3 Focus Order (A), 3.2.2 On Input (A)
See the demo

Keyboard-Reachable Steps#

Every step in the activity trail can be focused and operated from the keyboard.

Use case. The trail shows six tool calls, each expandable for detail, built as clickable divs. A keyboard user can see that detail exists and can't reach it.

What it buys. The detail is actually there for everyone, including power users who never touch the mouse.

Where it strains. More tab stops in a long trail. Group steps under a single disclosure and let the user drill in, rather than putting every step in the tab order.

Build it

Web
<details>/<summary> gets keyboard, state and semantics for free.
iOS / Android
Expandable rows need an expanded/collapsed state exposed to VoiceOver and TalkBack, not only a chevron.

WCAG 2.2: 2.1.1 Keyboard (A), 2.4.3 Focus Order (A)
See the demo

Semantic Activity Trail#

The trail is marked up as an ordered list of actions, not a stack of styled divs.

Use case. A screen reader user wants to skip six steps and get to the answer. With real structure they can jump by heading or list. Without it, they arrow through every line.

What it buys. Skimmability, and a trail that can be exported, tested, or handed to a reviewer.

Where it strains. Slightly less layout freedom. <details> inside an ordered list gets you most of the way with no JavaScript.

WCAG 2.2: 1.3.1 Info and Relationships (A)
See the demo

Actions on Focus#

Copy, retry, feedback and other message actions show up on keyboard focus, not only on hover, and are available as accessibility actions.

Use case. Copy and Regenerate fade in when the mouse is over a message. Keyboard users never see them. Screen reader users on mobile swipe through four icon buttons after every single message.

What it buys. The same features for everyone, and a less cluttered swipe order on mobile.

Where it strains. Always-visible actions add visual noise. Showing them on hover and focus-within is the usual compromise.

Build it

Web
:hover, :focus-within to reveal. Every icon button gets an accessible name. Announce "Copied" through the status region.
iOS
.accessibilityAction(named: "Copy") on the message puts actions in the rotor instead of in the swipe order.
Android
customActions in semantics does the same for TalkBack.

WCAG 2.2: 1.4.13 Content on Hover or Focus (AA), 2.1.1 Keyboard (A), 4.1.2 Name, Role, Value (A)
See the demo

Forgiving Composer#

The input works with keyboards, IMEs, dictation and voice control, and never throws away what the user typed.

Use case. Three failures in one text box. Enter sends the message while a Japanese IME is still composing a word. There's no Send button, so voice control users can't say "click Send". And when the request fails, the text they dictated is gone.

What it buys. Input that works for people who type slowly, type in other scripts, or don't type at all.

Where it strains. Enter-to-send is a strong convention. Keep it, but don't make it the only way.

Build it

Web
A labeled <textarea>, a visible Send button whose name matches its label, Shift+Enter for new lines, and no send while event.isComposing. Keep the draft until the message is accepted.
iOS / Android
Don't time out dictation or voice input. Keep the draft through errors and backgrounding.

WCAG 2.2: 2.1.1 Keyboard (A), 2.5.3 Label in Name (A), 3.3.7 Redundant Entry (A)
See the demo

Decisions and handoffs

Agents pause to ask for approval, stop at guardrails, need credentials, and sometimes need a person. Each of those moments is a decision point, and it has to be perceivable and operable for everyone.

Reachable Approval#

When the agent stops to ask, the user hears about it, can get there in one step, and the button says what it will do.

Use case. The agent pauses for approval in a card halfway up the transcript. Nothing is announced. The button says "Confirm". A screen reader user thinks the agent is still working and waits.

What it buys. An agent that doesn't stall forever waiting on a question nobody heard, and a user who knows exactly what they're approving.

Where it strains. Moving focus to the approval breaks Focus Stays Put. Announce it and offer a jump link instead. Moving focus is only right if the user just triggered the run and is waiting on it.

Build it

All platforms
Announce "Waiting for your approval: transfer $3,400". Label buttons with the action and amount ("Transfer $3,400", "Don't transfer"), not "Confirm"/"Cancel".

WCAG 2.2: 4.1.3 Status Messages (AA), 2.4.6 Headings and Labels (AA), 2.5.3 Label in Name (A)
See the demo

Untimed Approval#

No approval or confirmation expires on a timer.

Use case. An agent asks the user to confirm a transfer with a countdown before it proceeds. A user who needs longer to read, or who switched to a magnifier to check the amount, is forced into a decision they haven't finished making.

What it buys. Decisions made on purpose. Anyone checking an amount carefully gets time to check it, which is the point of asking.

Where it strains. Sessions stay open longer and pending actions need somewhere to live. If a limit is genuinely unavoidable, like an expiring quote, say so plainly and let the user ask for more time.

WCAG 2.2: 2.2.1 Timing Adjustable (A)
See the demo

Reversible by Default#

Actions that spend, send or change data can be undone, or are confirmed with a chance to review first.

Use case. A voice agent misheard "fifteen" as "fifty" and submitted. A user with a motor disability double-activated the Approve button. Neither has a way back.

What it buys. Room for the input errors that assistive tech, speech recognition and tremor all make more likely. WCAG requires one of reversible, checked or confirmed for financial and legal submissions.

Where it strains. Not everything can be undone. Sent email, executed trades. For those, the review step is the undo, so make it a real review that shows the details.

Build it

All platforms
An Undo control that isn't on a timer, announced when the action completes. Debounce approval buttons.

WCAG 2.2: 3.3.4 Error Prevention (Legal, Financial, Data) (AA)
See the demo

Status Beyond Color#

Success, failure, blocked and pending carry a text label, never color alone.

Use case. A step turns red when a guardrail blocks it. To a user with color vision deficiency it looks like every other step. To a screen reader user, the color doesn't exist.

What it buys. Status that survives a screenshot, a grayscale print, a dark theme, Windows contrast themes and a glance. Nobody has to learn your color code.

Where it strains. A busier trail. Keep labels to one word and use color as reinforcement, not the signal. Check forced-colors mode: colored borders disappear there, so the label has to carry it.

WCAG 2.2: 1.4.1 Use of Color (A), 1.4.11 Non-text Contrast (AA)
See the demo

Accessible Mid-Run Auth#

When an agent needs a code, password or re-login partway through a run, the step works with password managers, paste and assistive tech.

Use case. Halfway through a task the agent embeds a bank's verification step: a six-box code input that blocks paste, then a CAPTCHA inside an iframe the screen reader can't enter. The run dies there.

What it buys. Runs that finish. Agents hit auth walls all the time, and each one is a place where users of assistive tech are dropped.

Where it strains. You don't always control the third-party page. When you don't, say plainly that the next step is outside the agent, and hand off to a flow you know works.

Build it

Web
One-time code fields accept paste and use autocomplete="one-time-code". No cognitive-function tests without an alternative.
iOS / Android
.textContentType(.oneTimeCode) on iOS and the autofill hints in Compose, so codes fill themselves.

WCAG 2.2: 3.3.8 Accessible Authentication (Minimum) (AA), 2.1.2 No Keyboard Trap (A)

Consistent Human Handoff#

A way to reach a person is in the same place in every conversation, and asking for one works.

Use case. The agent can't understand a user with atypical speech. The only route to a human is to fail enough times that the system gives up, and the "Contact us" link moves between screens.

What it buys. A way out for anyone the model doesn't serve well. Speech recognition, reading level and unusual requests all fail some users more than others.

Where it strains. Human support costs money, so teams hide it. A handoff that's hard to find still costs you, just later and angrier.

WCAG 2.2: 3.2.6 Consistent Help (A)
W3C's COGA voice module recommends simple error recovery routes to a human.

Accessible by Contract#

When the agent generates UI, it picks from components that already carry roles, names and states. It doesn't write markup or ARIA itself.

Use case. A generative UI feature lets the model emit HTML for an inline form. Some runs produce labeled inputs. Others produce placeholder-only fields and a div with an onclick. Accessibility now varies per response.

What it buys. Accessibility that doesn't depend on the model getting it right every time. The model chooses a component. The component guarantees the semantics.

Where it strains. A smaller vocabulary for the model. That's the point. Validate the output against the catalog and refuse to render anything that doesn't conform, rather than drawing a partial UI.

Build it

All platforms
Define the catalog once with accessibility built in (heading level, button name, input label, group role), and test the rendered accessibility tree, not just the visuals.

WCAG 2.2: 4.1.2 Name, Role, Value (A), 1.3.1 Info and Relationships (A)
No established name found. Proposed here. Protocols like A2UI make this possible by having agents send component references instead of markup.

Anti-patterns

What ships when nobody checks. Each one points to the pattern that fixes it.

  • Live-Region Firehose

    aria-live on the element that streams.

    Why it fails Depending on the screen reader, every chunk is re-announced from the top, or chunks are dropped mid-sentence.

    Instead Announced State Changes

  • Silent Spinner

    A visual spinner or shimmer is the only sign the agent is working.

    Why it fails Screen reader users can't tell working from finished from crashed.

    Instead Announced State Changes

  • Focus Hijack

    Focus jumps to each new message as it arrives.

    Why it fails Pulls users out of the composer or out of the text they were reading.

    Instead Focus Stays Put

  • Scroll Yank

    The transcript snaps to the bottom on every chunk.

    Why it fails Nobody can read earlier content while the agent is responding, least of all at high zoom.

    Instead Scroll Stays Put

  • Hover-Only Actions

    Copy, retry and feedback appear only on mouse hover.

    Why it fails Keyboard, switch and touch screen reader users can't reach them.

    Instead Actions on Focus

  • Markdown Soup

    Model output shown as raw markdown, or styled to look like structure without the semantics.

    Why it fails "Asterisk asterisk" read aloud. Tables heard as a stream of cells.

    Instead Semantic Rendering

  • Clipped Accessible Text

    The accessibility layer gets a shorter or different string than the screen shows.

    Why it fails Screen reader users get part of the answer and have no way of knowing it's partial.

    Instead Post-Stream Readability

  • One Giant Element

    On iOS or Android, a whole long message is merged into one accessibility element.

    Why it fails VoiceOver or TalkBack reads 400 words in one go, with no way to move by paragraph, reread a number or stop partway.

    Instead Speaker Labels

  • Collapse and Vanish

    Reasoning or steps disappear once the answer arrives.

    Why it fails Anyone who wasn't watching live loses the explanation.

    Instead Post-Stream Readability

  • Toast-Only Errors

    Failures appear in a toast that disappears after a few seconds.

    Why it fails Missed by magnifier users, cut off for screen reader users, and gone before anyone can act.

    Instead Durable Errors

  • Retype on Retry

    A failed send clears the composer.

    Why it fails Users who type slowly, dictate or use switch access lose the most work.

    Instead Forgiving Composer

  • Enter Sends Mid-IME

    Enter sends even while an input method is composing characters.

    Why it fails Chinese, Japanese and Korean users send half-typed words.

    Instead Forgiving Composer

  • Visual-Only Reorder

    CSS order or reversed flex used to put the summary first on screen.

    Why it fails Reading and tab order still follow the markup, so what's heard and what's seen no longer match.

    Instead Summary First

  • Countdown Confirm

    "Proceeding in 10 seconds" unless the user intervenes.

    Why it fails Anyone who reads, zooms or navigates slowly is out of time before they've read what they're agreeing to.

    Instead Untimed Approval

  • Color-Coded Trail

    Step status shown with a colored dot or border only.

    Why it fails Invisible to screen readers, colorblind users and Windows contrast themes.

    Instead Status Beyond Color

  • Chart Without Words

    A generated chart or image is the whole answer.

    Why it fails The answer lives only in the pixels.

    Instead Described Media

  • Badge-Only Confidence

    Uncertainty shown as an icon or color next to the answer.

    Why it fails The users least able to verify the answer are the ones who can't perceive the warning.

    Instead Spoken Uncertainty

  • Confirm / Cancel

    Approval buttons that don't say what they do.

    Why it fails Out of context (voice control, a screen reader's list of buttons) "Confirm" could mean anything.

    Instead Reachable Approval

Test it in fifteen minutes

Automated checkers catch static problems. Almost everything above is about what happens over time, so it needs a person running a real session.

  1. Unplug the mouse. Send a message, stop it partway, copy the answer, expand a step and approve an action using only the keyboard. Watch where focus goes after each response.
  2. Turn on a screen reader and look away. VoiceOver (Cmd+F5 on Mac), NVDA on Windows, TalkBack on Android. Run a long task. Can you tell when it starts, stalls, finishes and fails without looking?
  3. Read the answer back. Navigate the finished response by heading and list. Is it all there? Does it match the screen word for word?
  4. Zoom to 400% or set the width to 320px. Ask for a table and a code sample. Does the page scroll sideways?
  5. Turn on reduced motion and a contrast theme. Does streaming stop animating? Does step status still read with the colors gone?
  6. Dictate a message with voice control or system dictation, then make the request fail. Is your text still there?

Sources and further reading

Found a pattern that belongs here, or one that's wrong? Open an issue and I'll add it with credit.