Accessible AI UI

By the Syncfusion DevTutorials team · Last updated

Make streaming answers work with screen readers, keyboard-only use and reduced motion, and give citation chips labels that make sense out loud.

What Accessible AI UI Means

Accessible = usable by people who use a screen reader, a keyboard only, larger text, or less motion, as well as by everyone else.

In this lesson, you will make the order panel work for a screen-reader user, a keyboard-only user and someone who needs reduced motion, without changing how it looks for anyone else.

AI interfaces bring new accessibility problems. Text appears word by word. Buttons like Stop exist only for a few seconds. Citation chips are tiny numbers.

A screen reader cannot follow any of that unless you tell it how.

Press Play, or use Next to go one step at a time.

  • Frame 1: no live region. The answer appears on screen, and the screen reader says nothing.
  • Frame 2: every token is announced. The user hears a stutter of single words.
  • Frame 3: one announcement per sentence. The user can follow along.

Simple lesson: a streamed answer must be streamed to the ear too, at a pace people can follow.

A Real-World Example

Think about announcements at a train station.

  • No announcements: people who can't see the board miss their train.
  • An announcement for every train, every second: nobody can hear anything.
  • One clear announcement per train, when it matters: everyone gets the news.

A live region works the same way:

  • No announcements = no live region.
  • Every second = announcing every token.
  • One per train = announcing once per sentence.

Click the button. The Build tab plays the streamed answer through the naive interface.

  • The screen: the answer streams in as usual.
  • Screen reader hears: nothing.
  • Then: switch to My interface and try "Announce every token" to hear the stutter.

Simple lesson: what a screen reader says is part of your interface.

What You Will Learn

In this lesson, you will learn:

  1. How live regions announce streamed text.
  2. Why per-token announcements fail.
  3. How to make short-lived controls reachable by keyboard.
  4. How to respect reduced motion.
  5. How to label citation chips.

About accessibility checks in this lab

  • The lab models what a screen reader announces, which keys work, and what moves on screen.
  • Real apps should also run axe-core, an automated checker, on every page. It finds missing labels, contrast problems and more.
  • Nothing replaces testing with a real screen reader, such as NVDA, JAWS or VoiceOver.

Live Regions for Streamed Text

Live region = a part of the page a screen reader watches and reads out when it changes, marked with aria-live.

Use aria-live="polite", so announcements wait until the user's screen reader is free.

Don't Announce Every Token

A model writes 10 or more tokens a second. If the live region updates on each one, the screen reader either stutters through single words or gives up.

Collect the text, and update the live region once per sentence:

function useSentenceAnnouncer(text: string, done: boolean) {
  const [said, setSaid] = useState("");
  const spoken = useRef(0);
  useEffect(() => {
    const unsaid = text.slice(spoken.current);
    const end = unsaid.lastIndexOf(". ");
    if (end >= 0 || done) { const cut = done ? unsaid.length : end + 1; setSaid(unsaid.slice(0, cut)); spoken.current += cut; }
  }, [text, done]);
  return said;
}

<p aria-live="polite" className="sr-only">{said}</p>

The visible answer still streams word by word. Only the announcement waits for each sentence.

Simple lesson: a polite live region, updated per sentence.

Keyboard, Motion and Labels

Step through all three frames.

  • Frame 1: a keyboard user presses Escape to stop and Ctrl+Z to undo. With mouse-only controls, nothing happens.
  • Frame 2: the user's system asks for less motion. The blinking cursor and shimmer stop; the text still streams.
  • Frame 3: a screen reader reads the source chips as "link, 1". With labels, it reads "Source 1, Late delivery policy §1".

Keyboard

Stop, Edit, Retry and Undo must be real buttons, in tab order, with a visible focus ring. Shortcuts like Escape for Stop are a bonus on top, never a replacement.

When Stop appears, don't steal focus. The user should be able to reach it without losing their place.

Reduced Motion

@media (prefers-reduced-motion: reduce) {
  .typing-cursor, .skeleton { animation: none; }
}

Labels

<a href={p.url} aria-label={`Source ${n}, ${p.title}`}>[{n}]</a>

Dynamic content needs extra care.

  • An undo window (Lesson 5) that closes after 10 seconds can be too short for some users. Offer a way to extend it, or a longer window.
  • A preview card (Lesson 5) must move focus to itself, so a screen-reader user hears what will happen.
  • Designed error states (Lesson 6) should use role="status" or role="alert", so they are announced.

Easy way to remember: Hear it, reach it, calm it, name it. Live region, keyboard, reduced motion, labels.

Simple lesson: every control by keyboard, no motion when asked, and labels that make sense out loud.

The Accessibility Audit

Step through both frames.

  • Frame 1: the naive interface fails all four accessibility checks.
  • Frame 2: after this lesson, all four pass.

Lab Exercise: Make the AI UI Accessible

Your task: change the Lesson 12 settings in the Build tab so all four scenarios pass. Then press Check all scenarios.

Click the button and open the Build tab.

  • The four scenarios: a streamed answer, keyboard only, reduced motion and citations.
  • Screen reader hears: the last three announcements.
  • Keyboard and Motion boxes: what the user gets.

Simple lesson: four checks, four fixes.

What Changes per Framework

OptionAccessibility
Hand-built ReactA sentence announcer, real buttons, a reduced-motion query and labels, as above
axe-coreAutomated checks for every page; add it to your Playwright tests
CopilotKit / Vercel AI SDK UICheck that your chat components announce streamed text sensibly
Syncfusion AI componentsSyncfusion components document keyboard and screen-reader support (optional, not graded)

Build It in React

In the Code tab, tokens arrive many times a second. A live region that changes on every token is too noisy for a screen reader. Keep the full text on screen, and copy only finished sentences into an aria-live="polite" region.

<p aria-live="polite">{lastFinishedSentence}</p>

The check listens to the region and counts how often it changes.

TL;DR

  1. Accessible = usable by screen reader, keyboard only, and with less motion.
  2. Live region = aria-live="polite", updated once per sentence, not per token.
  3. Keyboard = Stop, Edit, Retry and Undo as real buttons; shortcuts on top.
  4. Reduced motion = stop decoration, keep the content.
  5. Labels = "Source 1, Refund policy §2", not "link, 1".
  6. axe-core = automated checks on every page.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
No live region"The text is on screen."Announce it with aria-live.Screen-reader users hear nothing.
Announcing every token"More updates are better."Announce per sentence.A stutter nobody can follow.
Stop only by mouse"It's just a button."Real buttons in tab order, plus Escape.Keyboard users can't stop the agent.
Animations always on"It looks alive."Respect prefers-reduced-motion.Some users feel sick.
Chips as bare numbers"Users see the brackets."Label each chip with its source.Screen readers say "link, 1".

Check Your Understanding

Exercises

What to Remember

  • Streamed text must reach the ear too.
  • Use aria-live="polite", updated per sentence.
  • Every AI control must work by keyboard.
  • Respect reduced motion: stop decoration, keep content.
  • Label citation chips so they make sense out loud.
  • Run axe-core, and test with a real screen reader.

Further Reading

Next: Capstone: Your AI UX Kit, where everything you built is graded together against 15 scenarios, and you export your kit.