How to Create an Animated Spirit Day Presentation With fullPage.js
October 18th 2018 is Spirit Day, something we recognize each year at Tuts+, and on this day we speak out against LGBTQ bullying, especially among LGBTQ youth.
In this tutorial you’ll learn to build an animated Spirit Day presentation with fullPage v3.
Here’s what we’re going to build:
Note: You might remember another tutorial I wrote about fullPage some time ago. That tutorial uses fullPage v2, but as many people still use v2 in their projects there’s no need to commit that tutorial to the archives.
What is fullPage.js v3?
fullPage.js is a hugely popular JavaScript library created by Alvaro Trigo. It allows us to build beautiful one-page scrolling websites and applications.
While it’s considered to be a dependency-free JavaScript library, it can also be used as a jQuery plugin just like its previous version (v2).



Unlike v2, this new version is free under certain conditions. A license is required not only in commercial projects, but also in any non-commercial project that isn’t open sourced and compatible with the GPLv3 license.
It has great browser support and comes with detailed documentation. A big list of working examples can be found on this page.
Beyond the core features, it provides some extra paid features referred to as extensions.
Due to its popularity, a WordPress theme along with wrappers for different JS frameworks (e.g Vue.js wrapper) has also been developed.
Getting Started With fullPage.js v3
To get started with fullPage begin by downloading and installing the following files in your project:
-
fullpage.cssor its minified version -
fullpage.jsor its minified version
Optionally, you might want to import easings.min.js as well as scrolloverflow.min.js.
You can grab a copy of the corresponding fullPage files by visiting its Github repo, by using a package manager (e.g. npm), or by loading the necessary assets through a CDN (e.g. cdnjs). For this tutorial, I’ll choose the last option and pull everything into CodePen.
For the purposes of this tutorial, beyond the fullPage files, I’ve also incorporated Babel.
With that in mind, if you look under the Settings tab of our demo pen, you’ll see that I’ve included one external CSS file and two external JavaScript files.






