2. How to create a scrollytelling story
This guide will discuss the "scroll-based steps" style of scrollytelling, where cards of text are overlaid on a background and certain cards trigger animations or changes as the user scrolls. This is the most common type of scrollytelling and is often what people think of when they hear the term "scrollytelling."
We will first cover a conceptual understanding of how scrollytelling works and then walk through the process of creating a simple example using HTML, CSS, and JavaScript.
That being said, there are many ways to implement scrollytelling, and the specific approach will depend on the story you want to tell and the tools you have available.
2.1. The concept
Let's imagine you have a simple html page just with one div that takes up the entire viewport. Let's call this the background. This will be where you will put your main visual element such as a map or graphic.
However, the page has more content that is out of the viewport that you have to scroll to. You can only see the background but if you scroll down it would reveal more content.
Let's now put a new div below the background shown below in the light grey. This will be our foreground. This will hold our text cards, the black rectangles, that we will scroll through.
Ok this is almost right but now we want to make the background stay in place as we scroll. We can achieve this by making the background a sticky element. You now can see where the foreground comes into view with the text cards inside. Toggle the viewport to see the foreground start below and scroll over the the background.
2.2. The setup
Now that we have the idea, we can start setting up our scrollytelling story. We will introduce a few HTML / CSS concepts as we come across them
Setting up the background
Let's first make the background. The background will be a simple div with a background color that takes up the entire viewport. As mentioned, in reality this could be a map or graphic. The key element here as we discussed is that we will make this element position: sticky so it stays in the viewport. This feature is well supported in modern browsers. Read more about it here.
<div class="scroller">
<div class="background">
</div>
</div>
.background {
position: sticky;
top: 0;
width: 100%;
height: 100vh;
z-index: 0;
}
With this setup, the background will stay in place as we scroll down the page. We can add more content below the background to create a longer page that we can scroll through.
Setting up the foreground
Now we can set up the foreground. The foreground will be a div that is positioned on top of the background. We will use relative positioning and the z-index to place the foreground on top of the background. We will also give the foreground a higher z-index so it appears above the background. We then can add some cards to hold our text content.
<div class="scroller">
<div class="background">
</div>
<div class="foreground">
<div class="step">
<div class="card">Step 0</div>
</div>
<div class="step">
<div class="card">Step 1</div>
</div>
<div class="step">
<div class="card">Step 2</div>
</div>
</div>
</div>
.foreground {
position: relative;
width: 100%;
height: 100vh;
z-index: 1;
}
With this setup, we now have a foreground that is positioned on top of the background. As we scroll down the page, the background will stay in place while the foreground cards scroll up and out of view.
2.3. Responding to scroll events
Now the most important part. We want to be able to trigger animations or changes to the background as we scroll through the foreground cards. To do this, we can use JavaScript to listen for scroll events and determine which card is currently in view. We can then use this information to trigger animations or changes to the background.
There are many ways to listen for scroll events and determine which card is in view. However, best practice today is to use the Intersection Observer API, which allows us to efficiently observe when elements enter or leave the viewport and is compatible with modern browsers. Read more about it here.
Rather than use that API directly, we are going to use a tool called Scrollama which was built by the Pudding and is designed to simplify the process of creating scrollytelling experiences using the Intersection Observer API.
Other options to consider
Scrollama is by no means the only option for creating scroll-based storytelling experiences. Other tools to consider include:
- Just use the Intersection Observer API directly, which gives you more control but requires more setup and code.
- Waypoints is a very simple long used library for scroll-based interactions but it is no longer maintained and does not use the Intersection Observer API.
- GSAP is a powerful animation library that can be used for scroll-based animations. Often overkill to just use for scrollytelling
- If you are using Svelte, check out svelte-scroller written by Rich Harris, the creator of Svelte. This uses Intersection Observer's under the hood.
Setting up Scrollama
To set up Scrollama, we first need to include the Scrollama library in our HTML file. We can do this by adding a script tag that points to the Scrollama library.
<script src="https://unpkg.com/scrollama"></script>
Next, we need to initialize Scrollama in our JavaScript file. We can do this by creating a new instance of the Scrollama class and calling the init method. Here we will pass callback functions to define what happens when each step is entered, exited, or progressed through.
const scroller = scrollama();
// setup the instance, pass callback functions
scroller
.setup({
step: ".step",
threshold: 0.5, // trigger when 50% of the step is visible
})
.onStepEnter((response) => {
// { element, index, direction }
})
.onStepExit((response) => {
// { element, index, direction }
})
.onStepProgress((response) => {
// { element, index, progress, direction }
});
Let's explain this. We set the class for our steps (".step") and specify a threshold of 0.5, which means the callback will be triggered when 50% of the step is visible.
What can go in the callback? Anything you can imagine! You can change the background color, trigger an animation, update text, etc. The callback function will receive a response object that contains information about the step that was entered, exited, or progressed through, such as the element itself, its index, the direction of scrolling, and the progress through the step.
For example, let's change the color of the background as we scroll through the cards.
const colors = ["#b45c46", "#5f9ea0", "#7fa67a", "#b47846"];
const scroller = scrollama();
scroller
.setup({
step: ".step",
threshold: 0.5, // trigger when 50% of the step is visible
})
.onStepEnter((response) => {
const { index } = response;
const background = document.querySelector(".background");
background.style.backgroundColor = colors[index];
});
Full html file for the above example — download it here.
Loading...
2.4. Some tips and tricks
Here are some tips and tricks I have learned in my experience with scrollytelling.
Keep Scrollama callbacks simple
Rather than have complex logic in your Scrollama callbacks, just have it update a state variable such as currentStep. You can then use this state variable to control the behavior of other elements on the page as it changes. I find this helps keep the code maintainable and performant.
Use a config object for text
Rather than hardcoding text into your HTML, consider using a config object in your JavaScript file to store the text for each step. This can make it easier to manage and update the text content for your scrollytelling story. Many newsrooms use archieML, a tool created by the NYT, to pull text into your scrollytelling from a Google Doc or other source. This helps immensely with the writing/editing process and allows others working on the story to work in a more familiar environment and for you to not have to copy and paste text.
Design with mobile in mind
Scrollytelling stories can be particularly challenging to design for mobile devices, as the smaller screen size can make it difficult to create a visually engaging experience. When designing for mobile, consider using larger text and simpler graphics to ensure that the story is still accessible and engaging on smaller screens. You may also want to consider using a different scrollytelling technique, such as click-based progression, for mobile users.