recipe / 01

Specimen

Preview the live recipe, then copy HTML or Install. CSS is composition-only: copy the listed component stylesheets.

Preview

live-run / release notesstreaming / waiting for tokens

Summarize the latest changes and keep the draft editable.

I am still assembling the release note...

Agent is thinking
Read the repository
Write the summary

The host decides when a stream is complete. The component never invents success.

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 JavaScript

The 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

Drafting the final paragraph...

success complete response

The release note draft is complete.

error recoverable interruption

The stream stopped. Retry from the last saved token.

cancelled host stopped run

The host cancelled this response before completion.

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.