Skip to content
UI Kit
Accent color

Areas

Scroll stories and scroll effects

Sections that tell while the page scrolls. Each one renders its finished layout first, so it reads without JavaScript and rests with reduced motion.

On this page

Pick a scene#

Scroll stories and scroll journeys tell across several screens, a section holds and its content changes with the scroll.

ComponentVariantsWhat it does
sticky-stackfeatures, process, case-studies, split, pileCards stack while the page scrolls, the covered card steps back.
sideways-scrollcards, timeline, gallery, chapters, productsA row moves sideways while the page scrolls down.
zoom-revealimage, product, video, grid, app-iconA picture or product grows from small to the full window.
pinned-mediafullbleed, pan-zoom, wipe, reel, deviceOne medium stays while chapters change beside it.
number-journeycount, bar, flip, compare, ringOne large number travels through stages.
image-sequencespin, exploded, daylight, growA drawn picture that changes frame by frame like a flipbook.
path-journeyroute, delivery, cycle, ascentA marker travels along a path that draws itself.

Scroll effects and reveals and transitions are shorter. They make a transition or a headline memorable and hand the page back quickly.

ComponentVariantsWhat it does
split-revealdoors, shutter, diagonal, ticket, slatsParts open and reveal what lies behind.
parallax-layerslandscape, product, collage, typeLayers move at different speeds.
text-scrublines, converge, scale, stack, outlineLarge type moves exactly with the scroll.
card-fanhand, spread, arc, deck, photosCards fan out, spread and pile up.
mask-revealheadline, zoom-through, lines, numbersType as a window onto a picture.
section-transitionsdark-band, accent-wash, curtain, circle, card-shrinkHow one section hands over to the next.

Use a scene#

Scenes take content through props like any component. Pictures go in as media or items[].media, chapters as a list. In Astro a scene gets a client directive so it runs while scrolling.

StickyStack, variant processView in Components
import { StickyStack } from '@/components/ui/sticky-stack';
import { PinnedMedia } from '@/components/ui/pinned-media';

<StickyStack
  variant="process"
  title="From the first call to the launch"
  items={[
    { eyebrow: 'Week 1', title: 'Workshop', text: 'We sort goals, pages and content in one afternoon.' },
    { eyebrow: 'Week 2', title: 'Design', text: 'Three screens in your colors, ready to click.' },
    { eyebrow: 'Week 4', title: 'Launch', text: 'Live on your domain, with analytics and a handover.' },
  ]}
/>

<PinnedMedia
  variant="pan-zoom"
  title="A walk through the bakery"
  media={<img src="/bakery-plan.avif" alt="Floor plan of Lindner Bakery" width={1600} height={1000} />}
  chapters={[
    { title: 'The oven', text: 'Stone oven, fired at four every morning.', focus: { x: 0.28, y: 0.4, zoom: 1.8 } },
    { title: 'The counter', text: 'Twenty kinds of bread, sliced while you wait.', focus: { x: 0.7, y: 0.62, zoom: 1.5 } },
  ]}
/>
src/pages/index.astro
<!-- Without a directive the finished, calm layout shows -->
<StickyStack variant="process" items={items} />

<!-- With a directive the scene runs while scrolling -->
<PinnedMedia client:visible variant="pan-zoom" chapters={chapters} media={media} />

In the preview#

On the page of a category the preview of a scene gets a fixed window height and scrolls inside itself, so stacks, chapters and zooms run exactly as on your page. Scroll with the mouse or trackpad over the preview to see them.

A preview spots scenes by data-kit-viewport. Scenes pass every extra attribute on to their outer element, so you can mark your own demos too.

Reduced motion and no JavaScript#

Every scene renders its finished layout on the server. Without JavaScript and with reduced motion it stays that way, every chapter, picture and number stands in order and reads completely. What would only make sense through motion gets a calm form of its own there, such as a row you swipe yourself instead of the guided ride.

On phones many scenes have a layout of their own, such as a swipe row instead of the sideways ride or a timeline that runs down. Links in late chapters stay in the tab order, and scenes with a pinned track scroll to the matching spot.

Performance#

  • Progress runs as the CSS variable --p, not through React state. A scroll frame triggers no render.
  • Only transform, opacity and clip-path move, never anything that shifts the layout.
  • Heights of pinned tracks are measured on start and on resize, never while scrolling.
  • Scenes with drawn pictures keep the variable on small elements without pictures, so the picture is not repainted on every frame.
  • Give pictures a fixed width and height and a light format such as AVIF, a pinned scene shows them large and for long.

Your own scene#

Every scene shares scroll-scene. The same helpers carry a small scene of your own.

src/components/Statement.tsx
import { useLive, useSceneProgress, seg } from '@/components/ui/scroll-scene';

export function Statement() {
  // Runs only in the browser, from tablet up and without reduced motion
  const live = useLive('(min-width: 768px)');
  // Writes the progress from 0 to 1 as --p onto the element
  const ref = useSceneProgress<HTMLElement>(live, { mode: 'enter' });
  return (
    <section ref={ref} className="py-24">
      <h2 style={{ opacity: `calc(0.25 + 0.75 * ${seg(0, 0.7)})`, transform: `translateY(calc((1 - ${seg(0, 0.7)}) * 24px))` }}>
        Bread takes time. So do we.
      </h2>
    </section>
  );
}
HelperReturnsWhat it does
useLive(query?)booleanTrue once the scene may run, in the browser, with motion allowed and the media query matching.
useSceneProgress(live, opts)RefWrites the progress into a CSS variable on every scroll frame, without a React render. Modes 'pin', 'pass' and 'enter'.
seg(a, b)stringCSS expression that runs from 0 to 1 while the progress runs from a to b. Finished without JavaScript.
span, smooth, lerpnumberThe same in JavaScript, for scenes that write transforms directly.
scrollToProgress(track, p)voidBrings a pinned track to a progress, for links reached with the Tab key.

The ground rules for motion in the kit are under Motion.

Often searched

Jump to

Copied to the clipboard