Hello Reveal.js + MkDocs — From Zero to Expert: The Complete Guide
The Problem We All Face
Picture this: You’ve just finished an amazing technical presentation. Your slides are polished, your code examples are perfect, and your audience loved it. Fast forward two weeks—you update your documentation with new examples and improved explanations. But wait… your presentation deck now has outdated information. Sound familiar?
This is the documentation drift problem, and it plagues technical teams everywhere. You update the docs, forget the slides, or vice versa. Before you know it, you’re maintaining the same content in multiple places, and they’re all out of sync.
Today, we’re going to fix this once and for all.
What We’re Building
Imagine writing your content once in Markdown and having it automatically published as:
A beautiful MkDocs documentation site
An interactive Reveal.js presentation
A professional PDF deck
All from the same source file. All automated. All deployed automatically via GitHub Actions.
We’ll use IBM watsonx Agentic AI as our example topic, but this pipeline works for anything—from technical tutorials to business presentations.
Before We Begin: Prerequisites
Let’s make sure you have the right tools in your toolkit:
Essential Tools
- Python 3.11+ — We’ll use
uvfor lightning-fast dependency management - Pandoc — The universal document converter (Markdown → Reveal.js)
- Docker — For PDF export via DeckTape
- Git — For version control and GitHub Actions
Optional but Nice to Have
- Node.js + npm — Only if you want to run Reveal’s dev server locally
💡 Pro Tip: Run
make installafter cloning the repo—it bootstraps everything for you automatically.
The Architecture: How It All Fits Together
Before diving into code, let’s understand the workflow:
Single Markdown File (watsonx-agentic-ai.md)
│
├─→ [Pandoc] → Reveal.js HTML
│ │
│ └─→ [DeckTape] → PDF
│
└─→ [MkDocs] → Documentation Site
│
└─→ [GitHub Pages] → 🌐 Live Site
The magic? One source of truth. Multiple output formats. Zero manual synchronization.
Project Structure: Setting Up Your Workspace
Here’s how we’ll organize everything:
your-project/
├── docs/
│ ├── index.md # Homepage
│ ├── watsonx-agentic-ai.md # 📝 Your single source of truth
│ └── slides/ # Generated artifacts
│ ├── watsonx-agentic-ai.html # ← Generated by Pandoc
│ └── watsonx-agentic-ai.pdf # ← Generated by DeckTape
├── scripts/
│ ├── generate_slides.sh # Pandoc automation
│ ├── export_pdf.sh # DeckTape automation
│ └── bootstrap.sh # Tool installation helper
├── mkdocs.yml # MkDocs configuration
├── Makefile # Convenient commands
└── .github/workflows/
└── deploy.yml # CI/CD pipeline
Why docs/slides/ and not site/slides/?
Great question! MkDocs copies everything from docs/ to site/ during build. If we generated files directly into site/, the next build would wipe them out. By generating into docs/slides/, they become part of the source that MkDocs copies over.
Level 1: Writing Your Source Markdown
This is where the magic begins. We’ll write one Markdown file that works beautifully as both documentation and presentation.
The Secret Sauce: Header Attributes
Reveal.js uses special attributes on headers to control slide behavior. The beauty? MkDocs completely ignores these attributes—they just pass through as HTML attributes.
Create docs/watsonx-agentic-ai.md:
---
title: "IBM watsonx & Agentic AI"
author: "Ruslan Magana Vsevolodovna"
date: "2025-11-12"
---
# Introduction to Agentic AI
This document explores the emerging field of Agentic AI, with a focus on IBM watsonx capabilities.
## What is Agentic AI? {data-transition="slide"}
Agentic AI refers to systems designed to autonomously perceive, reason, plan, and act in complex environments to achieve specific goals.
- **Autonomy** — Acts without constant human intervention
- **Goal-oriented behavior** — Works toward defined objectives
- **Adaptability & learning** — Improves through experience
**Key insight:** Unlike simple prompt-response systems, agents can proactively plan multi-step actions.
## IBM watsonx.ai for Agents {data-transition="fade"}
IBM watsonx.ai provides foundational models and enterprise-grade tooling to build, deploy, and govern Agentic AI solutions.
**Core capabilities:**
- Foundation models (Llama, Granite, Mixtral)
- Prompt engineering tooling
- Governance framework (data lineage, risk, policies)
```python
# Illustrative code — check IBM docs for latest SDKs
from ibm_watsonx_ai import Model
model = Model(
model_id="ibm/granite-13b-chat-v2",
credentials={"apikey": "YOUR_API_KEY"},
project_id="YOUR_PROJECT_ID"
)
response = model.generate("Explain Agentic AI in one sentence.")
print(response)
::: notes 💡 Speaker note: Emphasize governance early for enterprise audiences—this is what differentiates watsonx from consumer AI tools. :::
Building an Agent {data-background-color=”#0f172a” data-transition=”convex”}
The agent development workflow consists of four critical phases:
- Define the goal — What problem are we solving? What does success look like?
- Select tools — APIs, databases, search engines, calculators, internal services
- Orchestrate control flow — Tool selection, planning, error handling, retries
- Evaluate & iterate — Offline testing, online monitoring, continuous improvement
# Conceptual CLI flow (illustrative)
agent-builder create my-wx-agent \
--goal "answer customer queries" \
--llm "ibm/granite-13b-chat-v2" \
--tools "search,calculator,crm-api"
agent-builder deploy my-wx-agent --env production
Ethical Considerations {data-transition=”fade”}
As we build increasingly autonomous systems, we must prioritize:
- Transparency & observability — Users should understand when they’re interacting with AI
- Bias & fairness checks — Regular audits and diverse training data
- Accountability & human oversight — Clear escalation paths and human-in-the-loop for critical decisions
“With great automation comes great responsibility.” ```
📝 Markdown Tips for Dual-Purpose Content
##headings → New horizontal slides (with--slide-level=2)###headings → Vertical sub-slides (deeper dives)- Speaker notes → Wrap in
::: notes ... :::(invisible in docs, visible in presenter mode) - Fragments → Use
<span class="fragment">text</span>for progressive reveal - Backgrounds → Add
{data-background-color="#hex"}to headers - Transitions → Use
{data-transition="slide|fade|convex|zoom"}
Level 2: Generating Reveal.js HTML Slides
Now comes the transformation. We’ll use Pandoc to convert our Markdown into a beautiful Reveal.js presentation.
The Challenge: Pandoc Version Compatibility
Different Pandoc versions expect different Reveal.js versions:
- Pandoc < 2.12 → Expects Reveal v3 (paths like
css/*,js/*) - Pandoc ≥ 2.12 → Expects Reveal v4 (paths like
dist/*)
Our script auto-detects your Pandoc version and selects the right CDN.
Create scripts/generate_slides.sh:
#!/usr/bin/env bash
# Auto-detects Pandoc version and selects compatible Reveal.js CDN
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.."; pwd -P)"
SOURCE_MD="${SOURCE_MD:-${ROOT}/docs/watsonx-agentic-ai.md}"
OUT_DIR="${OUT_DIR:-${ROOT}/docs/slides}"
DECK_NAME="${DECK_NAME:-$(basename "${SOURCE_MD%.*}")}"
HTML_OUT="${OUT_DIR}/${DECK_NAME}.html"
# Check if Pandoc is installed
command -v pandoc >/dev/null 2>&1 || {
echo "❌ Pandoc not found. Install from: https://pandoc.org/installing.html"
exit 1
}
# Get Pandoc version
PANDOC_VER="$(pandoc -v | head -n1 | awk '{print $2}')"
ver_ge() {
[ "$(printf '%s\n' "$2" "$1" | sort -V | head -n1)" = "$2" ]
}
mkdir -p "${OUT_DIR}"
# Determine embed flag based on Pandoc version
if pandoc --help | grep -q -- "--embed-resources"; then
EMBED_FLAG="--embed-resources"
else
EMBED_FLAG="--self-contained"
fi
# Configurable theme and transition
REVEAL_THEME="${REVEAL_THEME:-league}"
REVEAL_TRANSITION="${REVEAL_TRANSITION:-slide}"
# Select Reveal.js CDN based on Pandoc version
if ver_ge "${PANDOC_VER}" "2.12"; then
REVEAL_VERSION="${REVEAL_VERSION:-4.6.0}"
REVEAL_URL="https://cdn.jsdelivr.net/npm/reveal.js@${REVEAL_VERSION}"
echo "✅ Using Reveal v4 CDN (${REVEAL_URL}) with Pandoc ${PANDOC_VER}"
else
REVEAL_URL="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.9.2"
echo "✅ Using Reveal v3 CDN (${REVEAL_URL}) with Pandoc ${PANDOC_VER}"
fi
echo "📝 Generating Reveal.js slides → ${HTML_OUT}"
# Generate slides with full HD dimensions
pandoc \
--standalone \
--to=revealjs \
--slide-level=2 \
${EMBED_FLAG} \
--variable "revealjs-url=${REVEAL_URL}" \
--variable "theme=${REVEAL_THEME}" \
--variable "transition=${REVEAL_TRANSITION}" \
--variable slideNumber=true \
--variable hash=true \
-V width=1920 \
-V height=1080 \
--metadata=pagetitle:"IBM watsonx & Agentic AI" \
-o "${HTML_OUT}" \
"${SOURCE_MD}"
echo "✅ Slides generated successfully at ${HTML_OUT}"
Make it executable:
chmod +x scripts/generate_slides.sh
Run it:
make slides
# or directly:
bash scripts/generate_slides.sh
You’ll see output like:
✅ Using Reveal v3 CDN with Pandoc 2.9.2.1
📝 Generating Reveal.js slides → /path/to/docs/slides/watsonx-agentic-ai.html
✅ Slides generated successfully
Level 3: Exporting to PDF
Here’s where things get interesting. The original tutorial had a critical bug—it only exported the first slide. Let’s fix that.
Understanding the Problem
DeckTape was detecting all 6 slides but only capturing 1. Why?
The root causes:
- Timing was too aggressive — 1500ms isn’t enough for full HD (1920×1080) slides
- No pause between slides — DeckTape moved too fast
- Open-ended range —
1-confused the navigation - Insufficient memory — Complex slides need more shared memory
The Solution: Extended Timing + Explicit Range
Create scripts/export_pdf.sh:
#!/usr/bin/env bash
# Robust DeckTape export — CAPTURES ALL SLIDES, NOT JUST THE FIRST ONE
set -euo pipefail
# ========== Configuration (can be overridden via environment) ==========
DECKTAPE_IMAGE="${DECKTAPE_IMAGE:-astefanutti/decktape:latest}"
SLIDE_SIZE="${SLIDE_SIZE:-1920x1080}"
SLIDES_RANGE="${SLIDES_RANGE:-1-100}" # ⚠️ CRITICAL FIX: Explicit range, not open-ended
# ⏱️ CRITICAL TIMING FIXES FOR FULL HD SLIDES
LOAD_PAUSE="${LOAD_PAUSE:-8000}" # 8 seconds initial load (was 1500!)
PAUSE="${PAUSE:-2000}" # 2 seconds between slides (was missing!)
# Docker configuration for CI compatibility
if [ -z "${DOCKER_RUN_EXTRA:-}" ]; then
DOCKER_RUN_EXTRA="--shm-size=2g -e HOME=/tmp -u $(id -u):$(id -g)" # Increased to 2GB
fi
# Chrome arguments for reliable rendering
CHROME_ARGS=(
--chrome-arg=--allow-file-access-from-files
--chrome-arg=--disable-web-security
--chrome-arg=--autoplay-policy=no-user-gesture-required
--chrome-arg=--no-sandbox
--chrome-arg=--disable-setuid-sandbox
--chrome-arg=--disable-dev-shm-usage
--chrome-arg=--disable-gpu
--chrome-arg=--hide-scrollbars
--chrome-arg=--mute-audio
--chrome-arg=--disable-background-timer-throttling
--chrome-arg=--disable-renderer-backgrounding
--chrome-arg=--disable-backgrounding-occluded-windows
--chrome-arg=--user-data-dir=/tmp/chrome-user
--chrome-arg=--crash-dumps-dir=/tmp
)
# ========== Paths ==========
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.."; pwd -P)"
HTML_REL="${HTML_REL:-docs/slides/watsonx-agentic-ai.html}"
PDF_REL="${PDF_REL:-docs/slides/watsonx-agentic-ai.pdf}"
HTML_HOST="${ROOT}/${HTML_REL}"
PDF_HOST="${ROOT}/${PDF_REL}"
# ========== Pre-flight Checks ==========
command -v docker >/dev/null 2>&1 || {
echo "❌ Docker required. Install from: https://www.docker.com/"
exit 1
}
[ -f "${HTML_HOST}" ] || {
echo "❌ HTML file not found: ${HTML_HOST}"
echo " Run 'make slides' first to generate the HTML."
exit 1
}
# Ensure DeckTape image is available
docker image inspect "${DECKTAPE_IMAGE}" >/dev/null 2>&1 || {
echo "⬇️ Pulling DeckTape image..."
docker pull "${DECKTAPE_IMAGE}"
}
# Create output directory
mkdir -p "$(dirname "${PDF_HOST}")"
# ========== Container Configuration ==========
MOUNT_POINT="/work"
HTML_URL="file://${MOUNT_POINT}/${HTML_REL}" # ⚠️ NO ?print-pdf parameter
PDF_PATH="${MOUNT_POINT}/${PDF_REL}"
# ========== Export to PDF ==========
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🎬 Exporting Reveal.js Slides to PDF"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " 📄 Source: ${HTML_HOST}"
echo " 📦 Destination: ${PDF_HOST}"
echo " 🎯 Slide range: ${SLIDES_RANGE}"
echo " 📐 Size: ${SLIDE_SIZE}"
echo " ⏱️ Load pause: ${LOAD_PAUSE}ms (extended for full HD)"
echo " ⏸️ Slide pause: ${PAUSE}ms (allows navigation)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
docker run --rm -t \
${DOCKER_RUN_EXTRA} \
-v "${ROOT}:${MOUNT_POINT}" \
"${DECKTAPE_IMAGE}" \
reveal \
--size "${SLIDE_SIZE}" \
--slides "${SLIDES_RANGE}" \
--load-pause "${LOAD_PAUSE}" \
--pause "${PAUSE}" \
"${CHROME_ARGS[@]}" \
"${HTML_URL}" \
"${PDF_PATH}"
# ========== Verification ==========
if [ -f "${PDF_HOST}" ]; then
PDF_SIZE=$(du -h "${PDF_HOST}" | cut -f1)
# Optional: Count pages if pdfinfo is available
if command -v pdfinfo >/dev/null 2>&1; then
PAGE_COUNT=$(pdfinfo "${PDF_HOST}" 2>/dev/null | grep -i "^Pages:" | awk '{print $2}' || echo "unknown")
else
PAGE_COUNT="unknown (install poppler-utils for page count)"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ PDF Generated Successfully!"
echo " 📄 File: ${PDF_HOST}"
echo " 💾 Size: ${PDF_SIZE}"
echo " 📃 Pages: ${PAGE_COUNT}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Warn if file is suspiciously small (likely only 1 page)
PDF_BYTES=$(stat -f%z "${PDF_HOST}" 2>/dev/null || stat -c%s "${PDF_HOST}" 2>/dev/null || echo 0)
if [ "${PDF_BYTES}" -lt 100000 ]; then
echo "⚠️ WARNING: PDF is small (${PDF_BYTES} bytes) - may only contain 1 page"
echo " Try increasing LOAD_PAUSE and PAUSE: LOAD_PAUSE=12000 PAUSE=3000 make pdf"
fi
else
echo "❌ ERROR: PDF was not generated at ${PDF_HOST}"
exit 1
fi
Make it executable:
chmod +x scripts/export_pdf.sh
What Changed?
| Parameter | Before | After | Impact |
|---|---|---|---|
--slides |
1- (open) |
1-100 (explicit) |
Better navigation |
--load-pause |
1500ms | 8000ms | Full initialization time |
--pause |
(missing) | 2000ms | Navigation between slides |
--shm-size |
1g | 2g | More memory for rendering |
| URL | ?print-pdf |
(removed) | Uses native rendering |
Run it:
make pdf
You should now see:
Printing slide #/title-slide (1/6) ...
Printing slide #/introduction-to-agentic-ai (2/6) ...
Printing slide #/what-is-agentic-ai (3/6) ...
Printing slide #/ibm-watsonxai-for-agents (4/6) ...
Printing slide #/building-an-agent (5/6) ...
Printing slide #/ethical-considerations (6/6) ...
Printed 6 slides ✅
Level 4: Configuring MkDocs
Time to configure our documentation site. We’ll use the Material theme for a modern, professional look.
Create mkdocs.yml:
# Site Configuration
site_name: "Hello MkDocs + Reveal.js"
site_description: "Write once in Markdown, publish as docs, slides, and PDF"
site_url: https://YOUR_USERNAME.github.io/YOUR_REPO_NAME/
repo_url: https://github.com/YOUR_USERNAME/YOUR_REPO_NAME
repo_name: YOUR_USERNAME/YOUR_REPO_NAME
edit_uri: edit/main/docs/
strict: true
# Theme
theme:
name: material
palette:
- scheme: default
primary: indigo
accent: indigo
toggle:
icon: material/brightness-7
name: Switch to dark mode
- scheme: slate
primary: indigo
accent: indigo
toggle:
icon: material/brightness-4
name: Switch to light mode
features:
- navigation.tabs
- navigation.sections
- navigation.top
- search.suggest
- search.highlight
- content.code.copy
# Navigation
nav:
- Home: index.md
- IBM watsonx & Agentic AI: watsonx-agentic-ai.md
- Slides:
- 🎬 HTML Deck: slides/watsonx-agentic-ai.html
- 📄 PDF Download: slides/watsonx-agentic-ai.pdf
# Markdown Extensions
markdown_extensions:
- attr_list
- admonition
- tables
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.superfences
- pymdownx.inlinehilite
- pymdownx.snippets
- toc:
permalink: true
# Plugins
plugins:
- search
Preview locally:
make serve
# Opens at http://127.0.0.1:8000/
Level 5: Makefile for Convenience
Let’s create a Makefile to simplify our workflow:
# Makefile — Automated workflow for MkDocs + Reveal.js + PDF
SHELL := /usr/bin/env bash
# Configuration
DECKTAPE_IMAGE ?= astefanutti/decktape:latest
SLIDE_SIZE ?= 1920x1080
UV_PY ?= 3.11
PDF_SLIDES ?= 1-100
.PHONY: help install install-tools bootstrap slides pdf serve serve_noslides build clean check
help:
@echo "Available targets:"
@echo " install - Install tools and Python dependencies"
@echo " slides - Generate Reveal.js HTML slides"
@echo " pdf - Export slides to PDF (with extended timing)"
@echo " serve - Run MkDocs development server"
@echo " build - Build complete site (slides + PDF + docs)"
@echo " clean - Remove all generated files"
@echo " check - Verify external tools are installed"
install: install-tools
@set -e; \
if ! command -v uv >/dev/null 2>&1; then \
echo "📦 Installing uv..."; \
curl -LsSf https://astral.sh/uv/install.sh | sh; \
fi; \
export PATH="$$HOME/.local/bin:$$HOME/.cargo/bin:$$PATH"; \
echo "🐍 Ensuring Python $(UV_PY)..."; \
uv python install $(UV_PY); \
echo "📚 Installing dependencies..."; \
uv sync; \
echo "✅ Environment ready!"
install-tools: bootstrap
@echo "✅ Tools check complete"
bootstrap:
@bash scripts/bootstrap.sh
slides:
@bash scripts/generate_slides.sh
pdf:
@echo "⏱️ Using extended timing for full HD slides..."
@DECKTAPE_IMAGE="$(DECKTAPE_IMAGE)" \
SLIDE_SIZE="$(SLIDE_SIZE)" \
SLIDES_RANGE="$(PDF_SLIDES)" \
LOAD_PAUSE=8000 \
PAUSE=2000 \
bash scripts/export_pdf.sh
serve: slides
uv run mkdocs serve
serve_noslides:
uv run mkdocs serve
build: slides pdf
uv run mkdocs build --strict
clean:
rm -rf site .cache .venv
rm -f docs/slides/watsonx-agentic-ai.html
rm -f docs/slides/watsonx-agentic-ai.pdf
@echo "🧹 Cleaned all generated files"
check:
@command -v pandoc >/dev/null 2>&1 || (echo '❌ Missing: pandoc' && exit 1)
@command -v docker >/dev/null 2>&1 || (echo '❌ Missing: docker' && exit 1)
@echo "✅ All external tools available"
Usage:
make install # One-time setup
make slides # Generate HTML
make pdf # Generate PDF (with fixes!)
make serve # Preview locally
make build # Full build
Level 6: CI/CD with GitHub Actions
Now for the grand finale—automated deployment to GitHub Pages.
Create .github/workflows/deploy.yml:
name: Build & Deploy — MkDocs + Reveal.js + PDF
on:
push:
branches:
- main
- master
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
build:
name: Build site, slides & PDF
runs-on: ubuntu-latest
steps:
- name: 📥 Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: ⚙️ Configure GitHub Pages
uses: actions/configure-pages@v5
# ========== Generate Slides ==========
- name: 📦 Install Pandoc
run: |
sudo apt-get update
sudo apt-get install -y pandoc
- name: 🎬 Generate Reveal.js HTML slides
run: bash scripts/generate_slides.sh
# ========== Export PDF (WITH FIXES!) ==========
- name: 📄 Export PDF with DeckTape (ALL slides, not just first!)
run: |
mkdir -p docs/slides
uid="$(id -u)"; gid="$(id -g)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🎬 Exporting ALL slides with extended timing..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
docker run --rm -t \
-u "${uid}:${gid}" \
--shm-size=2g \
-e HOME=/tmp \
-v "$PWD":/work \
astefanutti/decktape \
reveal \
--size 1920x1080 \
--slides 1-100 \
--load-pause 8000 \
--pause 2000 \
--chrome-arg=--no-sandbox \
--chrome-arg=--disable-setuid-sandbox \
--chrome-arg=--disable-dev-shm-usage \
--chrome-arg=--disable-gpu \
--chrome-arg=--hide-scrollbars \
--chrome-arg=--mute-audio \
--chrome-arg=--disable-background-timer-throttling \
--chrome-arg=--disable-renderer-backgrounding \
--chrome-arg=--disable-backgrounding-occluded-windows \
--chrome-arg=--user-data-dir=/tmp/chrome-user \
--chrome-arg=--crash-dumps-dir=/tmp \
--chrome-arg=--allow-file-access-from-files \
--chrome-arg=--disable-web-security \
--chrome-arg=--autoplay-policy=no-user-gesture-required \
"file:///work/docs/slides/watsonx-agentic-ai.html" \
"/work/docs/slides/watsonx-agentic-ai.pdf"
- name: ✅ Verify PDF Quality
run: |
if [ ! -f "docs/slides/watsonx-agentic-ai.pdf" ]; then
echo "❌ ERROR: PDF file not found!"
exit 1
fi
PDF_SIZE=$(stat -c%s "docs/slides/watsonx-agentic-ai.pdf")
echo "📊 PDF size: ${PDF_SIZE} bytes"
if [ "${PDF_SIZE}" -lt 100000 ]; then
echo "⚠️ WARNING: PDF is small (${PDF_SIZE} bytes)"
echo " This might indicate only 1 page was captured"
else
echo "✅ PDF size looks good - likely contains all slides"
fi
# ========== Build MkDocs Site ==========
- name: 🐍 Install uv (Python package manager)
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: 🔧 Add uv to PATH
run: echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: 📚 Install Python 3.11 and dependencies
run: |
uv python install 3.11
uv sync
- name: 🏗️ Build MkDocs site
run: uv run mkdocs build --strict
- name: 📤 Upload site artifact
uses: actions/upload-pages-artifact@v3
with:
path: site
deploy:
name: Deploy to GitHub Pages
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: $
steps:
- name: 🚀 Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
Enabling GitHub Pages
- Go to your repository → Settings → Pages
- Under “Build and deployment”, select GitHub Actions
- Push your code to trigger the workflow
Your site will be live at:
https://YOUR_USERNAME.github.io/YOUR_REPO_NAME/
Power-Ups: Advanced Techniques
🎨 Custom Themes
# Generate with a different theme
REVEAL_THEME=night bash scripts/generate_slides.sh
Available themes: beige, black, blood, league, moon, night, serif, simple, sky, solarized, white
🎬 Custom Transitions
REVEAL_TRANSITION=zoom bash scripts/generate_slides.sh
Options: none, fade, slide, convex, concave, zoom
📐 Different Slide Sizes
# For 16:9 widescreen
SLIDE_SIZE=1600x900 bash scripts/export_pdf.sh
# For 4:3 classic
SLIDE_SIZE=1024x768 bash scripts/export_pdf.sh
🧮 Math Support
Add to your generate_slides.sh:
pandoc \
--mathjax \
# ... other flags
Then use LaTeX in your Markdown:
Inline math: $E = mc^2$
Display math:
$$\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}$$
🖼️ Image Handling
<!-- Works in both docs and slides -->

<!-- Slide-specific sizing -->
{width=400px}
📊 Code Highlighting
# Add to generate_slides.sh
--highlight-style=pygments
Testing Your Setup
Quick Smoke Test
# Start fresh
make clean
# Generate everything
make slides
make pdf
# Should see output like:
# Printing slide #/title-slide (1/6) ...
# Printing slide #/introduction-to-agentic-ai (2/6) ...
# ...
# Printed 6 slides ✅
# Verify files
ls -lh docs/slides/
# Should show:
# watsonx-agentic-ai.html (~200KB)
# watsonx-agentic-ai.pdf (~400KB+)
# Preview
make serve
Testing PDF Quality
# Install pdfinfo (optional)
sudo apt-get install poppler-utils
# Check page count
pdfinfo docs/slides/watsonx-agentic-ai.pdf | grep Pages
# Should show: Pages: 6 (or your actual slide count)
Workflow Recap: Your Daily Commands
# 🔧 One-time setup
make install
# ✍️ Edit your content
vim docs/watsonx-agentic-ai.md
# 🎬 Regenerate slides
make slides
# 📄 Regenerate PDF
make pdf
# 👀 Preview locally
make serve
# → http://127.0.0.1:8000
# 🚀 Deploy (automatic via git push)
git add .
git commit -m "Update content"
git push origin main
Performance Tips
Local Development
# Skip PDF generation during iteration
make slides
make serve_noslides
CI Optimization
The workflow caches Docker images automatically. First run: ~3 minutes. Subsequent runs: ~1 minute.
Going Further
Add More Content
Just create more Markdown files in docs/ and add them to mkdocs.yml:
nav:
- Home: index.md
- Watsonx AI: watsonx-agentic-ai.md
- Getting Started: getting-started.md # New!
- Slides:
- Watsonx: slides/watsonx-agentic-ai.html
- Getting Started: slides/getting-started.html # New!
Custom Domain
In your repo settings → Pages → Custom domain:
docs.yourdomain.com
Add a CNAME file to docs/:
echo "docs.yourdomain.com" > docs/CNAME
Analytics
Add to mkdocs.yml:
extra:
analytics:
provider: google
property: G-XXXXXXXXXX
Conclusion
You’ve just built a professional documentation and presentation pipeline that:
- Saves time — Write once, publish everywhere
- Prevents drift — Single source of truth
- Looks professional — Material theme + Reveal.js
- Deploys automatically — GitHub Actions
- Works reliably — Extended timing fixes PDF issues
The best part? It scales. Whether you’re documenting one feature or an entire product line, the workflow stays the same.
Resources
- 📖 Tutorial Repository: github.com/ruslanmv/hello-reveal-mkdocs
- 🎬 Reveal.js Documentation: revealjs.com
- 📚 MkDocs Documentation: mkdocs.org
- 🔧 Pandoc Documentation: pandoc.org
- 🐳 DeckTape GitHub: github.com/astefanutti/decktape
Happy documenting! 🚀
If this tutorial helped you, consider giving the repository a ⭐ on GitHub!
Leave a comment