ComponentsStreaming Text
Agent

Streaming Text

A model reply that types itself. The stream fills a column downward, then wraps into a new column leftward · the answer advances across the page, not down it.

Design question

Horizontal streaming grows the page downward and pins the reader to the bottom edge. On the vertical axis the line is a column: where does a live stream go when it reaches the column's floor, and what shape is the caret on a vertical baseline?

Decision

Let writing-mode do the layout: a vertical-rl block with a fixed column length wraps the growing text into new columns right-to-left on its own · no column math. The caret rotates with the baseline: a short horizontal bar under the last glyph, not a vertical beam. Vertically Chat ships this exact pattern.

Default

생성 중…

The reply fills its column downward, then wraps into a new column leftward · writing-mode does the layout, no column math. The caret is a horizontal bar under the last glyph.

tsx
1// The column length is fixed · writing-mode wraps the growing
2// text into new columns right-to-left. No column math.
3export function StreamingReply({ shown, done }: { shown: string; done: boolean }) {
4 return (
5 <div
6 aria-busy={!done}
7 style={{
8 writingMode: "vertical-rl",

Guidance

Do
  • Fix the column length and let vertical-rl wrap the stream into new columns on its own
  • Rotate the caret with the baseline · a 1em horizontal bar under the last glyph
  • Anchor the block's right edge so growth reads leftward from the reading start
  • Set aria-busy while streaming and announce once at completion
  • Reveal the full text immediately under prefers-reduced-motion
Don't
  • Compute column breaks in JS · fixed length plus writing-mode already wraps correctly
  • Blink a vertical beam caret · that is the horizontal baseline's shape
  • Fire a live-region update per token · screen readers re-read the whole answer
  • Auto-scroll against the reading direction while the reader is looking back

Accessibility

The streaming block is aria-busy until complete, and completion is announced once through a polite status region · never per token. The reveal collapses to instant under prefers-reduced-motion. The stream is plain text in DOM order, so screen readers get one coherent block.

Open question

When a stream outgrows the viewport, should the surface auto-advance leftward with the write head or hold still with the read head? Vertically Chat follows the write head; a longer-form reader might not.