Data-viz / scrollytelling · 12.1
Sticky-graphic scrollytelling
A museum exhibit where the placard moves and the artifact stays.
A graphic pins while step text scrolls past it, updating the graphic's state per step, then un-pins. The canonical data-storytelling pattern.
4 knobs
How it actually works
Every scrollytelling piece you have ever read is this. It is also the pattern most often built wrong, by attaching a scroll listener and recomputing every element's position on every scroll event. The Pudding rejected that in writing: scroll listeners "inhibit performance and are janky". That is the same conclusion our House Standard reached from measuring frames, arrived at from the other direction.
Plain CSS position: sticky holds the graphic. No JavaScript tracks dimensions, which is the Pudding's own emphasis. A light intersection trigger fires a callback only for state CHANGES: step 3 entered, step 2 re-entered. Scrollama, which the Pudding says is interchangeable with its own enter-view.js, is a thin wrapper over native IntersectionObserver that fires enter, exit, and 0 to 100% progress, deliberately decoupled from rendering. The offset decides where on screen a step "counts" — in raw IO that is a rootMargin, and turning the knob here rebuilds the observer.
The knobs, named
Step count, trigger offset, progress granularity, interpolation. The offset is drawn on the stage as a dashed line: drag the knob, watch the line move, watch the steps fire early or late against it.
| Knob | Source | What it teaches |
|---|---|---|
| Step count | sourced | How many boundaries the story has. Each step owns a target state for the graphic; scroll decides which one is current. |
| Trigger offset | sourced | Where in the frame a step counts as entered, as a fraction from the top. This is scrollama's offset and it is a rootMargin underneath. |
| Progress granularity | sourced | Step mode changes state only at boundaries. Progress mode also reports how far through the current step you are, and interpolates between the two states. |
| Interpolation | ours | How the graphic travels between two step states. Under step granularity this does nothing, which is itself the demonstration. |
sourced means the source names this parameter. ours means the source names none and the knob is our design against the mechanism. No knob here is invented and passed off as sourced.
Evidence
VERIFIED (author, primary source)
pudding.cool/process/scrollytelling-sticky/ and /process/introducing-scrollama/. The offset, the progress events, and the choice of IntersectionObserver over scroll listeners are all sourced verbatim. The Pudding also documents the tradeoff map across six approaches; graph-scroll.js is the only one built specifically around this transition.
- Seen on
- pudding.cool. The awwwards form pairs scrollama with d3.js for the chart draw.
- Dependencies
- vanilla, with raw IntersectionObserver. scrollama.js if you want the ergonomics.
- Difficulty
- moderate — the CSS is trivial; the step-boundary logic and the mobile reflow are the cost
- Performance
- Chosen explicitly over scroll listeners, in the source's own words, because those "inhibit performance and are janky". One observer, one property write per step change.
- Accessibility and the floor
- Breaks entirely without JS unless you render a static first frame, so ours renders every step's text and the graphic at its final state. A chart of circles conveys nothing to a screen reader: the step text carries the argument here, and it is real text, always present, never revealed.
Notes
Composability. The whole point is that the graphic is arbitrary. Put the funnel (12.2), the chart (12.3) or the odometer (12.4) in the sticky slot and the pattern does not change.