Scroll

Scroll Reveal Section

A section whose blocks arrive as the reader scrolls to them — no JavaScript.

Built for the scroll
Each block arrives as it reaches the viewport.
9
Elements
6
Groups
0
Hover states
1220ms
Duration

Why it is timed this way

The reasoning

Driven by animation-timeline: view() rather than a timer, so the motion follows the reader instead of firing once on load and being missed. The whole scroll-reveal category of JavaScript libraries exists to do this; CSS now does it natively, with no observer and no scroll listener. Browsers without it play the same animation on load, which is why the fallback matters more than the effect — a scroll-driven fade with no fallback leaves the content invisible.

9 elements move, the first at 0ms and the last starting at 320ms — the whole scene settles by 1220ms. Every bar below is one element's animation window.

Section Heading
Section Sub
Row 1 Mark
Row 1 Line
Row 2 Mark
Aside Panel
Row 2 Line
Row 3 Mark
Row 3 Line
0ms610ms1220ms

The code

Everything the scene needs, in whichever format you ship. Entrances compile to@keyframesand hover states totransition, with a reduced-motion guard on both.

386 lines
.scroll-section {
  transform: translate3d(0px, 0px, 0);
  width: 960px;
  height: 480px;
  transform-origin: 480px 184px;
}

.section-copy {
  transform: translate3d(0px, 0px, 0);
  height: 228px;
  transform-origin: 296px 114px;
}

.aside {
  transform: translate3d(0px, 0px, 0);
  width: 232px;
  height: 272px;
  transform-origin: 116px 136px;
}

.row-1 {
  transform: translate3d(0px, 0px, 0);
  height: 32px;
  background: #171a24;
  border-radius: 18px;
  transform-origin: 45px 36px;
}

.row-2 {
  transform: translate3d(0px, 0px, 0);
  height: 32px;
  background: #171a24;
  border-radius: 18px;
  transform-origin: 45px 36px;
}

.row-3 {
  transform: translate3d(0px, 0px, 0);
  height: 32px;
  background: #171a24;
  border-radius: 18px;
  transform-origin: 45px 36px;
}

.section-heading {
  transform: translate3d(0px, 0px, 0);
  height: 44px;
  background: #00000000;
  color: #eceef4;
  font-size: 36px;
  font-weight: 800;
  animation: section-heading-anim 1220ms linear 0ms 1 both;
  will-change: transform, opacity;
}

@keyframes section-heading-anim {
  0% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
    animation-timing-function: ease-out;
  }
  57.38% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
  100% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
}

@supports (animation-timeline: view()) {
  .section-heading {
    animation-timeline: view();
    animation-range: entry 0% cover 40%;
  }
}

.section-sub {
  transform: translate3d(0px, 0px, 0);
  height: 24px;
  background: #00000000;
  color: #98a1b5;
  font-size: 17px;
  font-weight: 500;
  animation: section-sub-anim 1220ms linear 0ms 1 both;
  will-change: transform, opacity;
}

@keyframes section-sub-anim {
  0% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
  }
  7.38% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
    animation-timing-function: ease-out;
  }
  64.75% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
  100% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
}

@supports (animation-timeline: view()) {
  .section-sub {
    animation-timeline: view();
    animation-range: entry 0% cover 40%;
  }
}

.row-1-mark {
  transform: translate3d(0px, 0px, 0);
  width: 32px;
  height: 32px;
  background: #6366f1;
  border-radius: 10px;
  animation: row-1-mark-anim 1220ms linear 0ms 1 both;
  will-change: transform, opacity;
}

@keyframes row-1-mark-anim {
  0% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
  }
  9.84% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
    animation-timing-function: ease-out;
  }
  67.21% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
  100% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
}

@supports (animation-timeline: view()) {
  .row-1-mark {
    animation-timeline: view();
    animation-range: entry 0% cover 40%;
  }
}

.row-1-line {
  transform: translate3d(0px, 0px, 0);
  height: 10px;
  background: #272c3a;
  border-radius: 999px;
  animation: row-1-line-anim 1220ms linear 0ms 1 both;
  will-change: transform, opacity;
}

