I do not remember when or how I first encountered the CSS Scroll Snap technique, but when I saw it, one thing came to my mind immediately: can we use this technique to make slides? (Spoiler: the answer later was YES)
If you have not heard of it before, here is a quick introduction on MDN to get you started. Basically, it enables elements to “snap” as you scroll over them, which means as soon as you scroll onto an element, it will just quickly jump into your view, instead of waiting for you to continuously scroll over it. If you have used mobile devices to view photos, you should be familiar with this user experience.
Slides give us a similar experience. If we have an HTML page with multiple sections, we could make each section “snap” using this CSS technique. Then this continuous page just becomes a series of “slides”!
Three CSS properties
A new HTML5 presentation framework with simply three CSS properties? That sounds awesome. No one can stop me from working on it.
html {
scroll-snap-type: y mandatory;
}
.slide {
min-height: 100vh; /* occupy at least 100% viewport height */
scroll-snap-align: start;
}
I ended up expanding these 7 lines of CSS into ~100 lines (snap.css) to style several other common elements of slides, such as the table of contents,1 slide titles, page numbers, timers, and special pages.
Organizing content as slide pages via JS
If the HTML code is well-organized as a series of sections (e.g.,
<div class="slide">
), I probably would not need to write much JavaScript.
Eventually I wrote about 200 lines of JS
(snap.js). One third of
the code reorganizes content into slide pages, e.g., convert
<h2>First slide</h2>
<p>Content</p>
<hr>
<h2>Second slide</h2>
<p>More content</p>
<hr>
to
<div class="slide-container">
<div class="slide">
<h2>First slide</h2>
<p>Content</p>
</div>
<div class="slide">
<h2>Second slide</h2>
<p>More content</p>
</div>
</div>
The basic assumption is that you use horizontal rules (<hr>
) to separate
slides, or each of your level-two section (<h2>
) is a slide.
The rest of JS code deals with footnotes, page numbers, timers, slide attributes, and keyboard shortcuts.
Putting together the JS and CSS
If you are lucky (or I was smart enough when writing the JS code), you can turn your HTML page into a slideshow after you add the JS and CSS to the page:
<script src="https://cdn.jsdelivr.net/npm/@xiee/utils/js/snap.min.js" defer></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xiee/utils/css/default.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xiee/utils/css/snap.min.css">
Of course, you would rather write slides in Markdown instead of HTML
You may have frowned upon all the HTML/JS/CSS code and jargon above. The good news is you can forget about them, and click a button to generate slides from Markdown. I will introduce two methods below.
Using the litedown package
You can install the litedown package. Then create a .md
file (or .Rmd
if
you need to execute code chunks) with the YAML metadata:
---
title: "Snap Slides"
output:
litedown::html_format:
meta:
css: [default, snap]
js: [snap]
---
For the body, insert a horizontal rule between two slides like this:
## First slide
**Content**.
---
More _content_ on the next page.
---
## Third slide
If you edit the file in RStudio, click the Knit
button to generate slides. If
you do not, you can call markdown::mark_html()
on the .md
file, or
rmarkdown::render()
on the .Rmd
file.
Using Quarto
The JS/CSS code is not tied to the litedown package. You can definitely use
it in Quarto documents. The key is to set the option minimal: true
and include
the JS/CSS via include-after-body
:
---
title: "Snap Slides"
format:
html:
minimal: true
include-after-body:
text: |
<script src="https://cdn.jsdelivr.net/npm/@xiee/utils/js/snap.min.js" defer></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xiee/utils/css/default.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xiee/utils/css/snap.min.css">
<style type="text/css">header{text-align: center;}</style>
---
If you do not like loading the JS/CSS from the web (the jsdelivr CDN), you can download a copy locally and include the local copy, which will make sure your slides can work offline.
Reasons why I like it
In 2017 I wrote about why I loved remark.js slides.2 Compared to remark.js, most features I liked are present in snap.js. The missing ones include:
- Incremental slides.
- Presenter notes.
- Highlighting specific lines of code.
None of them bother me much. I’m interested in implementing #2, and have a vague
idea how to do it (clone the window and use an Intersection Observer
to sync
the two windows). Other than that, snap.js is feature complete to me.
One big advantage of using snap.js over remark.js/xaringan is that we can use the RStudio Visual Markdown editor (or any visual editor that supports Pandoc / CommonMark).
A possibly unique feature of this slides format is that one slide can be arbitrarily long, in which case the scrolling behavior will become normal (i.e., continuous). If your content does not fit one slide, no worries.
The snap slides framework is very lightweight. That makes it ideal for open
source hackers to tweak its source code if they are not satisfied with anything,
although I think it should be the last resort—you can already customize many
things without touching the source. Want the yolo = TRUE
Karl Broman again? No
problem (I will leave this exercise to you).
So far I have only been bothered by a little problem: I cannot scroll very
quickly through slides no matter if I use my mouse or arrow keys, and I cannot
do anything about it (the limitation comes from the system). I can jump to the
first slide by pressing the Home
key (or Cmd + Up
), and last slide by End
(Cmd + Down
). For the rest of slides, you can scroll to the next slide only
after the current slide has stopped snapping (which takes a short moment). The
overview mode of slides can help a bit, though.
Demo and documentation
The package vignette in the litedown package,
vignette('slides', package = 'litedown')
,
can serve as both the demo and documentation of this slides framework. Enjoy!
-
No, usually you do not need an outline page in a talk. Nobody cares about it, and it simply wastes everyone’s time. But… I promise that I have styled the table of contents beautifully. The audience should appreciate the aesthetics. Just kidding. It is tempting to spend too much time on this page. Move on. ↩︎
-
Will I create yet another HTML slides format in six years? I do not know. ↩︎