MenuClose

Block Scroll Story — React Page Block

Scroll narrative: a sticky scene crossfades as story steps cross the viewport center; on small screens the scene docks above the steps so the story reads end to end.

View source
PreviewReact
Starting live preview

Usage

import { BlockScrollStory } from "@/components/BlockScrollStory";

export function Story() {
  return (
    <BlockScrollStory
      title="A release week, told in three scenes."
      steps={[
        { eyebrow: "Monday", title: "Sketch the week.", text: "Drop deploys onto one canvas." },
        { eyebrow: "Wednesday", title: "Ship behind a canary.", text: "Orbit pauses drift for you." },
        { eyebrow: "Friday", title: "Replay it.", text: "One scrubbable timeline." },
      ]}
      {...{
        title: "A release week, told in three scenes.",
        interactive: true,
        flip: false,
        accent: "#d5ed9a",
        scheme: "dark"
      }}
    />
  );
}
Implementation notes & API

Purpose: scroll-linked narrative with sticky media. Copy it, ship it — every string, link and color arrives through props. Runtime: pure DOM + CSS. No WebGPU, canvas, animation library or external assets; SSR-safe on first render. Theming: root custom properties (--vb-bg, --vb-fg, --vb-accent, --vb-border, --vb-radius, --vb-container) or the accent/scheme/className/style props. scheme flips the dark default to light. Guardrails: responsive via container queries down to 320px; interactive behavior degrades on touch; prefers-reduced-motion drops entrance/scroll motion while content stays complete. Active step detection is IntersectionObserver over the middle band of the viewport; CSS swaps scenes, so no scroll listeners or JS animation. Reduced motion keeps every step at full opacity with an instant scene swap — the narrative never hides content.

PropertyTypeNotes / default
eyebrow / title / descriptionReactNodeSection header
stepsreadonly StoryStep[]{ eyebrow?, title, text, media? }
flipbooleanfalse; media column on the right when true (desktop)
interactivebooleantrue; false pins the first scene (also respects reduced motion)
scheme"dark" | "light"dark
accentstring (hex)Sets --vb-accent for the whole block
className / stylestring / CSSPropertiesApplied to the section root
childrenReactNodeReplaces the default content entirely
View source on GitHubFree for commercial projects · MIT