---
title: Accessibility
description: Integrate reduced motion, semantic Markdown, and completion announcements into your application.
---

Smoothstream preserves Markdown's semantic structure and follows the user's reduced-motion preference by default. Your application supplies the surrounding page structure, accessible controls, and conversation layout.

## Keep Markdown outside live regions

The rendered Markdown is a normal document subtree. Readers can navigate its headings, lists, links, tables, and code using their assistive technology.

Avoid placing `aria-live`, `role="status"`, `role="log"`, or `role="alert"` on the Markdown root or an ancestor. During reveal, characters enter and temporary wrappers compact away. A live region around that activity can generate repeated announcements.

Each streaming instance creates a separate, visually hidden status region. For a nonempty response, it announces **Content ready.** once input is closed and the planned content has settled. The announcement points readers to the available content; it does not duplicate the entire answer in a live region.

Static mode skips this announcement. Use it for stored messages that should already be available when mounted.

## Add application status separately

Keep your request status or error message beside the Markdown, outside its subtree. For example:

```tsx
<section aria-label="Assistant response">
  <p role="status">{receiving ? "Receiving response…" : ""}</p>
  <Smoothstream receiving={receiving}>{markdown}</Smoothstream>
  {error && <p role="alert">{error}</p>}
</section>
```

Here the application's status describes the network request. Smoothstream's announcement describes the settled presentation, which may finish later. Avoid adding a second completion announcement when `receiving` becomes `false`.

The [completion callbacks](/guides/completion-callbacks) let your application react when the final unit appears or when presentation settles. They do not change the built-in announcement. The public API does not expose an announcement text option or announcement toggle; the message is `Content ready.`

## Respect reduced motion

| `reducedMotion` | Policy |
| --- | --- |
| `"system"` | Follow `prefers-reduced-motion`. This is the default. |
| `"always"` | Use reduced motion regardless of the system preference. |
| `"never"` | Allow animation regardless of the system preference. |

With reduced motion, structural stabilization still runs, but safe content appears immediately as semantic HTML. Character pacing, reveal spans, fades, and layout transitions are removed. An incomplete link or table can still wait for enough source to become stable.

Once a response enters reduced mode, it stays reduced for that mounted instance. Turning motion back on mid-response would risk replaying visible content. A new instance evaluates its own policy.

`mode="static"` and reduced motion solve different problems. Static mode makes completed Markdown appear immediately; reduced motion also governs interactive motion such as code-copy feedback. Use static mode for history and retain the default system policy for motion.

In React and Vue SSR, streaming mode with the system policy initially produces an empty shell. This keeps the server output and first hydration consistent until the browser reads the preference. See the [React](/adapters/react) or [Vue](/adapters/vue) guide for the SSR behavior.

## Preserve semantics when customizing

Keep meaningful alt text on images and descriptive link labels. Confirmed links are interactive while their labels animate. A footnote reference may temporarily have no `href` until its target appears; preserve that state in custom components.

Use semantic elements in [component overrides](/customize/components) and forward the supplied attributes and styles. Replacing a heading with a generic container, or dropping accessibility attributes, changes what assistive technology can understand.

Task-list checkboxes are native disabled inputs. The default theme uses custom visuals while restoring the platform checkbox in forced-colors mode. Setting `unstyled` leaves native checkbox appearance in place.

If you customize colors, check contrast against your application's background in each theme. If you implement scrolling, let readers move away from the bottom without repeatedly pulling them back. Those choices belong to the surrounding application.
