kai-scroll-button
Appears once the reader scrolls up, so the latest message is a click away.
- Shadow DOM
- 1 event
- 3 button variants
- Auto-hides at bottom
- Works via id or ancestor walk
Preview
Section titled “Preview”Point the button at its scroll container with the for attribute:
<div id="my-feed" style="height:400px; overflow-y:auto; position:relative;"> <!-- list content --></div>
<kai-scroll-button for="my-feed"></kai-scroll-button>
<script type="module"> import '@kitn.ai/ui/web-components';</script>for— theidof the scrollable container (mirrors<label for="...">). Omit to walk up the composed tree to the nearest scrollable ancestor.- Visibility — hidden when the container is within 50 px of the bottom; animated (translate + scale + opacity) otherwise. Uses a passive
scrolllistener — no polling. kai-scroll— fires (no detail) each time the button is clicked andscrollToBottom()completes. Use it to pause auto-scroll logic.
Positioning
Section titled “Positioning”No placement prop, and no positioning CSS of its own: floating the button over the thread is three lines of your layout.
- Put
position: relativeon a box that does not scroll, wrapping the scroll container. Not on the scroll container itself: an absolutely positioned child of a scrolling element is placed against its padding box and then scrolls away with the content. - Give the button
position: absoluteand an offset from the bottom. - Centre it, or anchor it to a corner.
<!-- the WRAPPER is the positioning context, not the scroll container --><div style="position:relative; height:400px"> <div id="my-feed" style="height:100%; overflow-y:auto"><!-- messages --></div>
<!-- centred over the content --> <kai-scroll-button for="my-feed" style="position:absolute; left:50%; bottom:12px; transform:translateX(-50%)"> </kai-scroll-button>
<!-- or anchored to the corner --> <kai-scroll-button for="my-feed" style="position:absolute; right:12px; bottom:12px"> </kai-scroll-button></div>On a wide thread, centre on the message column rather than the container, the way <kai-chat> does internally: the wrapper takes the same max width as the messages and is centred with left:50% plus translateX(-50%).
When a composer is docked at the bottom, raise the offset so the button clears it (bottom:56px suits a typical one).
Labelling
Section titled “Labelling”label sets the accessible name, announced whether or not the button is visible, and defaults to Scroll to bottom.
show-label also draws that text beside the icon, so the visible text is the accessible name and nothing gets announced twice.
<!-- icon only, announced in German --><kai-scroll-button for="my-feed" label="Zum Ende springen"></kai-scroll-button>
<!-- the name drawn beside the icon --><kai-scroll-button for="my-feed" show-label label="Jump to latest"></kai-scroll-button>Examples
Section titled “Examples”Default
Section titled “Default”Scroll the list up to reveal it, click to jump back.
Ghost Variant
Section titled “Ghost Variant”Solid (Default) Variant
Section titled “Solid (Default) Variant”Smaller Icon Size
Section titled “Smaller Icon Size”| Property | Type | Default | Notes |
|---|---|---|---|
| theme | "light" | "dark" | "auto" | 'auto' | Color mode (`auto` follows prefers-color-scheme). |
| for | string | — | CSS id of the scroll container to control. |
| variant | "outline" | "ghost" | "default" | 'outline' | Button visual variant. Defaults to `outline`. |
| size | "sm" | "md" | "lg" | "icon" | "icon-sm" | 'icon' | Button size token. Defaults to `'icon'` (square). |
| label | string | — | The button's accessible name. It is announced whether or not the label is visible, so the text is always localisable. Defaults to `'Scroll to bottom'`. |
| showLabel | boolean | — | Also render `label` visibly beside the icon. Default `false` (icon-only). |
Events
Section titled “Events”| Event | Detail | Notes |
|---|---|---|
| kai-scroll | — | Emitted when the user clicks the button and `scrollToBottom()` is called. Carries no detail; consumers use it to know a manual scroll occurred. |
Composed from
Section titled “Composed from”This element wraps these SolidJS components — reach for them directly when you need finer control than the props expose.