Scroll triggers, clock plays

Let scroll only trigger a reveal, then play it on a fixed duration.

From design-motion-principles, cookbook §14. See @jh3yy on X ↗.

Scroll it yourself

Scroll slowly, stop halfway, then scroll back up.

Scrubbed by scrollBad

Revenue by month

Every figure opens its own ledger lines.

animation-timeline: view()0%
Triggered, then 250msGood

Revenue by month

Every figure opens its own ledger lines.

style(--revealed: 1)–

Same scroll, any speed

Set a speed, press Play, and compare the timings.

Scrubbed by scrollBad

Close checklist

Seven sections, one owner each.

animation-range: entry 0% cover 50%
Triggered, fixed timeGood

Close checklist

Seven sections, one owner each.

animation: reveal 250ms
Measured reveal time per run
Scroll speedScrubbed cardTriggered card
Runs appear here.
@keyframes trigger { 0% { --revealed: 0; } 1%, 100% { --revealed: 1; } }
.feature {
  animation: trigger linear both;
  animation-timeline: view();
  animation-range: entry 50% entry 100%;
}
@container style(--revealed: 1) {
  .feature > .card { animation: reveal 250ms var(--ease-out) both; }
}

Fallback: IntersectionObserver

Simulate no support, then scroll both panes.

Scroll timelineNative

Filing calendar

Every deadline, graded by urgency.

view()–
IntersectionObserverFallback

Filing calendar

Every deadline, graded by urgency.

IntersectionObserver–

Why it matters, pros and cons

Why it matters

A reveal is an event, not a slider: scroll picks when, the designer how long.

Pros

  • Same timing at every scroll speed
  • No half-faded stalls mid-scroll
  • Trigger stays in CSS, off the main thread

Cons

  • Scrub when motion is the content, like progress bars
  • Style queries are recent; ship the fallback
  • Most content should simply be there