Create a Scrollytelling Component With Svelte
Adding a component that updates based on scroll position
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
This lesson preview is part of the Better Data Visualizations with Svelte course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Better Data Visualizations with Svelte, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 02:21] Hey y'all, in the first lesson of our final module, we're going to introduce scrolly telling to our article by adding a scrolly telling component. And what I mean by this is we're not going to try to reinvent the wheel here, right? Scrolly telling is kind of complex behind the scenes because it involves, you know, both listening to scroll events, but also an intersection observer to see when new steps come into view and exit the view, and it's simply too much work and beyond the scope of this course to reinvent that from scratch. So we're going to leverage existing tools as you should in any interactive data visualization project. In our case, we're going to use a tool or a component created by Russell Sumora who works at the pudding. He's created this component, which he calls scrolly, and he's, you know, tweeted about it, and he's linked it in a REPL. So you can read the tweet here, which I've added in the course notes, and he's linked the REPL within that tweet as well. So let's go ahead and take a high level look at what's happening within this component so we can kind of copy it, adopt it, and use it within our code base. So as you can see here, right now active is up in the top right, and this article by default only has a couple of things visible, but as you scroll, new items come into view. Now notice that as I scroll active up in the top right transforms from undefined into the current index of whatever step is currently visible. So here step zero is visible, which is why active colon zero is in the top right corner. That will increment upward to one and two as I progress throughout the article. So what's happening here behind the scenes, right? What I want to draw your attention to are lines one through five and 15 through 27. Obviously a lot happens in the style tag as well, but that's less relevant to actually understanding the internals of what's happening. So let's talk about those chunks of code. In our logic, you know, lines one through five, we're doing three things. First, we're importing the scrolling component, which in this case lives right next to it in the same directory. And that is called scrolly dots felt with a bunch of stuff that you don't need to understand and probably won't until you're a JavaScript expert. Okay. The second thing that we do is we instantiate a new value that we're literally calling value. And if you wanted to make this a bit more, you know, representative what it is, you could call it something like current step.
[02:22 - 03:51] But in the REPL that Russell has created, it's called value. And then the third and final thing we're doing is creating steps that we can then iterate through and in each block. Obviously these are just, you know, some emojis. So maybe in your case, you would use some pros that's written in line, some actual language. But for our case, this is the minimum viable example. So that's what's happening in our script tag. That's the logic. And then the second thing that's happening that I want to draw your attention to is lines 15 through 27, but really lines 18 through 24. What we're seeing here is that there's a large section component, which contains a spacer, which is this blank space at the top, and a spacer at the bottom, which is this blank space here. But then within the key body of this content is the scrolling component. And within that scrolling component, what's being what's occupying the slot of that component is in each block that is lit, that is iterating through each of the individual steps and rendering them in a paragraph tag. So what are we seeing here? On line 18, we open the scroll tag. And what we're doing is we're binding its value to another variable called value that lives on line three. And that is why active right here in the top right corner updates as we iterate through. So essentially, the important thing here is within scrolling, we want to bind value to whatever value exists in our larger parent component.
[03:52 - 05:48] And we do so with this notation. So like I said, if I wanted to instead call this current step, all I would have to do is add bind value to equal current step. And then anytime I'm referencing value throughout the markup, I would add a current step instead. And so these would be the three places. And now this would work functionally equivalent. The important thing is that we have bind value equals the name of the variable that we want to bind to value two. So that is the first thing that we're doing on line 18 is we're opening a scrolling tag and we're adding this declaration to bind value from the child to a variable called current step or value, whatever else you want to call it in the parent. And as a reminder, because value is the same name as its bound prop in the child, that's why we were able to just put bind value. Those are functionally equivalent. Okay, the second thing that we're doing in the markup is we are iterating through a series of steps, recall that our steps array is what's instantiated online for. We're looping through those and we're calling each iterated element text and its index I. Then we're opening a div tag and basically just rendering the markup for each of these steps within lines 20 through 22. We're adding a dynamic class of active if and when value or the current step is equal to the current index. That's why you'll notice that as something comes into view, for example, look at the difference between step zero and one, as it reaches that 50 percent threshold, it goes from a light gray to a darker gray. So that is what this active class is doing. And to verify that you could look in the styling itself and see step active has a background of this darker gray. Just to make that more pronounced so that you really understand what's going on, let's make the active step pink. And now you can see as we trigger a new active step, only one step is active at a time, that active step becomes pink.
[05:49 - 06:50] Okay, I'll move that back to as it was. And now we have a pretty good feel for what's actually happening within our scrolly telling component. Effectively, you know, we have logic, which imports scrolly, creates a value, create some steps, and then we actually render content within that scrolly in its slot and bind whatever is most in view as the value. We then call the value in our case, we also call it value in the parent, but as I said, we could find this to any other variable in the parent markup with this notation. So that is how scrolly telling works. That is a high level example of kind of what's happening behind the scenes. So let's go ahead and set this up in our code base. And in order to do that, we need to import scrolly as a component. So what you could do, and this is kind of the beauty of you know, the REPL system and a lot of reusable spell components, is you could just copy this entire bit of code from the REPL and move it into your code base .
[06:51 - 07:08] So I'm going to go ahead and copy, and then I'm going to move it over to my code base in a new folder, which I'm going to call helpers. So I'm going to open source, create a new folder, call it helpers. And then I'm going to create a new file and call it scrolly.sfelt.
[07:09 - 10:42] And I'm going to paste in this entire file. It's probably good practice to go ahead and give a proper attribution. So I'll put created by wrestle some more, and then I'll link the REPL that we were just working in, something to that effect. And just in case this REPL that I'm screen sharing is not visible, I have linked a stable version of scrolly.sfelt in the course notes. So you can download it from there as well. And so now that we have this component created, we're going to use it just as we've used components previously. And the way that we do that is basically in our app.sfelt, we import the component that we want to access, and we reference it in our code base. So in our case, let's go ahead and import scrolly from, and then find the helpers folder. So in our case, it's one direct , it's in the same directory. And then scrolly.sfelt is the title of it, and we'll hit save. Now just to match the other syntax that Russell had in his REPL, let's go ahead and add a variable called current step, you'll recall his was value, but let's do current step. And then let's make sure that this is going to work by console.logging current step. So let's hit save. And below all of the chart markup that we already have, let's go ahead and paste some of the boilerplate that we saw Russell work with in his scrolly REPL. So I'll go ahead and write this from scratch, but feel free in your case to, you know, manually copy and paste it over. What we know is that we want to open a scrolly, and we want to bind its value to current step, because that's the name of the variable that I've created. We'll then close this, and within the scrolly component, we want to render in each block. Remember, this is exactly what we saw in the REPL. I'm going to write some, some fake pros for you. Hello, scrolly telling world. And within this array, we want to iterate through this array and render each element as text and keep track of its index with i. Okay, then we're going to open a div and call it step. And we're going to apply that same active class dynamically, just like Russell did. So we'll say class of active only applies if current step equals i. Then we'll go ahead and render the text in a p tag. So we'll literally just, you know, wrap the text in these mustache braces, which says render the value, close the div, close the each block, and hit save. Now, what's the first thing that we notice? The first thing that we notice is that all of the steps are visible all at once , right? And so we do have three steps, but they're all visible. And obviously they're, they're all active or the browser doesn't really know which is active because they're all three very prominent. So this is where we get into some CSS, and we're going to add CSS to make sure each step occupies the entire screen height. So I'm going to go down in my markup, and I'm going to add a class of step and say it has a height of 90 VH, where VH basically means viewport height units. So each step would take up 90% of the viewport height. I'll type this and hit save. And now you'll see we only see hello. But if I scroll down, scrolly telling is 90% down the screen, and world is another 90% down the screen. And each of these steps takes up 90% of the viewport. So we know we have these steps, but right now there's no dynamic class of active that's visible to the user.
[10:43 - 11:25] So let's just verify that the active class is in fact applying before we make any visual changes in CSS. So here you'll see I'm currently looking at the step div right here. I'm going to move myself over so you can see. And so right now hello is the active step. Let's see what happens as I scroll down and scrolly telling comes into view. Now scrolly telling is the active step, and the one above it is no longer active. That means our dynamic class of active is being applied. And that was as simple as just plugging in Russell's scrolly component, and we're good to go. So now that we know that active is being applied as a class, let's go ahead and handle that in our CSS.
[11:26 - 11:51] What we'll do is by default make each step have an opacity of 0.3. So it's somewhat invisible. But then if and when it becomes active, which we'll achieve with this rule set, we'll add an opacity of 1 that will basically override it. So now notice how scrolling telling is faded out. But if I scroll up and it comes into primary view, 50% up the screen, it becomes dark again. The transition is somewhat abrupt, abrupt, basically going from gray to black.
[11:52 - 12:05] So let's add a transition property to make that smooth. We'll say transition of opacity that takes 300 milliseconds and eases into place. Now we notice that as you scroll into place, there's this smooth transition of opacity.
[12:06 - 12:47] So we basically have a scrolly telling site, but obviously there's this evident issue where the steps are just straight up below the chart and there's no constancy between the text elements and the chart that they should be overlaid on top of. So what we're going to want to do is place the chart or place the text elements on top of the chart. And the way that we're going to achieve this is by splitting up our existing application into two discrete sections. And they're going to be div elements. One will be the div element that we call the sticky element. And it's sticky because it stays at the top of the view port until it reaches the end of the section.
[12:48 - 12:58] And the other is what we're going to call the steps container, which is actually what we already have. So with that in mind, we're now moving to place it on top . That's what we're achieving in this part of the lesson.
[12:59 - 13:08] So what we're going to do is basically open and close a section here. And section is going to be the largest container of our component.
[13:09 - 13:44] And then we'll close it under the scrolly, right? And by default, nothing's going to change. But now let's start to apply these other styles. So I'm going to, like I said, create one div that's called sticky. And within that div is chart container and all of its components. And I'm going to close it before the scrolly telling component. And then I'm going to open up our other div that we call steps. I'm going to open and close that around the scrolly telling parent. Again, nothing changes here yet, because we need to now use CSS to basically place the text on top.
[13:45 - 14:11] So this is going to be kind of a workflow, the CSS you need to have one element fixed in the background or sticky in the background, and one element scrollable on top of it. Okay. So the first thing that we'll do is add a section with a position of relative. And that way, whenever we add a position of sticky to our chart element, or its parent, it will be sticky relative to the section.
[14:12 - 14:17] Then we'll go ahead and add that sticky rule set. And we'll give it a position of sticky and a top of zero.
[14:18 - 14:24] Now, before I hit save, I want to kind of describe what's happening. If you're not super familiar with CSS, this might come off as confusing.
[14:25 - 15:14] But you might remember that there are a couple of positions that we've used before, like absolute and fixed, that allow you to give pixel values for top, left, bottom, and right. And those elements will basically be taken out of the document flow and placed according to their pixel positions. Sticky is the same thing, but it also basically pays attention to its parent container. And so what this top of zero means is that the element will basically look normal until it reaches the zero pixel position of its relative container. So in our case, once the chart hits the top of the viewport or the top of the section container, it will then stick there until we get to the end of the section element, and then it will scroll out of you normally. So it's basically absolute or fixed, but relative to a parent container.
[15:15 - 15:24] And it reflects that container's scroll position. So maybe easier said than done. Maybe I'll just have to show you to really understand what's happening here.
[15:25 - 16:15] So let's hit save, see if anything changes. Well, now you notice that like I was describing, the chart does stay fixed at the top of the viewport, even while these other steps scroll on top of it, which means that something is definitely working here. Right now, the most apparent issue is that these steps look pretty ugly and they don't look like real steps. They just look like text elements. So let's update our step rule set, which we have online 111 through 115, to account for those changes. First, we'll add a display of flex, and then we'll add a justify content and a place items of center. And the reason we're going to do this is so that basically the step itself will occupy the middle of this 90 viewport height and of the entire width of the canvas. So basically, by adding this, these three commands, we say vertically and horizontally center the step.
[16:16 - 16:43] So now if I hit save, you'll notice how the step goes 45 viewport units down, relative to where it started, and it's centered on the screen. So this is much better. This looks a little bit more like a scrolling telling article that you've seen before. Still, you know, the steps don't look like real steps. They're just text elements. So let's go ahead and style the step content. And as a reminder, step content might not exist in our markup yet. So maybe we need to add that.
[16:44 - 16:52] Yeah, what we need is another div. I apologize for not referencing this earlier . We need a div with the class of step content that wraps around our p tag.
[16:53 - 17:01] So basically step is the 90 viewport height container. Step content can be thought of the entire box that can include multiple p tags.
[17:02 - 17:23] And the p tag would just be the text element. And you could have any number of these p tags. Right. So we want to target this step content. So that within the 90 viewport height container, we have one regular box that basically looks like a step. So I'm going to create a style for step content, or a rule set. What am I going to apply here?
[17:24 - 17:35] Well, this is pretty basic CSS for, you know, steps for things that look like value boxes. A background of, for example, white. Now you'll see this is white, so you can't really see it.
[17:36 - 17:55] But maybe a border of one px, the solid border and black. Now you see that it kind of looks like a box. But obviously you need padding so that it can breathe. So I'll do a 0.75 rem one rem where this is the vertical padding. This is the horizontal padding. Now you see it looks a little bit more like a step.
[17:56 - 18:02] Finally, I'll add a padding of 0.75 rem. Sorry. Finally, I'll add a border radius of three pixels.
[18:03 - 18:18] Now it looks like a nice step. And because I add to this background of white, as I scroll over, you might notice that you can kind of see the background visible. The issue is that sometimes the steps themselves are behind the circles.
[18:19 - 18:25] Sometimes you can't scroll. There's this whole issue. And so the reason for this is what we call the Z index.
[18:26 - 18:49] So I can't remember if we talked about Z index in the course before, but basically for elements that are positioned, absolutely, for example, or fixed or sticky, there can often kind of be an issue that the browser doesn't understand with where that element should be rendered in the overall document hierarchy. Right? So should element one be placed on top of or below element two?
[18:50 - 19:06] And Z index is the answer that we have to that question. So in our case, we want to add a higher Z index to the steps diff, rather than the sticky diff, so that the steps div will always appear on top of the sticky one.
[19:07 - 19:16] So here, for example, you might remember that we have section, sticky, and steps. We don't have a rule set for steps yet. So let's go ahead and create that.
[19:17 - 19:27] And we'll add a position of relative, which is needed for this Z index. And then we'll give it a Z index of two and give our sticky element a Z index of one.
[19:28 - 19:36] The important thing is that this number is lower than this number. Now, if I save, you'll notice the step appears on top of the chart itself.
[19:37 - 19:54] So this is exactly what we wanted to achieve. So to recap, basically, what we've done so far in this lesson, we took these text elements that we created that we're calling hello, scrolly telling world, and we ported them into Russell Samoara's existing scro lly component.
[19:55 - 20:30] And we placed those within a div that we called steps by adding another div that we called sticky within a broader section component. You know, this parent component that contains everything. We were then able to apply the following CSS rules, basically from lines 113 to 147 to build a functional scrolly telling component that toggles active status of a step on and off based on what's most in the viewport. So we had a really good start to our scro lly telling lesson to our module. And what we're going to do in the next lesson is trigger new behavior on scroll.
[20:31 - 20:43] So whenever one element comes into view, whenever one element becomes active, or otherwise, we will trigger new behavior, like moving the circles around. So I'm really excited to get into that. I hope this lesson was fun, and I'll see you in lesson two.
[20:44 - 20:44] Thanks.