Presentation slides using HTML and CSS

My HTML framework for simple drag and drop presentations from Markdown files.

I was fed up with PowerPoint-style presentation tools, and created my own browser-based presentation viewer called slides-now. Making a slide-based viewer is trivial using HTML and CSS3 transitions, and in this blog post I will go through the 4 steps to achieve the visual transition effect.

Goal

Display individual slides and transition between them on key presses.

Step 1: initial structure

Step 1 demo

I will use article HTML element to keep my presentation, and individual section elements for each slide. The content is directly a part of the HTML document's body

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<article>
<section><h2>Slide 1</h2>
<p>This is the first slide</p>
</section>

<section><h2>Slide 2</h2>
<p>Second slide</p>
</section>

<section><h2>Slide 3</h2>
<p>Another slide</p>
</section>
</article>
</body>

Each section is styled for clarity

1
2
3
4
5
6
7
section {
width: 200px;
height: 100px;
border: 1px solid black;
border-radius: 5px;
padding-left: 1em;
}

result

step 1 result

All slides are displayed at once, each block follows the another, just like a list of divs. The slides are styled to be very small for demo purposes, usually a slide would occupy 90-100% of the window's space.

Step 2: simple slide switch in place

Let's display a single active slide at a time. We will need a few additional CSS classes and JavaScript event handler to toggle CSS.

Step 2 demo

1
2
3
4
5
6
7
section.active {
visibility: visible;
}
section.inactive {
visibility: hidden;
display: none;
}

Event handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$(function () {
var currSlide = 0;
var allSlides = $('section');
$(document).on('keydown', function (event) {
var nextSlide;
if (event.keyCode === 37) { // clicked left arrow
nextSlide = currSlide ? currSlide - 1 : currSlide;
} else if (event.keyCode === 39) { // clicked right arrow
console.log('next slide');
nextSlide = currSlide < allSlides.length - 1 ? currSlide + 1 : currSlide;
}
if (nextSlide !== currSlide) {
$(allSlides[currSlide])
.removeClass('active')
.addClass('inactive');
$(allSlides[nextSlide])
.removeClass('inactive')
.addClass('active');
currSlide = nextSlide;
}
event.preventDefault();
});
});

A single slide is active, every other slide is inactive and hidden.

result

I will show the HTML DOM snapshot instead of page. In this case only the second slide is visible.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<article>
<section class="inactive">
<h2>Slide 1</h2>
<p>This is the first slide</p>
</section>
<section class="active">
<h2>Slide 2</h2>
<p>Second slide</p>
</section>
<section class="inactive">
<h2>Slide 3</h2>
<p>Another slide</p>
</section>
</article>

Step 3: prev - active - next slides

Showing a single slide at a time is nice, but the sudden switch to new slide is too abrupt. We would like to slide to the next chunk of content. Before we implement the motion, lets lay the groundwork by positioning the slides first.

Step 3 demo

Lets introduce two additional CSS classes and position them to the left (previous) and to the right (next) slide.

We are going to position the previous / next slides using CSS transform property, and make it very light using opacity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* by default slides are placed where top/left tells them */
section {
-webkit-transform: none;
-moz-transform: none;
-ms-transform: none;
-o-transform: none;
transform: none;
...
}
.prev {
-webkit-transform: translate3d(-110%, 0, 0);
-moz-transform: translate3d(-110%, 0, 0);
-ms-transform: translate3d(-110%, 0, 0);
-o-transform: translate3d(-110%, 0, 0);
transform: translate3d(-110%, 0, 0);
opacity: 0.25;
}
.active {
opacity: 1;
}
.next {
-webkit-transform: translate3d(110%, 0, 0);
-moz-transform: translate3d(110%, 0, 0);
-ms-transform: translate3d(110%, 0, 0);
-o-transform: translate3d(110%, 0, 0);
transform: translate3d(110%, 0, 0);
opacity: 0.25;
}

The key press handler becomes slightly more complicated to change all CSS classes on press

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (event.keyCode === 37 && currSlide) {
// previous slide
$(allSlides[currSlide - 1])
.removeClass('prev')
.addClass('active');
$(allSlides[currSlide + 1])
.removeClass('next')
.addClass('inactive');
$(allSlides[currSlide])
.removeClass('active')
.addClass('next');
$(allSlides[currSlide - 2])
.removeClass('inactive')
.addClass('prev');
currSlide -= 1;
}

result

step 3 result

You can switch between the slides using left / right keys. The transition is sudden, but the slides are positioned correctly for the next step.

Step 4: slide transitions

In this step we are going to interpolate changes in slide position and opacity using CSS3 transition specification.

Step 4 demo

1
2
3
4
5
6
7
section {
-webkit-transition: -webkit-transform .7s ease, opacity .7s ease;
-moz-transition: -moz-transform .7s ease, opacity .7s ease;
-ms-transition: -ms-transform .7s ease, opacity .7s ease;
-o-transition: -o-transform .7s ease, opacity .7s ease;
transition: transform .7s ease, opacity .7s ease;
}

The above CSS means that when removing class next and adding class active, CSS transform property changes from transform: translate3d(110%, 0, 0); to transform: none; and the browser CSS engine itself will interpolate the changes, making the entire animation last 0.7 seconds. Same principle applies to opacity: it will be interpolated from 0.25 to 1.0 over 0.7 seconds.

Our JavaScript remains unchanged: when moving a slide from active to prev state after clicking right arrow button, all we need to do is toggle CSS class names

1
2
3
$(allSlides[currSlide])
.removeClass('active')
.addClass('prev');

Step 4: advanced

An additional detail adds complexity to the correct visual transition. Let's say the user pressed right key button. The next slide becomes active, moving according to the CSS transformation. How should next + 1 slide become next? If there is not position information set, it will appear from the center, which looks weird. In order to keep things consistent, I wanted next slides to appear as if sliding from the right. So I introduced two additional CSS classes: before class is attached to the slides before prev slide. After class is attached to the slides after the next slide. The only purpose for theses classes is to have correct position information - they are hidden under prev or next slides waiting to fade in using opacity CSS interpolation.

1
2
3
4
5
6
7
8
.before {
-webkit-transform: translate3d(-220%, 0, 0);
-moz-transform: translate3d(-220%, 0, 0);
-ms-transform: translate3d(-220%, 0, 0);
-o-transform: translate3d(-220%, 0, 0);
transform: translate3d(-220%, 0, 0);
opacity: 0;
}

Next steps

The described visual steps create a tiny presentation engine, running in your browser. Right away I advise you to:

  • Use stylus instead of plain CSS to deal better with verbose CSS transforms.
  • Use jade instead of plain verbose HTML for faster editing.
  • Refactor JavaScript to make initialization and keeping track of active slide easier
  • Instead of hardcoding the presentation in HTML body element, load it dynamically, or better yet transform a Markdown document into HTML using marked
  • Test your presentation using casperjs

Finally, take a look at these projects:

  • bespoke.js slide engine and how it uses 3D effects for next, prev slides.
  • Markdown to HTML presentation engine slides-now-core based on bespoke.js
  • HTML5 application slides-now based on slides-now-core that adds drag and drop support and offline mode.