It is a common approach to fill in the intro section of a website with a slideshow: you're trying to show the users as much as you can above the fold, yet you want to deliver this information in an organized and clean way. Therefore we built for you a ready-to-use JavaScript slider, with some built-in options like video/image backgrounds and different text alignments. In an attempt to increase user engagement, we replaced the "navigation arrows" with buttons. The difference is: buttons have a title, a hint about what kind of content to expect. Arrows just tell users "you can switch slide".
Assets:
- Icons by Nucleo
- Images by Picjumbo
- iMac & Macbook mockup by Jozef Mak
- iPhone mockup by JustD
- Video by Mazwai
Creating the structure
The HTML is structured in 2 main elements: an unordered list (ul.cd-hero__slider
) containing the slides, and a div.cd-hero__nav
, containing the slider navigation and the span.cd-hero__marker
(used to create the marker for the selected item in the navigation).
<section class="cd-hero js-cd-hero js-cd-autoplay">
<ul class="cd-hero__slider">
<li class="cd-hero__slide cd-hero__slide--selected js-cd-slide">
<div class="cd-hero__content cd-hero__content--full-width">
<h2><!-- Title here --></h2>
<p><!-- Content here --></p>
<a href="#0" class="cd-hero__btn"><!-- Btn text here --></a>
</div> <!-- .cd-hero__content -->
</li>
<!-- other slides here -->
</ul> <!-- .cd-hero__slider -->
<div class="cd-hero__nav js-cd-nav">
<nav>
<span class="cd-hero__marker cd-hero__marker--item-1 js-cd-marker"></span>
<ul>
<li class="cd-selected"><a href="#0">Intro</a></li>
<li><a href="#0">Tech 1</a></li>
<!-- other navigation items here -->
</ul>
</nav>
</div> <!-- .cd-hero__nav -->
</section> <!-- .cd-hero -->
Adding style
The slider structure is pretty straightforward: all the slides are translated to the right, outside the viewport (translateX(100%)
); the .cd-hero__slide--selected
class is added to the visible slide to move it back into the viewport (translateX(0)
), while the .cd-hero__slide--move-left
class is used to translate a slide to the left (translateX(-100%)
).
To achieve the smooth animation, we used CSS3 Transitions applied to the .cd-hero__slide--selected
and the .cd-hero__slide--is-moving
elements: when a new slide is selected, the .cd-hero__slide--is-moving
class is assigned to the slide moving outside the viewport, while the .cd-hero__slide--selected
class is assigned to the selected slide.
.cd-hero__slide {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: translateX(100%);
}
.cd-hero__slide.cd-hero__slide--selected {
/* this is the visible slide */
transform: translateX(0);
}
.cd-hero__slide.cd-hero__slide--move-left {
/* slide hidden on the left */
transform: translateX(-100%);
}
.cd-hero__slide.cd-hero__slide--is-moving,
.cd-hero__slide.cd-hero__slide--selected {
/* the cd-hero__slide--is-moving class is assigned to the slide which is moving outside the viewport */
transition: transform 0.5s;
}
About the single slide animation: on big devices (viewport width more than 768px), we decided to spice up the entrance effect animating the single slide elements (.cd-hero__content--half-width
and .cd-hero__content--full-width
), changing their opacity and transform properties.
The .cd-hero__slide--from-left
and .cd-hero__slide--from-right
classes are used to detect if the selected slide is entering the viewport from the left or from the right, in order to trigger a different animation according to the entrance direction. For this effect to properly work, we used a different animation-delay
value for each animated element.
For the .cd-hero__content--half-width
elements, for example:
@media only screen and (min-width: 768px) {
.cd-hero__content.cd-hero__content--half-width {
opacity: 0;
transform: translateX(40px);
}
.cd-hero__slide--move-left .cd-hero__content.cd-hero__content--half-width {
transform: translateX(-40px);
}
.cd-hero__slide--selected .cd-hero__content.cd-hero__content--half-width {
/* this is the visible slide */
opacity: 1;
transform: translateX(0);
}
.cd-hero__slide--is-moving .cd-hero__content.cd-hero__content--half-width {
/* this is the slide moving outside the viewport
wait for the end of the transition on the <li> parent before set opacity to 0 and translate to 40px/-40px */
transition: opacity 0s 0.5s, transform 0s 0.5s;
}
.cd-hero__slide--from-left.cd-hero__slide--selected .cd-hero__content.cd-hero__content--half-width:nth-of-type(2),
.cd-hero__slide--from-right.cd-hero__slide--selected .cd-hero__content.cd-hero__content--half-width:first-of-type {
/* this is the selected slide - different animation if it's entering from left or right */
transition: opacity 0.4s 0.2s, transform 0.5s 0.2s;
}
.cd-hero__slide--from-left.cd-hero__slide--selected .cd-hero__content.cd-hero__content--half-width:first-of-type,
.cd-hero__slide--from-right.cd-hero__slide--selected .cd-hero__content.cd-hero__content--half-width:nth-of-type(2) {
/* this is the selected slide - different animation if it's entering from left or right */
transition: opacity 0.4s 0.4s, transform 0.5s 0.4s;
}
}
Events handling
The video used as background for one of the slides is not inserted directly into the HTML but loaded only if the device width is bigger than 768px; this way the video won't be loaded on mobile devices. The data-video
of the selected slide is used to retrieve the video url. You may consider doing the same for the <img>
elements inside the .cd-hero__content--img
(which is hidden on mobile devices).
Besides, we used JavaScript to implement the slideshow functionality: when user clicks one of the list items of the .cd-hero__nav
tab, we detect the position of the selected item and update the slider and the span.cd-hero__marker
position accordingly.
To do that, we created a HeroSlider
object and used the init
function to attach event handlers to the proper elements:
function HeroSlider( element ) {
this.element = element;
this.navigation = this.element.getElementsByClassName("js-cd-nav")[0];
this.navigationItems = this.navigation.getElementsByTagName('li');
this.marker = this.navigation.getElementsByClassName("js-cd-marker")[0];
this.slides = this.element.getElementsByClassName("js-cd-slide");
this.slidesNumber = this.slides.length;
// ...
this.init();
};
HeroSlider.prototype.init = function() {
// ...
//listen for the click event on the slider navigation
this.navigation.addEventListener('click', function(event){
if( event.target.tagName.toLowerCase() == 'div' )
return;
event.preventDefault();
var selectedSlide = event.target;
if( hasClass(event.target.parentElement, 'cd-selected') )
return;
self.oldSlideIndex = self.newSlideIndex;
self.newSlideIndex = Array.prototype.indexOf.call(self.navigationItems, event.target.parentElement);
self.newSlide();
self.updateNavigationMarker();
self.updateSliderNavigation();
self.setAutoplay();
});
//...
};
var heroSliders = document.getElementsByClassName("js-cd-hero");
if( heroSliders.length > 0 ) {
for( var i = 0; i < heroSliders.length; i++) {
(function(i){
new HeroSlider(heroSliders[i]);
})(i);
}
}
Note: if you want to animate the slider automatically, add the .js-cd-autoplay
class to the .cd-hero
element.