Components
Scroll Button
A floating scroll-to-bottom button, meant to pair with MessageScroller. Purely presentational — no Morphos equivalent.
Installation
npx kosmesis add scroll-buttonpnpm dlx kosmesis add scroll-buttonyarn dlx kosmesis add scroll-buttonbunx kosmesis add scroll-buttonInstall the following dependencies:
npm install @morphos/iconspnpm add @morphos/iconsyarn add @morphos/iconsbun add @morphos/iconsCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import { Icon } from "@morphos/icons";import { cn } from "@/lib/utils";export interface ScrollButtonProps { visible?: boolean; onClick?: () => void; class?: string;}/** Mount unconditionally and drive `visible` for the enter/exit transition — wrap the usage in a reactive thunk to update without remounting. */@Component()export class ScrollButton extends StatelessComponent<ScrollButtonProps> { render() { const { visible = true, onClick, class: cls } = this.props; return ( <button type="button" aria-label="Scroll to bottom" data-state={visible ? "visible" : "hidden"} class={cn( "absolute bottom-4 left-1/2 flex size-8 -translate-x-1/2 items-center justify-center rounded-full border bg-background text-foreground shadow-md transition-all", "data-[state=hidden]:pointer-events-none data-[state=hidden]:translate-y-2 data-[state=hidden]:opacity-0", cls, )} onClick={onClick} > <Icon name="ArrowDown" size={16} /> </button> ); }}Install the following dependencies:
npm install @praxisjs/css @morphos/iconspnpm add @praxisjs/css @morphos/iconsyarn add @praxisjs/css @morphos/iconsbun add @praxisjs/css @morphos/iconsCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { cx, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import { Icon } from "@morphos/icons";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);class ScrollButtonStyles extends Stylesheet { $root = this.css({ position: "absolute", bottom: "1rem", left: "50%", display: "flex", height: "2rem", width: "2rem", transform: "translateX(-50%)", alignItems: "center", justifyContent: "center", borderRadius: "9999px", border: `1px solid ${t.border}`, backgroundColor: t.background, color: t.foreground, boxShadow: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)", transition: "all 200ms ease", }).on('&[data-state="hidden"]', { pointerEvents: "none", transform: "translateX(-50%) translateY(0.5rem)", opacity: 0 });}export interface ScrollButtonProps { visible?: boolean; onClick?: () => void; class?: string;}/** Mount unconditionally and drive `visible` for the enter/exit transition — wrap the usage in a reactive thunk to update without remounting. */@Component()export class ScrollButton extends StatelessComponent<ScrollButtonProps> { @Styled(ScrollButtonStyles) $s!: ScrollButtonStyles; render() { const { visible = true, onClick, class: cls } = this.props; return ( <button type="button" aria-label="Scroll to bottom" data-state={visible ? "visible" : "hidden"} class={cx(this.$s.$root, cls)} onClick={onClick} > <Icon name="ArrowDown" size={16} /> </button> ); }}Examples
About
Purely presentational — no Morphos equivalent. Same visibility convention as ActionBar: mount it
unconditionally and drive visible for the enter/exit transition (wrap the usage in a reactive
thunk in your own component if you want it to update without remounting). Pair with
MessageScroller's scrollToBottom() for the onClick.
Usage
import { MessageScroller } from "@/components/ui/message-scroller";
import { ScrollButton } from "@/components/ui/scroll-button";
<div class="relative">
<MessageScroller ref={scrollerRef}>...</MessageScroller>
<ScrollButton visible={isScrolledUp} onClick={() => scrollerRef.current?.scrollToBottom()} />
</div>Props
| Prop | Type | Default |
|---|---|---|
visible | boolean | true |
onClick | () => void | — |
class | string | — |