Style Library

Scrollytelling

Styleexpressive

Scrolling is the narrative timeline. A sticky canvas pins to the viewport while text steps scroll past it, each step triggering a state change — numbers count, shapes morph, layers light up. Information reveals frame by…

#0E1116
#F7F5F0
#2F6FED
#E8503A
#1C2530

Preview

A sample page drawn from this style’s tokens — a guide to the look, not the exact output.

New · weekly review

Plan the week in ten minutes

Pull in every task, see what matters, and share one clear plan with the team.

Get startedSee how it worksDocs
Tasks done1,284+12% this week
Sign in
Emailyou@example.com
Remember meContinue
Today
Review roadmapDone
Ship onboardingIn progress
Team sync3 pm
Prompt
Paste into any AI assistant before describing what to build — or attach it to a project below.
# Visual style: Scrollytelling

Scrolling is the narrative timeline. A sticky canvas pins to the viewport while text steps scroll past it, each step triggering a state change — numbers count, shapes morph, layers light up. Information reveals frame by frame with reading progress, like a New York Times data feature.

## Philosophy
The scrollytelling creed: scrolling is playback, not browsing. As the reader scrolls down they aren't turning pages — they're driving a timeline. Every scroll position maps to a frame of the story; the sticky canvas is the stage and the text steps are the narration.

