Skip to content

Scrolly

Open .md
Live preview

Loading example…

Scrollytelling: text steps scroll past while a graphic stays in view and follows the active step. The active step is the one crossing a trigger line, found with an IntersectionObserver. Wide containers put the graphic beside the steps, narrow ones pin it above them (container queries, so it works inside a card or a dialog too). The text is real content in reading order, so it works with a screen reader and without scrolling effects.

import { Scrolly } from "@jcsoftdev/ui/containers";

Single-component entry points tree-shake to just this component: @jcsoftdev/ui/containers/scrolly.

export const Default: Story = {
render: (args) => (
<div style={{ padding: 16 }}>
<Narrative side={args.graphicSide} />
</div>
),
};

Source of the Storybook story.

Prop Type Default Description
graphic * (state: ScrollyGraphicState) => ReactNode The sticky graphic: receives the active step, e.g. to change the data of a chart.
children * ReactNode ScrollyStep elements, in reading order.
offset number 0.5 Where the trigger line sits in the scroll area: 0 top, 0.5 middle (default), 1 bottom.
root HTMLElement | null null Scroll container the steps move in; the page by default.
graphicHeight string "min(55svh, 26rem)" Height of the sticky graphic, any CSS length.
graphicSide "start" | "end" "end" Which side the graphic takes when the container is wide.
step number Controlled step; the scroll position decides when omitted.
onStepChange ((step: number) => void)
aria-label string Accessible name of the narrative.
className string

* marks a required prop.

packages/ui/src/containers/scrolly/scrolly.tsx