Agent layer
Turn
One step of an exchange. Everything else in this layer shows work; a turn is what the work happens inside — an answer is not a paragraph but a container for steps, a diff, an approval, a budget.
You14:02
Add a share bar above the history strip.
Agent14:02
I'll put it above the strip and let one legend key both.
Markup
<div class="inst-stack inst-stack--loose">
<div class="inst-turn" data-from="user">
<div class="inst-turn-head"><span class="inst-turn-who">You</span><span>14:02</span></div>
<div class="inst-turn-body">Add a share bar above the history strip.</div>
</div>
<div class="inst-turn">
<div class="inst-turn-head"><span class="inst-turn-who">Agent</span><span>14:02</span></div>
<div class="inst-turn-body">I'll put it above the strip and let one legend key both.</div>
<div class="inst-turn-actions">
<button class="inst-btn inst-btn--xs inst-btn--icon inst-btn--ghost" type="button" aria-label="Copy"><svg class="inst-icon" aria-hidden="true"><use href="#i-copy"/></svg></button>
<button class="inst-btn inst-btn--xs inst-btn--icon inst-btn--ghost" type="button" aria-label="Retry"><svg class="inst-icon" aria-hidden="true"><use href="#i-refresh"/></svg></button>
<button class="inst-btn inst-btn--xs inst-btn--icon inst-btn--ghost" type="button" aria-label="More"><svg class="inst-icon" aria-hidden="true"><use href="#i-more"/></svg></button>
</div>
</div>
</div>
Contract
| What | Required | Why |
|---|---|---|
data-from="user" on the person's turn |
yes | The machine's turn is the default. Marking both would mean an attribute value with nothing to declare |
A name in inst-turn-who |
yes | Colour and indent are not carriers: the recess is the same film in every theme, and a screen reader gets neither |
| A time | no | Useful in a long thread, noise in a short one |
inst-turn-actions keeps its space |
yes, if present | Hover may strengthen an element, but it may not bring one into being. The row is laid out either way, so nothing moves under the cursor; the buttons fade in, and @media (hover: none) gives them at full strength where there is no cursor to reveal them with. A row that is not laid out at all does not exist on a touchscreen |
| A thread as a list | no | Turns are siblings in an inst-stack--loose; the kit does not add a second name for a column |
The person's turn is recessed, the machine's has no surface at all: the answer is the one that holds steps, diffs and approvals, and each of those brings its own frame. A surface under them would put a plate inside a plate.
Accessibility
| Who spoke | Named in the text, not by the surface. The recess is decoration and reaches no assistive technology |
| Streaming | An answer still arriving carries the caret; the text itself is the announcement |
| Actions | Real buttons with labels. They stand in the flow, so Tab reaches them in reading order |
Composition
An answer that did work
Agent14:02
Added, and the legend now keys the tones.
edit src/data.css
.inst-share { display: flex; }
Markup
<div class="inst-turn">
<div class="inst-turn-head"><span class="inst-turn-who">Agent</span><span>14:02</span></div>
<div class="inst-turn-body">Added, and the legend now keys the tones.</div>
<details class="inst-step" open>
<summary class="inst-step-head">
<span class="inst-step-twist"></span>
<span class="inst-dot" data-tone="ok"></span>
<span class="inst-step-name">edit</span>
<span class="inst-step-sub">src/data.css</span>
<span class="inst-step-meta">0.4 s</span>
</summary>
<div class="inst-step-body">
<div class="inst-code">.inst-share { display: flex; }</div>
</div>
</details>
<div class="inst-turn-actions">
<button class="inst-btn inst-btn--xs inst-btn--ghost" type="button">Copy</button>
<button class="inst-btn inst-btn--xs inst-btn--ghost" type="button">Retry</button>
</div>
</div>
API
| Name | What it does |
|---|---|
| class | |
inst-turn | One step of an exchange |
inst-turn-head | Who and when, in the type of metadata |
inst-turn-who | The name inside that line |
inst-turn-body | What was said. It may hold any component of this layer |
inst-turn-actions | Copy, retry, branch. The row always holds its space; the buttons come up under the cursor, and stand at full strength wherever there is no cursor |
| Name | Value | What it does |
|---|---|---|
| attribute | ||
data-from | user | The person's turn, and only that one: it is recessed, the machine's turn is the default and bare |
| Name | Value | Where from |
|---|---|---|
| token | ||
--surface-recessed | light-dark(oklch(0 0 0 / 0.060), oklch(1 0 0 / 0.050)) | one across every cell |
--radius-lg | 12px | base +4: scale |
--pad-panel | var(--space-5) | base +14: density · scale |
--space-3 | 6px | one across every cell |
--text-xs | 0.78125rem | base +4: scale |
--text-muted | light-dark(var(--n-8), var(--n-6)) | one across every cell |
--text-secondary | light-dark(var(--n-9), var(--n-5)) | one across every cell |
--weight-medium | 600 | one across every cell |
--leading-ui | 1.4 | one across every cell |
Why the kit works this way — the constitution · Open source under MIT, github.com/keshon/instrument