v144 · CSS Animations

Parallax Sections

Two scroll-position systems working together: animation-timeline: scroll() drives a continuous parallax background, while a named timeline-trigger plus animation-trigger plays title entrances as sections cross view ranges. Use the scrubber to drive the same scroll input deliberately.

Feature detection: checking…
Continuous vs discrete: scroll-driven animations (animation-timeline: scroll()) progress proportionally with scroll position — like a timeline scrubber. Scroll-triggered animations create a named trigger from a scroll or view timeline, then bind animations to that trigger — like a declarative threshold crossing. They're complementary: use scroll-driven for parallax, progress bars, and scrubbed motion; use scroll-triggered for entrance effects and reveal state.
trigger: IO fallback scroll-driven: CSS
0%
active sectionSection 1
scrollTop0 px
trigger pathdetecting
/* ── Scroll-DRIVEN parallax (continuous) ──────────────────── */
.parallax-bg {
  animation-name: parallax-move;
  animation-timeline: scroll(nearest);  /* progress = scroll position */
  animation-duration: 1s;               /* irrelevant; timeline drives it */
  animation-timing-function: linear;
  animation-fill-mode: both;
}

@keyframes parallax-move {
  from { transform: translateY(-30px); }
  to   { transform: translateY(30px); }
}

/* ── Scroll-TRIGGERED title (discrete) ────────────────────── */
.section-title {
  animation-name: title-enter;
  animation-duration: 0.6s;
  animation-fill-mode: both;

  /* Chrome 144+ — named trigger sourced from the element's view timeline */
  timeline-trigger:
    --section-title
    view()
    contain 15% contain 85% / entry 100% exit 0%;
  trigger-scope: --section-title;
  animation-trigger: --section-title play-forwards play-backwards;
}

@keyframes title-enter {
  from { transform: translateY(30px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}

see also