Summarize the latest changes and keep the draft editable.
Recipe / chat streaming
Keep the stream honest.
A response can be running, complete, interrupted, or cancelled. Show the state on the message so the user never mistakes a partial answer for a finished one.
Specimen
Preview the live recipe, then copy HTML or Install. CSS is composition-only: copy the listed component stylesheets.
Preview
Code
chat-streaming.html
<section class="au-chat-thread" aria-label="Streaming conversation">
<div class="au-chat-thread__messages">
<article class="au-chat-message au-chat-message--user">
<p class="au-chat-message__body">Summarize the latest changes and keep the draft editable.</p>
</article>
<article class="au-chat-message au-chat-message--assistant" data-au-state="streaming">
<p class="au-chat-message__body">I am still assembling the release note...</p>
</article>
</div>
<div class="au-chat-thread__composer">
<form class="au-chat-input" method="post" aria-label="Message">
<textarea class="au-chat-input__textarea" id="stream-message" name="message" rows="1" placeholder="Send a message..."></textarea>
<div class="au-chat-input__footer">
<span class="au-chat-input__hint">Native form, host-owned submit.</span>
<button class="au-btn au-btn--primary" type="submit" aria-label="Send">↑</button>
</div>
</form>
</div>
</section>Composition only — copy component CSS from chat-thread, chat-message, chat-input, loading-state, and reasoning. Do not treat
recipes.css as product CSS.Install
head.html
<link rel="stylesheet" href="tokens.css">
<link rel="stylesheet" href="themes.css">
<link rel="stylesheet" href="components/button/button.css">
<link rel="stylesheet" href="components/chat-thread/chat-thread.css">
<link rel="stylesheet" href="components/chat-message/chat-message.css">
<link rel="stylesheet" href="components/chat-input/chat-input.css">
<link rel="stylesheet" href="components/chat-input/chat-input-features.css">
<link rel="stylesheet" href="components/loading-state/loading-state.css">
<link rel="stylesheet" href="components/reasoning/reasoning.css">
<link rel="stylesheet" href="host-chrome.css">Static state matrix
visible without JavaScriptThe host can pre-render any terminal state. Keep the message body and status label together so a refresh never loses the explanation.
streaming tokens still arriving
success complete response
error recoverable interruption
cancelled host stopped run
Contract and safety
- Install
- tokens · chat-thread · chat-message · chat-input · thinking-*
- States
- streaming · success · error · cancelled
- Host
- Token buffer, retry, cancel, persistence
For agents
chat-streaming.contract
# agent-ui / recipe / chat-streaming
recipe: chat-streaming
purpose: Honest streaming conversation with thinking, partial tokens, and a native composer.
install:
- tokens.css
- chat-thread.css
- chat-message.css
- chat-input.css
- loading-state.css
- reasoning.css
states_on_message: streaming | success | error | cancelled
no_javascript: Messages, elapsed steps, partial response, and native composer remain visible in document flow.
accessibility: Use an article; expose role as text; label the thread; announce status politely; thinking dots are decorative.
security: Streamed text is untrusted. Escape before insertion. Never interpret stream chunks as HTML.
host_owns: token buffering, retry, cancellation, persistence, and the decision that a response is complete.
rules:
- Host owns execution and stream lifecycle. Components only render state.
- Escape untrusted model output before insertion.
- Use only documented data-au-state values.Source map
The smallest copyable flow is split across independent primitives. Replace the demo controller with the host's stream events.