1. The HTML
To kick things off we’ll define a wrapper element which contains all the sections. Each section receives the required section class. This class can be customized during the library’s initialization process through the sectionSelector configuration option. All section elements are wrapped inside a .container element.
Here’s the basic HTML structure:
1 |
<div id="fullpage"> |
2 |
<div class="section"> |
3 |
<div class="container">...</div> |
4 |
</div>
|
5 |
<div class="section"> |
6 |
<div class="container">...</div> |
7 |
</div>
|
8 |
<div class="section"> |
9 |
<div class="container">...</div> |
10 |
</div>
|
11 |
<div class="section"> |
12 |
<div class="container">...</div> |
13 |
</div>
|
14 |
<div class="section"> |
15 |
<div class="container">...</div> |
16 |
</div>
|
17 |
<div class="section"> |
18 |
<div class="container">...</div> |
19 |
</div>
|
20 |
</div>
|
2. The CSS
With the HTML in place, let’s look at the CSS styles we’ll apply to our page. For simplicity, we’ll only discuss the most crucial of the available styles.
Firstly, we define two custom properties using CSS variables:
1 |
:root { |
2 |
--main-white-color: white; |
3 |
--main-black-color: black; |
4 |
}
|
Next, we’ll specify the styles for our sections:
1 |
.section { |
2 |
background: linear-gradient(to bottom, #7f2a8b 20%, #a336b2 70%, #b43dc4); |
3 |
}
|
4 |
|
5 |
.section:last-child { |
6 |
background: #7b2987 url(IMG_SRC) no-repeat fixed center / cover; |
7 |
background-blend-mode: luminosity; |
8 |
}
|
9 |
|
10 |
.section .container { |
11 |
max-width: 750px; |
12 |
width: 85%; |
13 |
padding: 15px; |
14 |
margin: 0 auto; |
15 |
text-align: center; |
16 |
}
|
17 |
|
18 |
.section .box { |
19 |
padding: 10px; |
20 |
}
|
21 |
|
22 |
.section img { |
23 |
display: block; |
24 |
max-width: 80%; |
25 |
width: 500px; |
26 |
margin: 0 auto; |
27 |
}
|
28 |
|
29 |
.section em { |
30 |
font-size: 0.8rem; |
31 |
}
|
32 |
|
33 |
.section a { |
34 |
font-size: 2.5rem; |
35 |
padding-bottom: 1px; |
36 |
border-bottom: 2px solid; |
37 |
text-decoration: none; |
38 |
}
|
Finally, we’ll set the rules responsible for the animation of the .box elements (these appear in the second, third, fourth, and fifth sections):
1 |
.section .box { |
2 |
opacity: 0; |
3 |
transform: scale3d(0, 0, 0) translate3d(0, -100%, 0); |
4 |
}
|
5 |
|
6 |
.section .box.is-animated { |
7 |
animation: fadeIn 0.5s ease-in-out forwards; |
8 |
}
|
9 |
|
10 |
@keyframes fadeIn { |
11 |
100% { |
12 |
opacity: 1; |
13 |
transform: scale3d(1, 1, 1) translate3d(0, 0, 0); |
14 |
}
|
15 |
}
|
Customizing the Navigation Appearance
It would be nice to customize the appearance of the dots navigation according to our needs.
The left part of the screenshot below shows the default styles, while the right part shows the desired styles:



Plus, we want one more thing. As we navigate to the last section, because we’ll be using a pale image background, the color of the dots navigation should change to black, like this:



There are two ways to keep track of when the last section is visible.
Firstly, through CSS. The library appends a class of the form fp-viewing-SECTION-SLIDE to the body element depending on the active section/slide.
So as we have six sections, when we leave the fifth section and visit the last one, the body class (starting from fp-viewing-0) will be as follows:



This method isn’t perfectly flexible. For example, we might add a rule that changes the dots’ color when the sixth (last) section becomes visible:
1 |
.fp-viewing-5 #fp-nav ul li a span { |
2 |
background: var(--main-black-color); |
3 |
}
|
But then, if we add a seventh section, we’d have to modify the aforementioned rule to this one:
1 |
.fp-viewing-6 #fp-nav ul li a span { |
2 |
background: var(--main-black-color); |
3 |
}
|
The second way is through JavaScript (as we’ll cover in the next section) and will give us more flexibility. Through our own instruction a custom fp-last class will be added to the body each time we navigate to the last section.



Working under the assumptions discussed above, let’s check out the styles needed for customizing the dots navigation appearance:
1 |
#fp-nav ul li { |
2 |
margin: 15px 7px; |
3 |
}
|
4 |
|
5 |
#fp-nav ul li a span { |
6 |
background: var(--main-white-color); |
7 |
border: 3px solid transparent; |
8 |
transition-duration: 0.5s; |
9 |
}
|
10 |
|
11 |
#fp-nav ul li a span, |
12 |
#fp-nav ul li:hover a span { |
13 |
width: 12px; |
14 |
height: 12px; |
15 |
}
|
16 |
|
17 |
#fp-nav ul li a span, |
18 |
#fp-nav ul li:hover a span, |
19 |
#fp-nav ul li a.active span, |
20 |
#fp-nav ul li:hover a.active span { |
21 |
margin: -9px 0 0 -9px; |
22 |
}
|
23 |
|
24 |
#fp-nav ul li:hover a span, |
25 |
#fp-nav ul li a.active span { |
26 |
background: transparent; |
27 |
border-color: var(--main-white-color); |
28 |
}
|
29 |
|
30 |
.fp-last #fp-nav ul li a span { |
31 |
background: var(--main-black-color); |
32 |
}
|
33 |
|
34 |
.fp-last #fp-nav ul li:hover a span, |
35 |
.fp-last #fp-nav ul li a.active span { |
36 |
background: transparent; |
37 |
border-color: var(--main-black-color); |
38 |
}
|
Image With CSS Blend Modes and Flexbox Centering
Our last frame is an image which I’ve downloaded from Envato Elements. It’s a full color image, but thanks to some CSS we can apply a blend mode in the browser which mixes the image with a purple background:
The blend mode (luminosity) is applied like so:
1 |
.section:last-child { |
2 |
background: #7b2987 url(spirit.jpg) no-repeat fixed center / cover; |
3 |
background-blend-mode: luminosity; |
4 |
}
|
For a better understanding of how CSS blend modes work, check out these tutorials:


