Hello Reveal.js + MkDocs — From Zero to Expert: The Complete Guide

14 minute read

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 uv for 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 install after 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:

  1. Define the goal — What problem are we solving? What does success look like?
  2. Select tools — APIs, databases, search engines, calculators, internal services
  3. Orchestrate control flow — Tool selection, planning, error handling, retries
  4. 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:

  1. Timing was too aggressive — 1500ms isn’t enough for full HD (1920×1080) slides
  2. No pause between slides — DeckTape moved too fast
  3. Open-ended range1- confused the navigation
  4. 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

  1. Go to your repository → SettingsPages
  2. Under “Build and deployment”, select GitHub Actions
  3. 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 -->
![Architecture Diagram](images/architecture.png)

<!-- Slide-specific sizing -->
![Logo](images/logo.png){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

Happy documenting! 🚀

If this tutorial helped you, consider giving the repository a ⭐ on GitHub!

Posted:

Leave a comment