The other day I saw “Alternate Column Scroll” by Manoela Ilic float by. It’s a tutorial/demo in which some of the content columns scroll in the opposite direction, powered by Locomotive Scroll.
Based on Manoela’s demo I recreated the scrolling part with CSS @scroll-timeline
.
~
Table of Contents
~
# The Basic Structure
I based myself upon Manoela’s original markup and start with one .columns
wrapper containing three nested .column
elements. The columns that should reverse scroll, get the class .column-reverse
added as well.
<div class="columns">
<div class="column column-reverse">…</div>
<div class="column">…</div>
<div class="column column-reverse">…</div>
</div>
The basic layout is done using CSS Grid:
/* Three column layout */
.columns {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
Inside each .column
, the contained .column__item
elements are positioned using CSS Flexbox:
.column {
display: flex;
flex-direction: column;
}
💡 We choose flexbox here because we’ll change flex-direction
for the .column-reverse
columns later on.
Combined, the starter template looks like this:
See the Pen Alternate Column Scroll (CSS @scroll-timeline first + JS ScrollTimeline fallback + Polyfill) by Bramus (@bramus) on CodePen.
~
# Implementing Reverse Scrolling using CSS @scroll-timeline
# Adjusting the columns
In browsers that support CSS @scroll-timeline
we shift the .column-reverse
columns up by translating them by -100%
. As they are now entirely off-screen, we counteract that by adding 100vh
. That way their bottom edge touches the bottom edge of the viewport.
/* Shift entire column up, but not so much that it goes out of view */
.column-reverse {
transform: translateY(calc(-100% + 100vh));
}
As we don’t want .columns
to grow by that translation, we also prevent the content from bleeding out of it.
/* As we're about to shift content out of .columns, we need it to hide its overflow */
.columns {
overflow-y: hidden;
}
Finally, as the columns will be scrolling in a reversed direction, we also reverse the order for the items inside each column. That way the first item will be at the bottom of the reversed column.
/* Flip item order in reversed columns */
.column-reverse {
flex-direction: column-reverse;
}
💡 To detect whether a browser supports CSS @scroll-timeline
we use @supports
and feature check on the telltale animation-timeline
CSS Property.
/* Scroll-Timeline Supported, Yay! */
@supports (animation-timeline: works) {
…
}
Browsers that support @scroll-timeline
also support animation-timeline
.
~
# Setting up + Attaching the Animation and Scroll-Timeline
Our ScrollTimeline is the default Scroll-Timeline of scrolling through the document from top to bottom. To set it up we don’t need anything special.
/* Set up scroll-timeline */
@scroll-timeline scroll-in-document {
source: auto; /* Default scroll-timeline: scrolling in the document */
}
The Animation itself starts at the translation we’ve already done (-100% + 100vh
). The end position is a translation in the opposite direction: 100%
down, but minus 100vh
to keep it on-screen.
/* Set up Animation */
@keyframes adjust-position {
/* Start position: shift entire column up, but not so that it goes out of view */
from {
transform: translateY(calc(-100% + 100vh));
}
/* End position: shift entire column down, but not so that it goes out of view */
to {
transform: translateY(calc(100% - 100vh));
}
}
☝️ I know, you can omit the from
here, but I’m including it here for educational purposes.
Using the animation-timeline we finally link up our ScrollTimeline to the .column-reverse
elements:
/* Hook our animation with the timeline to our columns */
.column-reverse {
animation: 1s adjust-position linear forwards;
animation-timeline: scroll-in-document;
}
💭 Curious to learn more about CSS @scroll-timeline
? Go read this article here on bram.us covering @scroll-timeline
in more detail.
~
# All together now
Combined, our demo now looks like this:
See the Pen Reverse-Scrolling Columns with CSS Scroll-Timelinel (+ JS ScrollTimeline Polyfill Fallback) by Bramus (@bramus) on CodePen.
👨🔬 The demo above will only work in Chromium 89+ with the #experimental-web-platform-features
flag enabled through chrome://flags
. That’s because it’s the only browser that supports CSS @scroll-timeline
at the time of writing.
~
# Implementing Reverse Scrolling with JS, for browsers that don’t speak CSS @scroll-timeline
# The Polyfill
In browsers that don’t support CSS @scroll-timeline
, the Scroll-Timeline Polyfill by Robert Flack is loaded and used.
// Polyfill for browsers with no Scroll-Timeline support
import "https://flackr.github.io/scroll-timeline/dist/scroll-timeline.js";
The polyfill will register itself when needed.
🤔 Polyfill?
A polyfill is a piece of code (or plugin) that provides the technology that you, the developer, expect the browser to provide natively — What is a Polyfill?
~
# WAAPI + ScrollTimeline
The animation itself is a regular WAAPI animation and does exactly the same as the CSS Animation described above. The animation is, however, extended with a linked ScrollTimeline
instance the polyfill provides us.
Above that, the entire code is wrapped inside a little snippet that feature detects CSS support for animation-timeline
. When it’s not supported, the contained code will be executed.
// Fallback for browsers that don't support CSS ScrollTimeline
if (!CSS.supports("animation-timeline: foo")) {
// As we're about to shift content out of .columns, we need it to hide its overflow
document.querySelector(".columns").style.overflowY = "hidden";
// Set up ScrollTimeline instance
const timeline = new ScrollTimeline({
scrollSource: document.documentElement,
timeRange: 1,
fill: "both"
});
// Loop all eligible columns
document.querySelectorAll(".column-reverse").forEach(($column) => {
// Flip item order in reverse columns
$column.style.flexDirection = "column-reverse";
// Hook Animation
$column.animate(
{
transform: [
"translateY(calc(-100% + 100vh))",
"translateY(calc(100% - 100vh))"
]
},
{
duration: 1,
fill: "both",
timeline
}
);
});
}
💭 Curious to learn more about WAAPI + ScrollTimeline? Go read this article on CSS-Tricks that covers WAAPI + ScrollTimeline in more detail.
~
# All together now
With that, our final demo now becomes this:
See the Pen Reverse-Scrolling Columns with CSS Scroll-Timelinel (+ JS ScrollTimeline Polyfill Fallback) by Bramus (@bramus) on CodePen.
I know, there’s a FOUC here. I’ll leave it to you, reader, as an exercise to solve.
~
# What about the rest?
The rest of the demo has not been recreated, as that falls out of the scope of CSS @scroll-timeline
. To recreate those, keep an eye out on the Shared Element Transitions API that allows a simple set of transitions in both Single-Page Applications (SPAs) and Multi-Page Applications (MPAs).
~
🔥 Like what you see? Want to stay in the loop? Here's how:
Leave a comment