Blending Modes in CSS: Color Theory and Practical Application

Jonathan Cutrell07 Dec 2015

How to Apply Instagram Filters in the Browser Using Pure CSS

Adi Purdila22 Feb 2018
3. The JavaScript
At this point we’re ready to turn our attention to some JavaScript.
Initializing fullPage 3
As a first step, we’ll initialize fullPage with the following customizations:
- We set the license key as
OPEN-SOURCE-GPLV3-LICENSE. If you plan to use fullPage in a commercial project you’ll have to change this value with your own license key. - By default the library doesn’t show the dots navigation. In our case, it will be visible and vertically positioned to the left of the page.
- We use JavaScript instead of CSS3 transforms to scroll within sections by setting
css3: false. We do this to avoid a conflict which happens in some browsers between the transformed elements and their fixed-positioned child elements. Our last section has a fixed background image.
- A scrollbar will appear in case the section’s content is bigger than its height. For this reason we set
scrollOverflow: trueand load thescrolloverflow.min.jslibrary.
1 |
const fullPage = document.getElementById("fullpage"); |
2 |
|
3 |
new fullpage(fullPage, { |
4 |
licenseKey: "OPEN-SOURCE-GPLV3-LICENSE", |
5 |
navigation: true, |
6 |
navigationPosition: "left", |
7 |
css3: false, |
8 |
scrollOverflow: true |
9 |
});
|
Working With Section Events
In our project, the following things should happen:
- Each time we navigate to a section, we check to see if that section has a
.boxelement. If that’s the case, we give it theis-animatedclass. - Each time we leave a section, we check to see if that section has a
.is-box.is-animatedelement. In this scenario, we remove theis-animatedclass from it. - Each time we leave a section, we check to see which is the destination section. If this is the last section, we add the
fp-lastclass to thebodyelement. Otherwise we make sure this class is removed from it.
To satisfy the requirements above, we take advantage of the afterLoad and onLeave events.
The afterLoad event is triggered once a section has been loaded, after the scrolling has ended.
The onLeave event is fired once the user leaves a section, in the transition to the new section.
Here’s the resulting initialization with the aforementioned events:
1 |
const body = document.querySelector("body"); |
2 |
const fullPage = document.getElementById("fullpage"); |
3 |
const boxes = document.querySelectorAll(".box"); |
4 |
|
5 |
new fullpage(fullPage, { |
6 |
licenseKey: "OPEN-SOURCE-GPLV3-LICENSE", |
7 |
navigation: true, |
8 |
navigationPosition: "left", |
9 |
css3: false, |
10 |
scrollOverflow: true, |
11 |
afterLoad: function(origin, destination, direction) { |
12 |
const activeSection = destination.item; |
13 |
if (activeSection.querySelector(".box")) { |
14 |
activeSection.querySelector(".box").classList.add("is-animated"); |
15 |
}
|
16 |
},
|
17 |
onLeave: function(origin, destination, direction) { |
18 |
destination.isLast |
19 |
? body.classList.add("fp-last") |
20 |
: body.classList.remove("fp-last"); |
21 |
|
22 |
if (document.querySelector(".box.is-animated")) { |
23 |
document
|
24 |
.querySelector(".box.is-animated") |
25 |
.classList.remove("is-animated"); |
26 |
}
|
27 |
}
|
28 |
});
|
Conclusion
And we’re done! In this tutorial we covered the very basics of fullPage 3. There’s much more we could discuss and I hope that the demo gave you enough inspiration for building something beautiful with this amazing library.
If you’d like to see more advance topics with fullPage (I’m planning something based on WordPress, fullPage, and AJAX if you’re interested?) let me know in the comments below. And don’t forget to take a look at https://www.glaad.org/spiritday to show your support today. Go purple!




