Scroll-driven
storytelling
for shadcn/ui
Sticky image reveals with animated captions. Built on the CSS Scroll-Driven Animations API. Zero JavaScript, fully accessible, respects prefers-reduced-motion.
pnpm dlx shadcn@latest add https://shadcn-scroll-reveal.vercel.appr/scroll-reveal.jsonScroll to see it live
Image enters from the left
Add imageEnter="left" to slide the image in from the left as it enters the viewport. The caption appears in its natural flow position below the sticky image.
imageEnter="left"
The image slides in from the left. No captionMotion applied — the caption appears in its natural position below.
Caption sweeps in from the side
Combine imageEnter="bottom" with captionMotion="left-center" — the caption starts off-screen and lands in the center reading position as you scroll.
captionMotion="left-center"
The caption animates from off-screen left into the center reading position. Great for body text you want users to actually read.
Full pass-through motion
Use captionMotion="right-left" for a dramatic full-width sweep. Works best for short labels — reduce captionMaxWidth for a tighter card.
captionMotion="right-left"
The caption crosses the full width of the stage. Use a smaller captionMaxWidth for pass-through effects.
Installing
pnpm dlx shadcn@latest add https://shadcn-scroll-reveal.vercel.appr/scroll-reveal.jsonOne more step
@layer vanilla-scroll-sky block from globals.css to your project's global stylesheet.Props
Every CSS custom property is exposed as a typed React prop.
| Prop | Type | Default | Description |
|---|---|---|---|
image* | ReactNode | — | Content inside the sticky image container (img, video, svg) |
caption | ReactNode | — | Caption content; omit entirely for image-only scenes |
imageEnter | "left" | "right" | "top" | "bottom" | "none" | "bottom" | Direction the image slides in from on entry |
captionMotion | "left-center" | "right-center" | "left-right" | "right-left" | — | Horizontal animation path of the caption box |
sceneDuration | string | "220svh" | Total scroll height allocated to the scene |
imageHeight | string | "66svh" | Height of the sticky image |
imageStickTop | string | "0" | Sticky top offset — set to your header height |
captionDelay | string | "40svh" | Scroll distance before the caption appears |
captionMotionDistance | string | "80vw" | Horizontal distance the caption travels |
sceneExitSpace | string | "55svh" | Padding after the scene ends |
captionMaxWidth | string | "52rem" | Max width of the caption card |
imageFit | "cover" | "contain" | "cover" | CSS object-fit for the image element |
imagePosition | string | "center" | CSS object-position for the image element |
imageStartScale | string | ".96" | Initial scale of the image before it animates in |
Basic usage
import { ScrollReveal } from "@/components/scroll-reveal/scroll-reveal"
<ScrollReveal
imageEnter="left"
imageStickTop="4.5rem"
image={<img src="/hero.jpg" alt="..." />}
caption={
<>
<h2>Your headline</h2>
<p>A short caption that appears as you scroll.</p>
</>
}
/>Find it useful?
Star the repo and share it with your team.
0 bytes
Runtime JS
none
Dependencies
scroll-driven
Browser API
supported
Reduced motion