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.css
or its minified version -
fullpage.js
or 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 ApplicationJonathan Cutrell07 Dec 2015
- How to Apply Instagram Filters in the Browser Using Pure CSSAdi 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: true
and load thescrolloverflow.min.js
library.
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
.box
element. If that’s the case, we give it theis-animated
class. - Each time we leave a section, we check to see if that section has a
.is-box.is-animated
element. In this scenario, we remove theis-animated
class 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-last
class to thebody
element. 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!