# MessageScroller

> A scrollable frame for a conversational message log.

```ts
import { MessageScroller } from "@sdxc/ui";
```

Renders the frame's root host: a bordered, rounded `<div>` that
declares the `ui-message-scroller` container queried by
MessageScroller.Button for its own placement.

## Examples

```tsx
<MessageScroller mix={css({ blockSize: "32rem" })}>
	<MessageScroller.Viewport>
		<MessageScroller.Content aria-busy={isStreaming}>
			<MessageScroller.Item messageId={turn.id} scrollAnchor>
				{turn.content}
			</MessageScroller.Item>
		</MessageScroller.Content>
	</MessageScroller.Viewport>
	<MessageScroller.Button aria-label={t("chat.jumpToLatest")} />
</MessageScroller>
```

## Props

| Prop | Type | Description |
| --- | --- | --- |
| `children` | `RemixNode` | The frame's compound parts: MessageScroller.Viewport and, optionally, MessageScroller.Button. |

Also accepts everything in `TagProps<"div">`.


### MessageScroller.Viewport

Renders the frame's scrolling surface as ScrollArea.Viewport, fixed to the block axis.

| Prop | Type | Description |
| --- | --- | --- |
| `children?` | `RemixNode` | MessageScroller.Content, scrolling inside the viewport. |

Also accepts everything in `TagProps<"div">`.


### MessageScroller.Content

Renders the frame's live region: a `<div>` carrying the ARIA log role, stacking its MessageScroller.Item rows in a column and announcing each added row via `aria-relevant="additions"`.

| Prop | Type | Description |
| --- | --- | --- |
| `"aria-busy"?` | `TagProps<"div">["aria-busy"]` | Marks the log as busy while a row inside it is still being written to — a reply still streaming in, for instance — forwarded to the host's native `aria-busy` attribute. Clear it once the row settles. |
| `children?` | `RemixNode` | The MessageScroller.Item rows to render in document order. |

Also accepts everything in `TagProps<"div">`.


### MessageScroller.Item

Renders a single message row: a `<div>` mirroring `messageId` onto `data-message-id` and, when `scrollAnchor` is set, an empty `data-scroll-anchor` attribute for a scroll-follow behavior to read.

| Prop | Type | Description |
| --- | --- | --- |
| `messageId` | `string` | Stable id of the message this row renders, mirrored onto the host's `data-message-id` attribute so a scroll-follow behavior can find, measure, and scroll to this exact row directly. |
| `scrollAnchor?` | `boolean` | Marks this row as a candidate anchor point — typically the first row of a new turn — mirrored onto `data-scroll-anchor` so a scroll-follow behavior can tell which rows are worth anchoring the viewport to. |
| `children?` | `RemixNode` | The row's own content, typically a conversational row composed inside it. |

Also accepts everything in `TagProps<"div">`.


### MessageScroller.Button

Renders a static jump-to-latest control as Button, floating over the frame's block-end edge and starting `hidden` until a paired scroll-follow behavior clears the attribute.

| Prop | Type | Description |
| --- | --- | --- |
| `hidden?` | `boolean` | Whether the control is absent from rendering and the accessibility tree. A paired scroll-follow behavior clears this attribute as the reader scrolls away from the live edge and sets it again on return or press. |

Also accepts everything in `Button.Props`.

