Sticky Scroll

Steps that scroll past a pinned picture, which changes in place to match the step being read.

Examples

Steps with a pinned picture

A day out with Fernpath

Four steps, one picture each

  1. Plan the route at home

    Draw the walk on the map, see the climbs ahead of time and the places to refill water on the way.
  2. Keep the map offline

    The route and twenty kilometres around it stay on the phone, for the valleys where the signal drops.
  3. Walk without looking down

    A short vibration before every turn, so the phone can stay in a pocket for most of the day.
  4. Share the day

    Distance, time and the three photos you starred, in one card your friends can open without an account.

Stacked sections

06:30

Set up

Traders unload on the closed street. Each pitch is chalked on the ground with its number, and the covered stalls go up first.

09:00

Doors open

The welcome desk hands out coffee and the printed map. Most questions are about the bakery, which sells out before eleven.

14:00

Pack up

Stalls come down in reverse order. By three the street is swept and the tables are back in the hall for next Saturday.

See you next Saturday

Installation

npx shadcn@latest add https://ui.ndekocode.dev/r/motion-sticky-scroll.json

Component API

StickyScroll

The step that crosses the middle of the scrolling area is the active one. The picture of the active step fades in over the previous one, without sliding. The pinned panel repeats the pictures of the steps for sighted readers, so it is hidden from screen readers; below 36rem of width, each step shows its own picture above its text instead.

PropTypeDefaultDescription
stepsStickyScrollStep[]The steps, each with an id, a title, a description and its media.
containerRefRefObject<HTMLElement | null>The scrolling element, when it is not the page.
mediaSide'start' | 'end''end'The side of the pinned picture on wide layouts.
frameClassNamestringh-svhClasses of the pinned panel. Its height should match the height of the scrolling area.
stepClassNamestringmin-h-[70svh]Classes of each step. Taller steps keep each picture on screen for longer.
classNamestringundefinedOptional CSS class for the root element.

StickySections

A wrapper for stacked sections. The sections stay pinned until the end of the wrapper, then leave together.

PropTypeDefaultDescription
classNamestringundefinedOptional CSS class for the wrapper.

StickySection

A section pinned to the top of the scrolling area. Each section covers the one before it as it arrives; the scroll itself is the only movement.

PropTypeDefaultDescription
classNamestringh-svhClasses of the section, merged over sticky top-0 flex flex-col.