About this snippet

A landing page hero with a sun, two ranges of hills and a headline. Scroll down and the layers separate. The sun and far hills drift slowly, the near hills move faster, and the title fades and lifts away before the content section takes over.

Each layer has a normal keyframe animation, but instead of running on a clock it is bound to the page scroll with animation-timeline set to scroll on the root. animation-range limits the effect to the first screen of scrolling, so the layers settle once the hero has gone. The differing distances in the keyframes create the depth.

Scroll-driven animations are in Chrome, Edge and Safari 26 and sit on the Interop 2026 list, with Firefox still behind a flag. The @supports rule only enables them where the timeline exists, so elsewhere the hero is a static illustration.

Size
3.4 KB
Uses
HTML, CSS
Loads
Nothing external
Views
98
<header class="hero">
  <div class="sun"></div>
  <div class="hills far"></div>
  <div class="hills near"></div>
  <div class="title">
    <p class="eyebrow">Trail season 2026</p>
    <h1>Walk further than you think you can</h1>
    <p>Routes, maps and gear notes from people who spend their weekends above the tree line.</p>
  </div>
</header>
<main class="content">
  <h2>Scroll to see the layers separate</h2>
  <p>The sun and the far hills barely move, the near hills slide faster and the title lifts away. All of it is CSS bound to the scroll position rather than a script watching scroll events.</p>
  <p>Once the hero has left the viewport the animations sit at their end state, so nothing keeps running while you read.</p>
  <p>Resize the preview or scroll back up and the layers return to where they started, because the animation is a function of scroll position rather than time.</p>
</main>
*, *::before, *::after {
  box-sizing: border-box;
}

body {
  font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  margin: 0;
  background: #0b1120;
  color: #f8fafc;
}

.hero {
  position: relative;
  height: 100vh;
  overflow: hidden;
  background: linear-gradient(180deg, #1e1b4b 0%, #4c1d95 45%, #db2777 100%);
}

.sun {
  position: absolute;
  left: 50%;
  top: 8%;
  width: 150px;
  height: 150px;
  margin-left: -75px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 40%, #fde68a, #f97316 70%);
  box-shadow: 0 0 80px 30px rgba(249, 115, 22, 0.4);
}

.hills {
  position: absolute;
  left: -10%;
  right: -10%;
  bottom: -40px;
  height: 46%;
}

.hills::before,
.hills::after {
  content: "";
  position: absolute;
  bottom: 0;
  border-radius: 50% 50% 0 0;
}

.far {
  height: 55%;
}

.far::before {
  left: 0;
  width: 60%;
  height: 100%;
  background: #6d28d9;
}

.far::after {
  right: 0;
  width: 55%;
  height: 80%;
  background: #5b21b6;
}

.near::before {
  left: -5%;
  width: 55%;
  height: 100%;
  background: #1e1b4b;
}

.near::after {
  right: -5%;
  width: 65%;
  height: 90%;
  background: #172554;
}

.title {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(90%, 620px);
  text-align: center;
}

.eyebrow {
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #fde68a;
  margin: 0 0 12px;
}

.title h1 {
  font-size: clamp(2rem, 6vw, 3.5rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 0 16px;
}

.title p:last-child {
  margin: 0;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.85);
}

.content {
  position: relative;
  background: #0b1120;
  padding: 60px 24px 120px;
  max-width: 640px;
  margin: 0 auto;
}

.content h2 {
  font-size: 1.5rem;
  margin: 0 0 16px;
}

.content p {
  color: #cbd5e1;
  line-height: 1.7;
  margin: 0 0 18px;
}

@keyframes drift-sun {
  to { transform: translateY(50px) scale(0.9); }
}

@keyframes drift-far {
  to { transform: translateY(40px); }
}

@keyframes drift-near {
  to { transform: translateY(-140px); }
}

@keyframes lift-title {
  to { transform: translate(-50%, -140%); opacity: 0; }
}

@supports (animation-timeline: scroll()) {
  .sun, .far, .near, .title {
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: scroll(root);
    animation-range: 0 90vh;
  }

  .sun { animation-name: drift-sun; }
  .far { animation-name: drift-far; }
  .near { animation-name: drift-near; }
  .title { animation-name: lift-title; }
}
Esc
↑↓ moveEnter select

Start from a template

Open an .html file, or HTML, CSS, JS, Markdown, Less or TypeScript files. You can also drop them here. Styles and scripts go into their panes.

Share

Source

Keyboard shortcuts

Save