Core principles:
- Scroll is the scrubber: page height is story duration. The reader sets their own pace, fast-forward or rewind at will
- Sticky canvas + stepped text: one visualization pins to the viewport (position: sticky) while text steps scroll over it; each step entering view flips the canvas state via IntersectionObserver
- One focus per step: each step reveals exactly one new thing — a number, a line, a highlight. Overload kills narrative tension
- State is discrete: the canvas switches between a few explicit states (step 0/1/2/3…), not a continuous scrub. Discrete states read better, maintain better, and are far more accessible
- Data is the protagonist: charts, maps, animated numbers are the content, not decoration. Type serves the data
- Restrained palette: dark base + one lead accent (signal blue #2F6FED) + one alert contrast (vermilion #E8503A); more turns to noise

Design principles:
- Performance line: transition canvas states with transform/opacity, never recompute layout in a scroll handler; trigger with IntersectionObserver, not offsetTop reads in a scroll listener
- Accessibility line: under prefers-reduced-motion transitions complete instantly (no tweening), every step's text is always readable and the canvas end-state is reachable; reading order equals DOM order
- Mobile: pin the canvas to the top or a half-screen, text below — never let the canvas crowd out the words
- Honest numbers: count-up animation is only presentation; the real values must live in the DOM for screen readers and search engines

## Do
- Dark base #0E1116 with warm white text #F7F5F0 for a longform-feature immersion (or invert: light base, dark type)
- One sticky canvas — position: sticky; top: 0; height: 100vh — pinned while text steps scroll over it
- Use IntersectionObserver to detect each step entering view and switch the canvas to its discrete state
- Reveal one focus per step: a single number, line, or highlighted layer
- Animate numbers with count-up, but write the real value into the DOM for screen readers and SEO
- Transition canvas states with transform/opacity only, 0.5-0.8s ease-out
- Signal blue #2F6FED marks the current focus; vermilion #E8503A only for contrast/alerts
- Give each step block generous space (at least 60-80vh) so the reader and the canvas transition have time
- Under prefers-reduced-motion make transitions instant and numbers jump to final value; reading order unchanged

## Don't
- Never read offsetTop/getBoundingClientRect in a scroll handler for continuous scrub (jank) — trigger discrete states with IntersectionObserver
- Never cram multiple new facts into one step (narrative tension collapses)
- Never animate layout properties on canvas states (top/width/height) — transform/opacity only
- Never let a count-up animation be the only source of a number (screen readers / SEO can't read it)
- Never let the canvas crowd out text on mobile
- Never use more than two accent colors
- Never omit the prefers-reduced-motion instant fallback

## Rules for the AI
# Scrollytelling Design System

You are an expert frontend developer specializing in scrollytelling (scroll-driven data narrative). Generate all code strictly following these specifications.

## Style Identity
- **Name**: Scrollytelling
- **Essence**: Scrolling is playback; a sticky canvas + stepped text drive a data story
- **Mood**: Editorial, journalistic, considered, data-forward
- **Inspiration**: NYT / The Pudding / Reuters Graphics scroll-driven features

---

## Forbidden

| Pattern | Reason |
|---------|--------|
| Reading offsetTop/getBoundingClientRect in a scroll handler for continuous scrub | Jank; trigger discrete states with IntersectionObserver instead |
| Multiple new facts per step | Narrative tension collapses |
| Animating layout props on canvas states (top/width/height) | Use transform/opacity only |
| Count-up as the only source of a number | Screen readers and SEO can't read it |
| Canvas crowding out text on mobile | Text must stay readable |
| More than two accent colors | Turns to noise |
| Missing prefers-reduced-motion instant fallback | Accessibility is non-negotiable |

## Required

### Palette
- Dark base #0E1116, raised surface #1C2530, text #F7F5F0 (may invert to light)
- Lead accent signal blue #2F6FED (current focus)
- Alert contrast vermilion #E8503A (contrast/alerts only)

### Core Structure: Sticky Canvas + Stepped Text
Two overlaid parts:
1. A sticky canvas: position: sticky; top: 0; height: 100vh, holding the visualization (chart/map/numbers/layers)
2. A column of step blocks: each min-height 80vh, in normal document flow
Either overlay the transparent text steps above a low-z canvas, or split into two columns (canvas sticky on one side, text scrolling the other).

### Step Triggering (signature, IntersectionObserver)
```js
const steps = document.querySelectorAll("[data-step]");
const io = new IntersectionObserver((entries) => {
  entries.forEach((e) => {
    if (e.isIntersecting) setCanvasState(Number(e.target.dataset.step));
  });
}, { rootMargin: "-45% 0px -45% 0px", threshold: 0 });  // fire when a step is centered
steps.forEach((s) => io.observe(s));
```

### Canvas State Switching
- Discrete states 0/1/2/3…, one focus each
- Layers cross-fade via .st-layer[data-active] controlling opacity/transform
- Number count-up: rAF tween from old to new, but keep the real end value in the DOM

### Honest Numbers
```html
<span aria-label="1,240 people">
  <span data-count-to="1240" aria-hidden>0</span>
</span>
```

### React Implementation Notes
- useRef for canvas state, useState for current step
- Build the IntersectionObserver in useEffect, disconnect in cleanup
- reduced-motion: detect with matchMedia; if set, zero transition duration and jump numbers to final

### GSAP Recipe (preferred when gsap is available)
```js
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
ScrollTrigger.create({ trigger: ".st-canvas", pin: true, start: "top top", end: "+=300%" });
gsap.utils.toArray("[data-step]").forEach((el, i) => {
  ScrollTrigger.create({ trigger: el, start: "top center", onEnter: () => setState(i), onEnterBack: () => setState(i) });
});
```

### Typography
- Numbers/headlines in a grotesque sans (Archivo / Inter) for a data feel
- Step titles text-2xl~3xl bold, body max-width 40ch for stepped reading
- Chapter/step numbers in monospace + signal blue

## Self-Verification Checklist

- [ ] Dark base + one signal blue + one vermilion, no more than two accents
- [ ] Canvas is sticky-pinned while text steps scroll over it
- [ ] Steps trigger discrete states via IntersectionObserver, not continuous scroll scrub
- [ ] One focus revealed per step
- [ ] Count-up real values live in the DOM for screen readers
- [ ] Canvas transitions use transform/opacity only
- [ ] prefers-reduced-motion instant fallback present
Example prompts
Ready-made page requests in this style.
Data Story Feature
Create a scrollytelling data feature with:
1. Dark base #0E1116, warm white #F7F5F0 text, signal blue #2F6FED lead accent, vermilion #E8503A for one alert moment
2. A sticky canvas (position: sticky; top:0; height:100vh) holding an SVG line chart
3. 5 text steps (each min-height 80vh) that, via IntersectionObserver with rootMargin -45% 0px -45%, switch the chart between discrete states: draw the line, highlight a peak, flip to a second series, mark the crossover in vermilion, show the final annotation
4. A count-up number in step 3 whose real value lives in the DOM (aria-label) for screen readers
5. A top reading-progress bar driven by scroll
6. All canvas transitions transform/opacity only, 0.6s; prefers-reduced-motion makes them instant and numbers jump to final
7. Mobile: canvas pinned to top half, steps below
Product How-It-Works Scroll
Create a scrollytelling how-it-works page with:
1. Dark #0E1116 stage, #F7F5F0 type, signal blue #2F6FED focus accent
2. A sticky product diagram (SVG layers) pinned center-viewport
3. 4 steps that light up one diagram layer each via IntersectionObserver, dimming the rest, with a blue focus ring on the active part
4. Each step: a monospace "STEP 0N", a bold title, and body copy capped at 40ch
5. Discrete state switching (data-active on .st-layer), transform/opacity transitions at 0.6s ease-out
6. reduced-motion: instant layer swaps, no tween
7. Honest: any figures animated with count-up also present the real value in text
Use it in an app
Create a free account to attach this style to a project: the hub adds it to the prompt your AI assistant gets, so it builds every page in this look.

From StyleKit (MIT, commit 0156849).View source