Recap Story Scroll
A full-screen, scene-by-scene scroll-snap narrative scroll — as each scene settles, oversized numbers and keywords are choreographed in, the background color transitions across the whole scene by theme, and progress dots and autoplay are built in.
This is a WebberUI Pro component
Free during the launch campaign: sign up or sign in, then hit “Copy install command” in the preview above and it installs straight away — no payment, no credit card. The command below returns 401 while you are signed out.
npx shadcn@latest add "https://webberui.com/r/recap-story-scroll.json?t=<install token>"Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<RecapStoryScroll />
Installation
npx shadcn@latest add "https://webberui.com/r/recap-story-scroll.json?t=<install token>"Or, once registries are configured in components.json, install it as @webberui/recap-story-scroll.
Usage
Assembled from compound components: RecapStoryScroll is the scroll-snap container, each RecapScene is one scene, and inside a scene you choreograph the data with RecapStat (oversized numbers) and RecapReveal (relay reveals). The container height is set through className, and each scene fills the container's full height.
import {
RecapStoryScroll,
RecapScene,
RecapStat,
RecapReveal,
} from "@/components/ui/recap-story-scroll";
<RecapStoryScroll autoPlay className="h-[560px]">
<RecapScene background="#100a24" label="Prologue">
<RecapReveal>
<p className="text-7xl font-bold">2025</p>
</RecapReveal>
</RecapScene>
<RecapScene background="#1c0b33" label="Listening">
<RecapStat value={48210} suffix=" minutes" label="Total listening time" />
<RecapReveal delay={0.35}>
<p className="text-sm text-white/70">That is 33 days without a break.</p>
</RecapReveal>
</RecapScene>
<RecapScene background="#0a2418" label="Epilogue">
<RecapReveal>
<p className="text-4xl font-bold">Thanks for your 2025</p>
</RecapReveal>
</RecapScene>
</RecapStoryScroll>Props
RecapStoryScroll
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Put RecapScene children here, one per scene |
autoPlay | boolean | false | Whether to play through the scenes automatically after mount (force-disabled under reduced motion) |
autoPlayInterval | number | 4500 | How long each scene stays during autoplay (milliseconds) |
loop | boolean | true | Whether to loop back to the first scene after the last one plays |
showProgress | boolean | true | Whether to show the scene progress dots on the right |
showKeywords | boolean | true | Whether to show the keyword relay row at the bottom (taken from each scene's label) |
showControls | boolean | true | Whether to show the scene counter and playback controls in the top-left |
onSceneChange | (index: number) => void | — | Callback when the settled scene changes |
className | string | — | Container styles; used to set the height (h-[560px] by default) |
RecapScene
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Scene content (RecapStat / RecapReveal or your own nodes) |
background | string | #0a0a0a | Background color for this scene (any CSS color value); when the scene settles, the whole background transitions smoothly to it |
label | string | — | Scene label, used for the progress dot tooltip and the keyword relay at the bottom |
className | string | — | Class appended to that scene's container |
RecapStat
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | — | Target value, counted up from 0 and scaled in when the scene settles |
prefix | string | "" | Prefix for the number (for example "$") |
suffix | string | "" | Suffix for the number (for example "+" or "%") |
decimals | number | 0 | Number of decimal places |
locale | string | "en-US" | Intl.NumberFormat locale, deciding the thousands separator and decimal mark |
duration | number | 1.4 | Duration of the count animation (seconds) |
label | string | — | Caption text below the number |
className | string | — | Styles for the number |
labelClassName | string | — | Styles for the caption text |
RecapReveal
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Content to reveal (headings, keywords, explanatory sentences, and so on) |
delay | number | 0 | Delay before the reveal starts after the scene settles (seconds), for relaying several passages within one scene |
y | number | 20 | Initial vertical offset (px) |
className | string | — | Styles |
How it works
- Scene-by-scene settling: the container is a
snap-y snap-mandatoryscroll area with each scene filling its height; a singleIntersectionObserverpicks whichever scene is closest to the viewport as the settled scene. - Data reveal: the components inside a scene read from context whether their scene has settled.
RecapStatcounts up from 0 and scales in when the scene settles, and resets to zero when it leaves so it can replay;RecapRevealstaggers bydelay, which is how the keywords relay passage by passage within a single scene. - Whole-scene color change: the background color is driven by the settled scene's
background, transitioning smoothly on the container's base layer (backgroundColoris interpolatable). This component is an immersive dark canvas, so give each scene a fairly darkbackgroundand adjust the text color throughclassNamewhere needed. - Autoplay:
autoPlaystarts after mount and advances one scene everyautoPlayInterval; it pauses automatically when the user interacts with the wheel or by touch, and you can also switch scenes by hand with the buttons in the top-left. - Keyword relay: the bottom lists each scene's
labelin order, lighting up the settled scene's keyword and fading the rest, forming a baton-passing index of the narrative.
Accessibility
- When the user has "reduce motion" enabled at the system level, the scroll-snap layout and the content are kept (everything is shown directly) while the count / scale reveals, the whole-scene color transition, and autoplay are disabled, and jumping between scenes becomes an instant scroll.
- The scroll area is focusable and supports scene-by-scene navigation with the arrow keys, PageUp, PageDown, Home, and End; any keyboard or mouse interaction pauses autoplay.
- The progress dots on the right are native buttons that can be focused with the keyboard and clicked to jump scenes, with the settled scene marked
aria-current; the play button carriesaria-pressedand anaria-label. - Each scene carries
aria-roledescription="scene"and anaria-label; the counting layer ofRecapStatis hidden from assistive technology and announces its final value througharia-label.
Exploded View Scroll
A scroll-driven exploded view — once the section pins, the product's cut-out layers blow apart along the Z axis, the specs annotated on each layer reveal in order, and at the end of the scroll everything collapses back into the complete product and releases into the next section.
Scrollytelling Stage
A two-column scroll narrative with a sticky data stage on the left and step-by-step prose on the right; the stage's chart state, highlight band, and annotation line morph between steps by tweening on the step index.