In this tutorial, we're going to make a page that snaps from one fullscreen section to another as the user scrolls. This effect was popularized by the fullpage.js
library. But now, there are better options due to licensing changes to fullpage (you now need to pay for it), newish CSS scroll-snap module, and complete browser support of the intersection observer API.
So, if you're looking for a fullpage.js alternative, you might not even need an entire library, you can just code it yourself!
In fact, the snapping effect is 100% CSS. JS is just used to fade elements in when the screen "snaps" into view.
We'll be making use of this "Dinosaur Park scroll snap demo" by Michelle Barker available on CodePen for inspiration:
By using CSS coupled with Intersection Observer, we'll make each fullscreen section of the page snap-scroll, and fade content in when it enters the viewport.
Even better, it's only ~30 lines of JS code! Animations are done with CSS; all we need to do is understand when sections and content enter our viewport with JS.
First, will need to build a page, or configure an existing one to be compatible with this simple effect. There are two things to note here:
We're using a <main>
element, which contains our sections. This main element is 100vh, as are each of the <section>
elements within.
HTML:
CSS:
Here we can see that there are multiple sections, each with a height of 100vh.
The <main> element, is also 100vh, with overflow-y:scroll.
To the user, scroll behavior is the same as a default webpage.
If you added the CSS mentioned above, the snapping effect will already be in play. Let's take a look at how it works.
First, the "@media (min-height: 30em)
" query disables the snapping effect for mobile screen sizes that are to short in terms of height. Under the main section, there's one property that enables the snapping:
This is a cool little property "sets how strictly snap points are enforced on the scroll container in case there is one" (MDN).
And then under our sections, we have:
This property "property specifies the box's snap position as an alignment of its snap area (as the alignment subject) within its snap container's snapport (as the alignment container)" (MDN).
Learn more about the CSS scroll-snap module here:
And it's as simple as that. Direct children within the <main> container (all sections), will scroll-snap to the center of the <main> container. Because both the Main and the Section are 100vh, the scrolling will snap from section to section.
By now, we have a full page scroll effect, and if you don't want to do anything else, you can skip this section and enjoy your website. However, when each full page section snaps into view, I want to fade-in the content within - a unique and cool effect (also one that's possible with fullpage.js).
To do this, we'll use intersection observer to understand the positioning of sections within our <main> viewport.
There are two things being done here. First, it identifies when a section is in the viewport, and as it enters, adds a CSS class .is-visible
. As it leaves the viewport, it removes that class.
Additionally, as this section enters the viewport it consecutively (one after another) sets property of delay for 250ms on each of the children elements. This makes for a staggered fade in effect.
It only does this to children within elements that have the attribute of data-content. So you need to add that to the wrappers around your pages content if you want the fade in effect.
Now, to make this work, we need to set up the fade-in animation for elements that have the .is-visible class added.
If we refer back to the HTML from the beginning demo, you'll see that there's a div wrapping around content with the class of .section__content, and the data-content attribute.
This ties our JS and CSS together. Opacity is 0 for content until the .is-visible
class is added by intersection observer when it enters the viewport. When it leaves, it reverts back to an opacity of 0, so regardless of your scrolling direction there will always be a feed in effect.
Finally, the delay
which is applied consecutively to each child element staggers the fade in effect.
Note the third section. The section header isn't within a data-content section, so it won't fade in (will already be there).
Here's the demo:
Here's all the code we used in our demo:
This is the perfect fullpage.js alternative that doesn't just stop at snapping between full screen sections. We also used the intersection observer API to understand when a section enters the page and then dynamically apply a staggered animation. You can use this basic example to build off of and make some really cool effects with minimal vanilla JS and CSS.