Chat Marker
The rows in a conversation that are not messages. Interleave them with Message bubbles to show what is happening between the turns: an assistant thinking, a tool that ran, a system note, a date break.
It is what makes an agent thread feel complete — without it, a transcript is just bubbles with no sense of activity.
Despite the name, this is not a text highlighter: it is a row in a conversation. To mark words inside a sentence, reach for Mark (manual) or Highlight (query-driven) instead.
Basic Usage
Streaming status
status makes the row a live region, so a text swap inside it is announced.
Add shimmer while work is in flight — it shimmers the label and marks the row
aria-busy.
The live region is rendered up front and stays in the DOM. That matters: a
region that appears at the same moment its text does is inert to assistive
technology — so a wire:stream swap into a conditionally-rendered region would
never announce. This one always announces.
{{-- 1. The row is always present; only its TEXT changes as work progresses --}}
<x-wirekit::chat-marker status :shimmer="$working">
{{ $statusLine }}
</x-wirekit::chat-marker>
System notes
intent colors a note with its semantic state token — and the text always says
what the color says, so the meaning never depends on color alone.
Variants
border draws a hairline under the row (a boundary between activity groups).
separator renders a label flanked by rules — the date break.
Interactive markers
Chat Marker is presentational on purpose. When a marker should be clickable,
compose a link component or a button component
inside it — they already carry the correct semantics, focus handling, and the
target="_blank" protection.
{{-- 1. The link brings its own semantics + rel protection --}}
<x-wirekit::chat-marker icon="external-link">
<x-wirekit::link href="https://github.com/example/pr/42" target="_blank">
View the pull request
</x-wirekit::link>
</x-wirekit::chat-marker>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant |
string | 'default' |
default (inline row), border (hairline under), separator (label between rules) |
icon |
string|null | null |
Decorative leading glyph |
status |
bool | false |
Render as an always-present polite live region |
shimmer |
bool | false |
Shimmer the label (implies aria-busy) |
intent |
string | 'neutral' |
neutral, info, success, warning, danger |
scope |
string|null | null |
Scoped personalization name |
Accessibility
statusrendersrole="status"+aria-live="polite"on a row that is always in the DOM, so streamed text swaps actually announce.shimmersetsaria-busy="true"— assistive technology knows work is in flight, and the shimmer itself is disabled under reduced-motion.- A row without
statusis not a live region: a static note must not interrupt the reader. intentis never the only signal — the row text always states the condition (WCAG 1.4.1).- The
iconis decorative (aria-hidden); the row text carries the meaning. variant="separator"delegates to Divider and gives it an explicit accessible name, so a date break is announced ("Today, separator") rather than silently skipped.
Keyboard Interaction
This component is presentational and does not respond to keyboard input. Any interactive content you compose inside it keeps its own keyboard behavior.
Pitfalls
- Do not make every row a live region. Only
statusrows should announce; a thread full of live regions is unusable with a screen reader. - Do not use it for text highlighting — that is Mark / Highlight.
- Keep labels short. A marker is a glance, not a paragraph.
Design Tokens
| Element | Token |
|---|---|
| Row text size | --text-wk-xs |
| Neutral text | --color-wk-text-muted |
intent text |
--color-wk-{info,success,warning,danger}-text |
border hairline |
--color-wk-border |
| Gap / spacing | --gap-wk-sm, --space-wk-sm |
| Shimmer sweep | --shimmer-wk-duration |