Reviving Parallax with Scroll-Driven CSS Animations

In the 2010s, parallax was a popular way to make websites look “cool,” with Chris Coyier discussing it as early as 2008. Parallax involves different webpage elements moving at varying speeds during scrolling, creating a 3D effect. Originally, it required JavaScript, but now CSS-only solutions are available through scroll-driven animations, avoiding JavaScript’s main-thread blocking.

Despite becoming cliché, revisiting parallax with new CSS features is worthwhile. Note that scroll-driven animations are supported in Chrome, Edge, Opera, and Firefox (with a feature flag) at the time of writing.

**Starting code**: We’ll apply parallax animations to a universe-themed webpage’s background and icons in three “hero” sections. We’ll use lightly styled markup with alternating hero and text sections and placeholder space-related content.

**Adding initial animations**: Add an animation to the background pattern in each hero section to change the background position using `keyframes` and the `animation` property. Initially, animations run for three seconds upon page load. We aim to tie animation progress to the page’s scroll position.

Scroll-driven animations introduce `view()` and `scroll()` functions to calculate CSS animation progress. We’ll focus on `scroll()`, linking animation progression to the user’s scroll position within a container. Parameters can adjust the scroll axis and container element but aren’t needed here.

Use a scroll progress timeline for animation:

“`css
section.hero {
animation: parallax linear;
animation-timeline: scroll();
}
“`

Upon refreshing, the background of each hero section changes as you scroll, reversing when scrolling up. This CSS animation operates off the main thread, avoiding JavaScript blocking.

**Using the view progress timeline**: Add a new parallax layer by animating the header text and icons in each hero section. Initially, use the `scroll()` function for the animation timeline.

“`css
.hero-content {
position: absolute;
top: 25%;
animation: float linear;
animation-timeline: scroll();
}
“`

The animation for sections further down is nearly complete by the time they appear. The view animation timeline solves this by setting `animation-timeline` to `view()`, basing animation progress on the subject’s position in the scrollport. Scrolling in reverse also reverses the animation.

Change the animation timeline property for hero text:

“`css
.hero-content {
animation-timeline: view();
}
“`

This looks good, but header content flashes into view when scrolling back up. The view timeline is based on the original position of the subject element.

To solve this, add an `inset` parameter to `view()`, adjusting the container size for animation. A negative value makes the container larger than the window, triggering animation to start before and end after the subject is visible.

“`css
animation-timeline: view(-100px);
“`

Now, text and background animate smoothly at different speeds.

**Adjusting animations using animation ranges**: We’ve used `scroll` and `view` progress timelines. Another way to adjust animation timing is using `animation-range`, modifying where the animation starts and ends along the timeline.

Add a `view()` timeline animation to the `#spaceship` emoji:

“`css
#spaceship {
animation: launch;
animation-timeline: view();
}
“`

The emoji returns to its `0%` position once its original position is outside the scrollport.

Animations are based on the original position of the subject. Previously, we solved this with an inset parameter in `view()`. We can also adjust the animation range to continue beyond 100% of the timeline without manipulating the view timeline inset.

“`css
#spaceship {
animation-range: 0% 120%;
}
“`

The animation continues until an extra 20% beyond the scroll timeline’s normal endpoint.

To add an animation to the `#comet` emoji that starts after passing `4rem` from the bottom of the scrollport:

“`css
#comet {
animation: rotate linear;
transform-origin: center 125px;
animation-timeline: view();
animation-range: 4rem 120%;
}
“`

This “delayed” animation is shown in action.

Combine animation ranges for different animations at different timeline points. For the `#satellite` icon, the first animation runs until 80% of the scrollport, then the second animation takes over for the last 20%.

“`css
#satellite {
animation: orbit-in linear, orbit-out ease;
animation-timeline: view();
animation-range: 0% 80%, 80% 110%;
}
“`

**Fallbacks and accessibility**: Consider accessibility for motion sensitivities using the `prefers-reduced-motion` CSS media feature. Values are `no-preference` and `reduce`. To disable animations by default and enhance selectors


Discover more from WIREDGORILLA

Subscribe to get the latest posts sent to your email.

Similar Posts