Keep the draft editable while the agent works.
Installation
tokens.css + chat-thread.css + chat-message.css + chat-input.css + button.csshead.html
<link rel="stylesheet" href="tokens.css">
<link rel="stylesheet" href="components/chat-thread/chat-thread.css">Basic
Code
chat-thread.html + chat-thread.css
chat-thread
<style>
.au-chat-thread {
--au-thread-bg: var(--au-bg);
--au-thread-border: transparent;
--au-thread-max-width: var(--au-thread-max, 44rem);
background: var(--au-thread-bg);
border: 0;
border-radius: 0;
display: flex;
flex-direction: column;
min-height: 420px;
overflow: hidden;
}
.au-chat-thread__messages {
display: flex;
flex-direction: column;
gap: 24px;
margin-inline: auto;
max-width: var(--au-thread-max-width);
min-height: 0;
overflow: auto;
padding: 16px 16px 56px;
scroll-behavior: smooth;
width: 100%;
}
.au-chat-thread__composer {
background: linear-gradient(to top, var(--au-bg) 70%, color-mix(in srgb, var(--au-bg) 0%, transparent));
border-top: 0;
margin-inline: auto;
max-width: var(--au-thread-max-width);
padding: 0 16px 16px;
width: 100%;
}
.au-chat-thread__new {
align-self: center;
background: var(--au-surface);
border: 1px solid var(--au-border-color);
border-radius: var(--au-radius-pill);
box-shadow: var(--au-elevation-composer);
color: var(--au-fg-muted);
font: 600 12px/1 var(--au-font-sans);
margin-bottom: var(--au-space-3);
padding: 8px 12px;
}
/* --- absorbed: tabs variant --- */
/* agent-ui / chat-tabs — first-class component (promoted from host extracts). */
/* 07 Chat tabs */
.au-chat-tabs { display: flex; gap: 4px; margin: 0 0 12px; }
.au-chat-tabs__tab[aria-pressed="true"] {
background: var(--au-surface-muted);
color: var(--au-fg);
}
.au-chat-tabs__panel[hidden] { display: none; }
</style>
<section class="au-chat-thread" aria-label="Preview conversation"><header class="au-chat-header"><button type="button" class="au-btn au-btn--ghost au-chat-header__icon au-chat-header__menu" aria-label="Toggle menu"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 5h16"/><path d="M4 12h16"/><path d="M4 19h16"/></svg></button><button type="button" class="au-btn au-btn--ghost au-chat-header__icon au-chat-header__sidebar" aria-label="Hide sidebar"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/></svg></button><span class="au-chat-header__title">Keep the draft editable</span><button type="button" class="au-btn au-btn--ghost au-chat-header__icon au-chat-header__share" aria-label="Share" disabled><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2v13"/><path d="m16 6-4-4-4 4"/><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/></svg></button></header><div class="au-chat-thread__messages"><article class="au-chat-message au-chat-message--user"><p class="au-chat-message__body">Keep the draft editable while the agent works.</p></article><article class="au-chat-message au-chat-message--assistant"><p class="au-chat-message__body">The conversation remains in normal document flow.</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="thread-preview-message" name="message" rows="1" placeholder="Send a message..." aria-label="Message"></textarea><div class="au-chat-input__footer"><span class="au-chat-input__hint">Native form</span><button class="au-btn au-btn--primary" type="submit" aria-label="Send">↑</button></div></form></div></section>Tabs
Code
chat-thread.html + chat-thread.css
chat-thread
<style>
.au-chat-thread {
--au-thread-bg: var(--au-bg);
--au-thread-border: transparent;
--au-thread-max-width: var(--au-thread-max, 44rem);
background: var(--au-thread-bg);
border: 0;
border-radius: 0;
display: flex;
flex-direction: column;
min-height: 420px;
overflow: hidden;
}
.au-chat-thread__messages {
display: flex;
flex-direction: column;
gap: 24px;
margin-inline: auto;
max-width: var(--au-thread-max-width);
min-height: 0;
overflow: auto;
padding: 16px 16px 56px;
scroll-behavior: smooth;
width: 100%;
}
.au-chat-thread__composer {
background: linear-gradient(to top, var(--au-bg) 70%, color-mix(in srgb, var(--au-bg) 0%, transparent));
border-top: 0;
margin-inline: auto;
max-width: var(--au-thread-max-width);
padding: 0 16px 16px;
width: 100%;
}
.au-chat-thread__new {
align-self: center;
background: var(--au-surface);
border: 1px solid var(--au-border-color);
border-radius: var(--au-radius-pill);
box-shadow: var(--au-elevation-composer);
color: var(--au-fg-muted);
font: 600 12px/1 var(--au-font-sans);
margin-bottom: var(--au-space-3);
padding: 8px 12px;
}
/* --- absorbed: tabs variant --- */
/* agent-ui / chat-tabs — first-class component (promoted from host extracts). */
/* 07 Chat tabs */
.au-chat-tabs { display: flex; gap: 4px; margin: 0 0 12px; }
.au-chat-tabs__tab[aria-pressed="true"] {
background: var(--au-surface-muted);
color: var(--au-fg);
}
.au-chat-tabs__panel[hidden] { display: none; }
</style>
<div class="au-chat-tabs" data-au-component="chat-thread" data-au-variant="tabs" role="tablist"><button type="button" class="au-btn au-btn--ghost au-btn--sm au-chat-tabs__tab" aria-pressed="true">Chat</button><button type="button" class="au-btn au-btn--ghost au-btn--sm au-chat-tabs__tab" aria-pressed="false">Context</button></div>Component contract
- Install
- tokens.css · chat-thread.css · chat-message.css · chat-input.css · button.css
- States
- static, streaming, new-content; tabs via variant parts
- Host
- scroll, insertion, persistence, and tab switching
For agents
chat-thread.contract
# agent-ui / chat-thread
component: chat-thread
selector: .au-chat-thread
data_au_component: chat-thread
purpose: Conversation scroll + composer slot, with optional tabs variant (absorbed chat-tabs).
install:
- tokens.css
- chat-thread.css
- chat-message.css
- chat-input.css
- button.css
states: static, streaming, new-content; tabs via variant parts.
css_variables: Uses thread surface tokens.
events: native form, focus, and click only — no custom events or runtime required
no_javascript: Messages stay readable; nested forms can submit.
host_owns: The host owns scroll, insertion, persistence, and tab switching.
accessibility: Keep messages in a readable region; tabs need pressed/selected text.
security: Thread content is host-owned escaped text.
rules:
- Host owns execution, authorization, persistence, validation, and state transitions.
- Components render host-provided state; they never execute tools or imply approval.
- Escape untrusted model output, tool args, and tool output before insertion.
- Use only documented data-au-* attributes and data-au-state values.
- Keep the no-JS path usable. Do not add React, Tailwind, bundlers, or remote assets.
source: components/chat-thread/chat-thread.{html,css} + README.md + fixture.html