@keyframes row-1-line-anim {
  0% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
  }
  13.11% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
    animation-timing-function: ease-out;
  }
  70.49% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
  100% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
}

@supports (animation-timeline: view()) {
  .row-1-line {
    animation-timeline: view();
    animation-range: entry 0% cover 40%;
  }
}

.row-2-mark {
  transform: translate3d(0px, 0px, 0);
  width: 32px;
  height: 32px;
  background: #22d3ee;
  border-radius: 10px;
  animation: row-2-mark-anim 1220ms linear 0ms 1 both;
  will-change: transform, opacity;
}

@keyframes row-2-mark-anim {
  0% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
  }
  16.39% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
    animation-timing-function: ease-out;
  }
  73.77% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
  100% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
}

@supports (animation-timeline: view()) {
  .row-2-mark {
    animation-timeline: view();
    animation-range: entry 0% cover 40%;
  }
}

.row-2-line {
  transform: translate3d(0px, 0px, 0);
  height: 10px;
  background: #272c3a;
  border-radius: 999px;
  animation: row-2-line-anim 1220ms linear 0ms 1 both;
  will-change: transform, opacity;
}

@keyframes row-2-line-anim {
  0% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
  }
  19.67% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
    animation-timing-function: ease-out;
  }
  77.05% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
  100% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
}

@supports (animation-timeline: view()) {
  .row-2-line {
    animation-timeline: view();
    animation-range: entry 0% cover 40%;
  }
}

.row-3-mark {
  transform: translate3d(0px, 0px, 0);
  width: 32px;
  height: 32px;
  background: #6366f1;
  border-radius: 10px;
  animation: row-3-mark-anim 1220ms linear 0ms 1 both;
  will-change: transform, opacity;
}

@keyframes row-3-mark-anim {
  0% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
  }
  22.95% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
    animation-timing-function: ease-out;
  }
  80.33% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
  100% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
}

@supports (animation-timeline: view()) {
  .row-3-mark {
    animation-timeline: view();
    animation-range: entry 0% cover 40%;
  }
}

.row-3-line {
  transform: translate3d(0px, 0px, 0);
  height: 10px;
  background: #272c3a;
  border-radius: 999px;
  animation: row-3-line-anim 1220ms linear 0ms 1 both;
  will-change: transform, opacity;
}

@keyframes row-3-line-anim {
  0% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
  }
  26.23% {
    transform: translate3d(0px, 48px, 0);
    opacity: 0;
    animation-timing-function: ease-out;
  }
  83.61% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
  100% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
}

@supports (animation-timeline: view()) {
  .row-3-line {
    animation-timeline: view();
    animation-range: entry 0% cover 40%;
  }
}

.aside-panel {
  transform: translate3d(0px, 0px, 0);
  width: 232px;
  height: 272px;
  background: #1f2331;
  border-radius: 18px;
  animation: aside-panel-anim 1220ms linear 0ms 1 both;
  will-change: transform, opacity;
}

@keyframes aside-panel-anim {
  0% {
    transform: translate3d(64px, 0px, 0);
    opacity: 0;
  }
  16.39% {
    transform: translate3d(64px, 0px, 0);
    opacity: 0;
    animation-timing-function: ease-out;
  }
  73.77% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
  100% {
    transform: translate3d(0px, 0px, 0);
    opacity: 1;
  }
}

@supports (animation-timeline: view()) {
  .aside-panel {
    animation-timeline: view();
    animation-range: entry 0% cover 40%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .section-heading,
  .section-sub,
  .row-1-mark,
  .row-1-line,
  .row-2-mark,
  .row-2-line,
  .row-3-mark,
  .row-3-line,
  .aside-panel {
    animation: none;
    transition: none;
  }
}

More scenes

Now in beta
Ship motion
that feels right.
Design it, tune it, export the CSS.

Launch Hero

A product hero whose copy, buttons and panel arrive in sequence.

Starter
$0
Pro
$12
Team
$40

Three-Plan Pricing

Three plans that pop in, with the featured column raised and hover lifts on every card.

Canvas
Timeline
Easing
States
Export
Import

Feature Tile Grid

Six tiles arriving as a wave across the grid, each lifting on hover.