reveal-template
This is a helper package to simplify the generation of reveal.js presentations using Markdown files only. It contains a set of (optional) reveal plugins to simplify the creation of lectures.
It is primarily used by myself so it is quite opinionated and makes certains assumptions on the structure of the lecture. Markdown files should follow a certain naming pattern:
- Single level:
00 - Introduction.md,01 - Bla.md, etc. - Two level:
00 - Introduction.md,01 - Topic 1.md,01a - Sub-Topic 1.md,01b - Sub-Topic 1.md,02 - Topic 2, etc.
dennis_build compiles the markdown files into a fully static dist/ folder (Vite, minified/hashed assets; the markdown is prerendered to reveal <section> HTML at build time — the real reveal markdown plugin runs in jsdom, so output is identical to former runtime parsing — and the pages are minified). The result can be published on any web server and does not require server-side components. Markdown parser options default to { smartypants: true } and can be overridden via "farberg": { "markdown": { ... } } in the lecture's package.json.
Requirements and Installation
To use this project, node.js and npm are required.
Create a new lecture:
- Create a new folder and initialize an empty node.js project (e.g.,
npm init --yes) - Add this package as a dependency (
npm i @farberg/reveal-template) - Read the documentation below
Usage
- Create a file
package.jsondescribing your presentation (e.g., like this one) —title,authorsandhomepageend up on the title slide, QR code and in the combined PDF name. - Create your presentation in Markdown (see this file for an example).
- Optionally create a
deck.config.jsexporting{ revealOptions, mermaid }for deck-specific plugins/config. - Run
npx dennis_build— this producesdist/:- one page per deck at a pretty URL (
02a - Immutable Infrastructure.md->/02a-immutable-infrastructure, extensionless URLs need a small rewrite rule, see below), plus a<slug>-printvariant per deck index.htmlredirecting to the first decktoc.json, a machine-readable table of contents rendered by the show-toc plugin, with the sections of every deck for the contents overlay- hashed/minified JS/CSS under
assets/, staticimg/,code/,data/copied as-is (configurable via"farberg": { "assetDirs": [...], "assetExclude": [...] }inpackage.json)
- one page per deck at a pretty URL (
Other modes:
npx dennis_build dev— Vite dev server with prod-identical routes and auto-reload- optional dev hooks run commands when matching files change, then reload the browser, e.g. regenerate SVGs from PowerPoint sources:
"farberg": { "devHooks": [{ "watch": "^img/[^~][^/]*\\.pptx$", "run": "npm run img" }] }inpackage.json(watchis a regex on the repo-relative path; changes are debounced and a hook never runs twice at once)
- optional dev hooks run commands when matching files change, then reload the browser, e.g. regenerate SVGs from PowerPoint sources:
npx dennis_build --watch— rebuilddist/on every change (use any static server ondist/, e.g. VS Code Go Live)npx dennis_build --toc-only— refresh the PDF links indist/toc.jsonafter PDFs were generatednpx dennis_serve_dist dist— tiny static server with extensionless URLs (used by CI for PDF rendering)
Apache rewrite rule for extensionless URLs (put in .htaccess, it is copied into dist/):
RewriteEngine on
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.+)$ $1.html [L]
Plugins
Click on the link for documentation
Tested and working
| Name | Description |
|---|---|
| reveal-plugin-dir-tree.js | Create a zip from existing files for download and display a tree of files |
| reveal-plugin-html-example.js | Renders a code example as innerHTML of a target element |
| reveal-plugin-prefix-with-base-url.js | Prefixes the pages base URL to an element's inner Text. Optionally converts it to inline code. |
| reveal-plugin-show-attribution.js | Shows credits on the lower right corner |
| reveal-plugin-show-code-snippets.js | Shows a code snippet loaded from a file (begin/end markers, Nth occurrence, optionally incl. marker lines) |
| reveal-plugin-show-qr-code.js | Display a QR code and link to the presentation on the slides slides |
| reveal-plugin-show-title.js | Create a title slide automatically |
| reveal-plugin-show-toc.js | Renders the table of contents from the toc.json generated by dennis_build |
| reveal-plugin-navigation.js | Bottom strip and navigation: main-flow slide number, arrow to vertical slides, menu levels, keys A and O |
| reveal-plugin-course-profile.js | Course profiles: badges on deck title slides and exercise slides, picker on key U (see Course Profiles) |
| reveal-plugin-contents.js | Contents overlay on key I or ?contents: agenda with the level-1 and level-2 sections of every deck as one scrollable page (see Contents Overlay) |
Non-functional, untested or in development
| Name | Description |
|---|---|
| reveal-plugin-modify-font-size.js | Add keybindings + and - to change base font size |
| reveal-plugin-toggle-solutions.js | Show of hide solutions |
Files of a Code Example
Add data-files to a code embed to show a collapsed tree of the files the example works with, built from the file system at build time: <a data-code="bash" data-begin="# Begin demo" data-end="# End demo" data-link data-files href="code/x/steps/demo.sh">demo.sh</a>. Without a value, the directory is the one the script runs in (cd "$(dirname "$0")/.." in the script means the parent of its directory, otherwise its own directory); data-files="code/x" sets it explicitly. Only files that git would publish are listed (no ignored files), minus farberg.assetExclude. A click opens the tree (links, view, zip download).
Chapter Overview
Put <div data-chapter-overview></div> on a slide; the build replaces it with one entry per slide that has a data-summary, in slide order, each linking to its slide (title, grey note and sub-entries are links, highlighted on hover): <!-- .slide: data-name="Object Storage" data-summary="Object storage with an S3 API (SeaweedFS)" -->. data-level (default 1): level 1 is a paragraph like plain text on a slide, level 2 a list item below it, level 3 a nested list item. A trailing (...) is shown as a comment. The key O jumps to this slide (ESC still opens the slide overview), A to the agenda.
Course Profiles (?profile=)
One lecture site can serve several courses with different agendas. A start link with ?profile=<id> (e.g. https://example.org/lecture/01-introduction?profile=wdski24) stores the id in the browser's localStorage; ?profile=none removes it. The profile is profiles/<id>.json in the lecture repository (copied to dist/profiles by the build), so the agenda can change during the semester without a new link:
{
"title": "WDSKI24",
"decks": { "03f": "prior", "04b": { "status": "core", "date": "2026-11-23" }, "04d": { "status": "core", "sections": { "Access Control": "skip", "Supply Chain": "optional" } }, "04e": "skip", "05b": { "status": "optional", "date": "2026-12-14" } },
"exercises": { "tilt": "optional", "keycloak": "skip" }
}
Deck keys are number plus sub letter (04a, 05 for a chapter deck), exercise keys are slide ids. Status: prior (prior knowledge), core (default), optional, skip. The agenda (data-toc-src) greys out prior knowledge, shows optional decks in italics, strikes skipped ones and adds a date column; title slides and listed exercise slides get a small badge. Without a profile nothing changes.
sections in a deck entry restricts a deck to some of its parts: keys are the data-name of the first slide of a section (the name in the menu bar, numbered like Exercise 2 when it occurs several times), a section ends at the next named slide of the same or a higher data-level. A section with skip is removed from the presentation (slides, menu item, chapter overview lines; slide numbers count without it). With prior or optional every slide of the section gets the badge, its menu item and its chapter overview lines are greyed out (prior) or set in italics (optional).
The key U opens a picker with all profiles (the build lists profiles/*.json with their titles in toc.json) and "Full agenda"; choosing one sets it like a start link and reloads the page.
Contents Overlay (I, ?contents)
The key I opens a compact summary of the whole lecture as one scrollable page over the slides: every deck of the agenda with the data-summary of its level-1 and level-2 slides (the lines of its chapter overview). dennis_build collects them into toc.json (sections of each deck). Every line links to its slide, I, Escape or the close button closes it.
?contents in the URL opens it on load, also together with a course profile, e.g. https://example.org/lecture/?profile=wdski24&contents (the start page keeps the query when it redirects). With a profile, decks and sections follow its status: prior knowledge greyed out, optional in italics, skipped struck through.
Terminal Recordings (asciinema)
Embed a recording with <asciinema data-conf='{ "cols": 120, "rows": 30, "theme": "dhbw-light", "autoPlay": true }' src="img/demo.cast" />; data-conf is passed to the asciinema player. Besides the player's built-in themes, the template provides dhbw-light (black on white, palette readable on white; see css/asciinema-themes.css).
Conversion to PDF
Run npx dennis_build && npx dennis_generate_pdfs to render dist/pdfs/<slug>.pdf per deck plus a combined PDF (puppeteer-based; CI uses Gotenberg against the same <slug>-print pages instead).
Local Development
To develop on this repository:
- Check out this repository (
git clone https://github.com/pfisterer/reveal-template.git) - Run
npm installto install required dependencies - In a lecture repo, run
npm link @farberg/reveal-template, then usenpx dennis_build devthere
Publish a new version (maintainers only)
# Bump version in `package.json`
# Run this command to publish on npm
npm login && sleep 3s && npm publish --access public