Kosmesis
Components

Scroll Button

A floating scroll-to-bottom button, meant to pair with MessageScroller. Purely presentational — no Morphos equivalent.

Installation

npx kosmesis add scroll-button
pnpm dlx kosmesis add scroll-button
yarn dlx kosmesis add scroll-button
bunx kosmesis add scroll-button

Install the following dependencies:

npm install @morphos/icons
pnpm add @morphos/icons
yarn add @morphos/icons
bun add @morphos/icons

Copy and paste the following code into your project.

scroll-button.tsx
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/icons
pnpm add @praxisjs/css @morphos/icons
yarn add @praxisjs/css @morphos/icons
bun add @praxisjs/css @morphos/icons

Copy and paste the following code into your project.

scroll-button.tsx
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

PropTypeDefault
visiblebooleantrue
onClick() => void
classstring

On this page