Pretty Slides with Pandoc and impress.js

Posted on June 5, 2013

I recently saw Meredith’ talk “LANGSEC 2011-2016” at CONFidence in Krakow. Besides the nice content (but who would have expected anything else?) I also liked the “non-linear” style of the presentation. So I dug a bit into all those new and fancy tools for making slides and the first one I found was Prezi. I was rather disappointed when I realized it was a cloud-based software written in Flash.

Fortunately, there are a couple of other tools that offer similar effects. One of them is impress.js. There are already plenty of tutorials on how to use impress.js, so I’m not going to write yet another one. However, as I don’t feel very comfortable with writing raw HTML, I started looking for an editor. The github wiki mentions Strut, but it’s a rather basic tool. Also, I actually wanted to have something text-based for editing my slide decks. Using Pandoc, this is pretty straight-forward. I found this document, describing the setup. It is not very complicated to get it running. However, there are a number of features that are not enabled by default.

Being a LaTeX guy, I at least wanted to have the possibility to typeset mathematical formulas, preferably using LaTeX syntax. Fortunately, Pandoc comes with built-in support for that. However, in order to be able to include them in HTML output, you need some additional software. I use MathJax for that purpose. You can simply use the --mathjax switch for Pandoc.

Furthermore, source code syntax highlighting is a must-have for me. Again, Pandoc already has support for that. You simply have to include a CSS file that provides proper styles for you syntax highlighting.

Another feature I wanted to have is PDF export. Conference organizers often ask for PDF slides and I also consider it not too polite to force people to use bloated^Wshiny technology such as web browsers for viewing presentations. As you might have guessed, that feature is also provided by Pandoc already. You can use the -t beamer switch to produce nice PDF slides using LaTeX Beamer. I had to download ifluatex.sty in order to make it work, but that might be due to my weird TeTeX installation. The default theme Pandoc uses for Beamer is not very pretty (i.e., it’s not the theme I prefer ;) ). Using -V theme:Warsaw -V colortheme:seahorse helps.

Oh, and some random note: --self-contained and mathjax=... don’t play together too well; you’ll not got pretty-printed formulas if you combine the two switches.

OK, I guess by now you’d like to see how it all works together, so here we go. First, install the software that we’ll need:

Now use your OS’ package manager to install Pandoc - if you have the Haskell Platform installed, you can type:

I’m using a simple HTML template for generating the slides. Save the following file as impress-template.html:

For source code, I’m using the file sourcecode.css below:

Now, we still need a CSS file for styling the slides themselves. That is done by impress-style.css:

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, 
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
    margin: 0;
    padding: 0;
    border: 0;
    font-size: 100%;
    font: inherit;
    vertical-align: baseline;
}

article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
    display: block;
}

body {
    line-height: 1;
}

blockquote, q {
    quotes: none;
}

ol, ul {
}

blockquote:before, blockquote:after,
q:before, q:after {
    content: '';
    content: none;
}

section {
   overflow: hidden;
   border-style:solid;
   border-width:2px;
   height: 768px;
   word-wrap: break-word;
   background-color: white;
}

h1 {
   width: 900px;
   margin-left: -40px;
   padding-left: 10px;
   margin-top: -40px;
   color: black;
   background-color: #7777bb;
   min-height: 50px;
}

table {
    border-collapse: collapse;
    border-spacing: 0;
}

body {
    font-family: 'PT Sans', sans-serif;
    min-height: 740px;
    background-color: gray;
}

b, strong { font-weight: bold }
i, em { font-style: italic }

a {
    color: inherit;
    text-decoration: none;
    padding: 0 0.1em;
    background: rgba(255,255,255,0.5);
    text-shadow: -1px -1px 2px rgba(100,100,100,0.9);
    border-radius: 0.2em;

    -webkit-transition: 0.5s;
    -moz-transition:    0.5s;
    -ms-transition:     0.5s;
    -o-transition:      0.5s;
    transition:         0.5s;
}

a:hover,
a:focus {
    background: rgba(255,255,255,1);
    text-shadow: -1px -1px 2px rgba(100,100,100,0.5);
}


.fallback-message {
    font-family: sans-serif;
    line-height: 1.3;

    width: 780px;
    padding: 10px 10px 0;
    margin: 20px auto;

    border: 1px solid #E4C652;
    border-radius: 10px;
    background: #EEDC94;
}

.fallback-message p {
    margin-bottom: 10px;
}

.impress-supported .fallback-message {
    display: none;
}

.step {
    position: relative;
    width: 900px;
    padding: 40px;
    margin: 20px auto;

    -webkit-box-sizing: border-box;
    -moz-box-sizing:    border-box;
    -ms-box-sizing:     border-box;
    -o-box-sizing:      border-box;
    box-sizing:         border-box;

    font-family: 'PT Serif', georgia, serif;
    font-size: 32px;
    line-height: 1.5;
}

.impress-enabled .step {
    margin: 0;
    opacity: 0.3;

    -webkit-transition: opacity 1s;
    -moz-transition:    opacity 1s;
    -ms-transition:     opacity 1s;
    -o-transition:      opacity 1s;
    transition:         opacity 1s;
}

.impress-enabled .step.active { opacity: 1 }

.slide {
    display: block;

    width: 900px;
    height: 700px;
    padding: 40px 60px;

    background-color: white;
    border: 1px solid rgba(0, 0, 0, .3);
    border-radius: 10px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .1);

    color: rgb(102, 102, 102);
    text-shadow: 0 2px 2px rgba(0, 0, 0, .1);

    font-family: 'Open Sans', Arial, sans-serif;
    font-size: 30px;
    line-height: 36px;
    letter-spacing: -1px;
}

.slide q {
    display: block;
    font-size: 50px;
    line-height: 72px;

    margin-top: 100px;
}

.slide q strong {
    white-space: nowrap;
}

#overview { display: none }

.impress-on-overview .step {
    opacity: 1;
    cursor: pointer;
}

.impress-enabled          { pointer-events: none }
.impress-enabled #impress { pointer-events: auto }

In order to make it more convenient, I’ve written a small Makefile:

And that’s it! After saving all those files, you can finally create sample.md:

Try it out by typing

And open the resulting files in your PDF viewer or your browser, respectively.

For your convenience, I’ve placed the resulting HTML presentation here and the PDF here.