Go to homepage

Projects /

Presentation Slideshow

A simple presentation template in CSS and jQuery.

Presentation Slideshow
Check our new component library →

Although presentations are usually created using native apps, we decided to challenge ourselves and design a presentation template for the browser. It wasn’t an easy task, for the way you interact with a presentation slideshow is different compared to how you scroll through a website: each unit/slide must be isolated, so that its content can be assimilated before switching to the next one.

How do you navigate through a presentation? The easiest way is to use keyboard arrow keys (for devices that have them). However we had to take into account that all other interactions (click, scroll…) had to work as well when building something for the web.

Let us know what you think in the comments section ;)

👋 A new version of this component is available. Download now →.

Creating the structure

The HTML structure is composed by two main elements: a <nav> for the slideshow navigation, and an ordered list for the slideshow items. We used a nested ordered list for items with multiple sub slides.

<div class="cd-slideshow-wrapper">
   <nav class="cd-slideshow-nav">
      <button class="cd-nav-trigger">
         Open Navigation
         <span aria-hidden="true"></span>
      <div class="cd-nav-items">
            <li><a href="#slide-1">Slide 1</a></li>
               <a href="#slide-2">Slide 2</a>
               <ol class="sub-nav">
                  <li><a href="#slide-2">Slide 2 - Sub 1</a></li>
                  <!-- other sub-slide links here -->
            <li><a href="#slide-3">Slide 3</a></li>
            <!-- other slide links here -->
      </div> <!-- .cd-nav-items -->
   </nav> <!-- .cd-slideshow-nav -->
   <ol class="cd-slideshow">
      <li class="visible" id="slide-1">
         <div class="cd-slider-content">
            <div class="content-wrapper">
               <h2>Presentation Slideshow</h2>
               <p>A simple presentation template in CSS &amp; jQuery.</p>

      <li id="slide-2">
         <ol class="sub-slides">
               <div class="cd-slider-content">
                  <div class="content-wrapper">
                     <h2>Slider #2</h2>
            <!-- sub-slides content here -->

         </ol> <!-- .sub-slides -->

      <!-- additional slides here -->
   </ol> <!-- .cd-slideshow -->
</div> <!-- .cd-slideshow-wrapper -->

Adding style

On small devices (viewport width smaller than 1100px), we organized all slides as a simple list. For items with sub slides, though, we implemented a slider with drag/swipe interaction to navigate it.

.cd-slideshow .sub-slides {
  width: 100%;
  transition: transform 0.3s;
.cd-slideshow > li, 
.cd-slideshow .sub-slides > li {
  position: relative;
  z-index: 1;
  height: 100vh;
  width: 100vw;
.cd-slideshow .sub-slides > li {
  float: left;

The width of the ordered list items with sub slides is set using JavaScript. When a user navigates from a sub slide to the previous/next one, we translate the .sub-slides element (nested ordered list) along the X axis (more in the Events handling section).

On bigger devices, we set the .cd-slideshow-wrapper height to 100vh and add an overflow hidden, so that only the slide in the viewport is visible. We then set height, width and margin of the .cd-slider-content to center it in the viewport.
The .visible class is added to the visible slide: it is used to hide the .cd-slider-content::after pseudo-element (used to change the slide background color when the slide is out of focus) and show the slide content.

@media only screen and (min-width: 1100px) {
  .cd-slideshow-wrapper {
    height: 100vh;
    overflow: hidden;

  .cd-slideshow {
    transition: transform 0.6s;
  .cd-slideshow > li, .cd-slideshow .sub-slides > li {
    height: auto;
    width: auto;

  .cd-slider-content {
    height: 84vh;
    width: 90vw;
    margin: 2vh 5vw;
    border-radius: 10px;
    cursor: pointer;
  .visible .sub-visible .cd-slider-content, 
  .visible > .cd-slider-content {
    /* visible slide */
    cursor: auto;
  .cd-slideshow > li:first-of-type .cd-slider-content {
    margin-top: 8vh;
  .sub-slides > li:first-of-type .cd-slider-content {
    margin-left: 5vw;
  .sub-slides > li .cd-slider-content {
    margin-left: 1.25vw;
    margin-right: 1.25vw;
  .cd-slider-content .content-wrapper {
    height: 100%;
    /* hide the slide content if the slide is not selected/visible */
    opacity: 0;
    box-shadow: 0 6px 40px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.15);
    border-radius: inherit;
    transition: opacity 0.6s;
  .cd-slider-content::after {
    /* this is used to change the slide background color when the slide is out of focus */
    content: '';
    position: absolute;
    z-index: 3;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    border-radius: inherit;
    background-color: #3a3a3a;
    box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1);
    opacity: 1;
    visibility: visible;
    transition: opacity 0.6s, visibility 0.6s;
  .visible .cd-slider-content .content-wrapper {
    opacity: 1;
  .visible .cd-slider-content::after {
    opacity: 0;
    visibility: hidden;

When a user navigates from a slide to the next/previous one, we translate the .cd-slideshow element along the Y axis (more in the Events handling section).

Events handling

The presentation can be navigated in different ways: using the keyboard; using the slideshow main navigation; clicking on slides out of focus or scrolling through the slides.

Two main functions have been used to implement the slideshow navigation: the updateSlide to navigate from a slide to the next/previous one, and the updateSubSlide to navigate from a sub slide to the next/previous one. For the updateSubSlide function, for example, we used:

function updateSubSlide(listItem, string, subSlide) {
   var translate,
       marginSlide = Number(listItem.find('.cd-slider-content').eq(0).css('margin-right').replace('px', ''))*6,
       windowWidth = window.innerWidth;

   windowWidth = ( mq == 'desktop' ) ? windowWidth - marginSlide : windowWidth;

   if( listItem.children('.sub-slides').length > 0 ) {
      var subSlidesWrapper = listItem.children('.sub-slides'),
      visibleSubSlide = subSlidesWrapper.children('.sub-visible');
      if( string == 'nav' ) {
         /* we have choosen a new slide from the navigation */
         var newSubSlide = subSlide;
      } else {
         var newSubSlide = (string == 'next') ? visibleSubSlide.next() : visibleSubSlide.prev();
      var newSubSlidePosition = newSubSlide.index();
      translate = parseInt(- newSubSlidePosition*windowWidth);
      setTransformValue(subSlidesWrapper.get(0), 'translateX', translate + 'px');

function setTransformValue(element, property, value) {
   element.style["-webkit-transform"] = property+"("+value+")";
   element.style["-moz-transform"] = property+"("+value+")";
   element.style["-ms-transform"] = property+"("+value+")";
   element.style["-o-transform"] = property+"("+value+")";
   element.style["transform"] = property+"("+value+")";
   // ...

Project duplicated

Project created

Globals imported

There was an error while trying to export your project. Please try again or contact us.