Message Scroller

The window a chat transcript scrolls inside: a 420 × 480 frame holding 585 of messages, and one 28px button that appears when you are no longer at the newest...

Two components, six variants

Using the Message Scroller

What is this actually for?
Keeping a streaming transcript from yanking the page around. The source splits it into a headless provider that owns the scroll state, a viewport that takes the native scroll events, a content container, an item wrapper per row, and the button. The kit draws the two of those you can see: the frame and the button.
Why is the frame 480 tall over 585 of content?
Because the ratio is the component. A viewport shorter than what it holds is the entire premise - if the transcript fit, there would be nothing to scroll and no button to draw. The content is parked at -42, which puts the newest message exactly at the fold: one turn behind, the smallest gap that still earns the control.
What does Direction change?
Only the rotation. Up is the Down chevron turned 180 degrees rather than a second glyph, so the two can never drift apart. Start scrolls to the top of the transcript, End to the newest message, and each is inert when there is nothing to reach in that direction.
Why is there no Disabled variant?
Because inert here means absent. The source hides the button rather than greying it, so a scroller sitting at the live edge simply does not render one. Drawing a disabled state would be drawing something the component never shows.
Does it own the messages?
Not at all. The transcript is six Message instances 32 apart inside 20 of padding - avatars, a header and a delivery footer included. The scroller adds nothing to a row; it only decides which rows you can see. It has no composer and no header either, so a real chat window is this with a Field under it.
What do the five scenarios on this page show?
The behaviours a still frame cannot. Anchored turns jump to the top of a turn rather than a fixed distance. Group chat anchors on markers - a day, someone joining - because with three people the turn stops being the useful unit. Context kept is the one the component exists for: messages arrive while you read back and the viewport does not move, the button takes a count instead. Opening a thread lands on the first unread rather than the top or the bottom. And loading earlier messages shifts the offset by exactly the height that went on top, so the row under your eye stays put.