Comprehensive RevealJS slideshow system with markdown and Astro support

Summary

Implemented a comprehensive slideshow presentation system that supports both Astro components and markdown files, rendered with RevealJS. The system includes dynamic routing, dedicated index pages, unified navigation controls, and maintains consistent dark theme styling across all presentation interfaces.

Why Care

This implementation transforms the site into a powerful presentation platform that can handle multiple content formats seamlessly. The system enables content creators to build presentations using either structured Astro components or simple markdown files, with professional RevealJS rendering, PDF export capabilities, and responsive design. This significantly expands the site's capabilities for educational content, documentation, and interactive presentations while maintaining consistent UX patterns.

Implementation

Changes Made

File Structure Overview

text
src/
├── components/basics/cta/
│   └── TextCTA.astro                    # Fixed uppercase styling
├── content/slides/
│   ├── docker-intro.md                  # New markdown presentation
│   ├── git-basics.md                    # New markdown presentation  
│   ├── sample-presentation.md           # Demo markdown presentation
│   └── typescript-fundamentals.md      # New markdown presentation
├── layouts/
│   ├── MarkdownSlideDeck.astro         # New markdown renderer component
│   └── OneSlideDeck.astro              # Enhanced with navigation controls
└── pages/slides/
    ├── astro-basics.astro              # New Astro presentation
    ├── index.astro                     # Enhanced main presentations index
    ├── markdown-demo.astro             # Demo markdown usage
    ├── modern-css.astro                # New Astro presentation
    ├── reveal-intro.astro              # Original RevealJS demo
    ├── web-performance.astro           # New Astro presentation
    └── markdown/
        ├── [...slug].astro             # Dynamic markdown slideshow router
        └── index.astro                 # Markdown presentations index

Core Components Created

MarkdownSlideDeck.astro - New component for rendering markdown as RevealJS presentations:
astro
<!-- src/layouts/MarkdownSlideDeck.astro -->
---
interface Props {
  markdownContent: string;
  title?: string;
}

const { markdownContent, title } = Astro.props;

// Process markdown content to split into slides
const processMarkdownToSlides = (content: string) => {
  const horizontalSlides = content.split(/\n---\n/);
  return horizontalSlides.map(slide => {
    const verticalSlides = slide.split(/\n--\n/);
    if (verticalSlides.length > 1) {
      return {
        type: 'vertical',
        slides: verticalSlides.map(s => ({ type: 'single', content: s.trim() }))
      };
    }
    return { type: 'single', content: slide.trim() };
  });
};
---
Dynamic Markdown Router - Filesystem-based routing for markdown presentations:
astro
<!-- src/pages/slides/markdown/[...slug].astro -->
---
export const prerender = false;

import { readFile } from 'fs/promises';
import path from 'path';

const { slug } = Astro.params;
const slidePath = Array.isArray(slug) ? slug.join('/') : slug;

try {
  const filePath = path.join(process.cwd(), 'src/content/slides', `${slidePath}.md`);
  const fileContent = await readFile(filePath, 'utf-8');
  
  // Parse frontmatter and markdown content
  const frontmatterMatch = fileContent.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
  // ... processing logic
} catch (error) {
  // Error handling with detailed feedback
}
---
graph TD A[Presentation System] --> B[Astro Presentations] A --> C[Markdown Presentations] B --> D[/slides/index - Main Index] B --> E[Individual Astro Components] B --> F[OneSlideDeck Layout] C --> G[/slides/markdown/index - Markdown Index] C --> H[Dynamic Route [...slug].astro] C --> I[MarkdownSlideDeck Layout] F --> J[RevealJS Renderer] I --> J J --> K[Navigation Controls] K --> L[Exit Button] K --> M[PDF Export] K --> N[RevealJS Controls] style A fill:#0078ff,color:#fff style J fill:#ff6b6b,color:#fff style K fill:#51cf66,color:#fff

Enhanced Control System

Unified Navigation Controls - Positioned relative to slide content:
css
/* src/layouts/OneSlideDeck.astro & MarkdownSlideDeck.astro */
.control-buttons {
  position: absolute;
  top: max(5rem, 50% - 450px - 40px); /* Position above 900px tall slides */
  right: max(20px, 50% - 800px); /* Align with 1600px wide slides */
  z-index: 30;
  display: flex;
  gap: 10px;
}

.control-button {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 8px 12px;
  background-color: var(--clr-primary, #0078ff);
  color: white;
  border-radius: 4px;
  transition: all 0.2s ease;
}

.exit-button {
  background-color: #6c757d;
}

Presentation Content Examples

Git Basics Markdown Structure:
markdown
---
title: Git Basics
description: Introduction to version control with Git
---

# Git Basics
## Version Control Made Simple
Learn the fundamentals of Git

---

## What is Git?
- **Distributed** version control system
- Created by Linus Torvalds in 2005

---

## Basic Commands
```bash
# Initialize a new repository
git init

# Clone an existing repository
git clone <url>

Example Astro Presentation Structure:

astro
<!-- src/pages/slides/astro-basics.astro -->
<Layout frontmatter={frontmatter}>
  <OneSlideDeck>
    <section class="title-slide">
      <h1>{title}</h1>
      <p>The modern static site builder</p>
    </section>

    <section>
      <h2>What is Astro?</h2>
      <ul>
        <li>Static site generator with a focus on performance</li>
        <li>Ships zero JavaScript by default</li>
      </ul>
    </section>
  </OneSlideDeck>
</Layout>

Technical Details

Markdown Processing Pipeline

  1. File Reading: Direct filesystem access via Node.js readFile
  2. Frontmatter Parsing: Custom regex-based YAML frontmatter extraction
  3. Slide Separation: Split content on --- (horizontal) and -- (vertical) delimiters
  4. RevealJS Integration: Convert parsed structure to RevealJS sections with data-markdown

Component Architecture Decisions

Why MarkdownSlideDeck vs OneSlideDeck?
  • OneSlideDeck: Optimized for Astro component slot content with HTML structure
  • MarkdownSlideDeck: Processes raw markdown strings through RevealJS markdown plugin
  • Shared Styling: Both components use identical CSS and RevealJS configuration
Navigation Control Positioning Algorithm:
javascript
// Calculate position relative to slide dimensions (1600x900)
top: max(5rem, 50% - 450px - 40px)    // Above slides with header clearance
right: max(20px, 50% - 800px)         // Align with slide right edge

Dark Theme Integration

CSS Custom Property Usage:
css
.presentation-card {
  background-color: var(--clr-secondary-bg, #1a1a1a);
  border: 1px solid var(--clr-border, #333);
  color: var(--clr-text, #e0e0e0);
}

.keyboard-hint {
  background-color: var(--clr-secondary-bg, #2a2a2a);
  border: 1px solid var(--clr-border, #444);
  color: var(--clr-text, #e0e0e0);
}

TextCTA Component Fix

Before: Forced uppercase styling
css
.text-cta-text {
  letter-spacing: 0.05em;
  text-transform: uppercase; /* REMOVED */
}
After: Improved readability
css
.text-cta-text {
  letter-spacing: 0.02em; /* Reduced spacing */
  /* text-transform: uppercase; REMOVED */
}

Integration Points

RevealJS Configuration Standardization

Both presentation types use identical RevealJS settings:
javascript
{
  controls: true,
  progress: true,
  slideNumber: true,
  history: true,
  center: true,
  touch: true,
  hideInactiveCursor: true,
  transition: 'slide',
  backgroundTransition: 'fade',
  width: 1600,
  height: 900,
  plugins: [RevealMarkdown, RevealHighlight, RevealNotes, RevealZoom]
}

Routing Integration

URL Structure:
  • Astro presentations: /slides/{presentation-name}
  • Markdown presentations: /slides/markdown/{filename}
  • Main index: /slides/
  • Markdown index: /slides/markdown/

File System Dependencies

Direct Filesystem Access:
  • No content collections required
  • Files read from src/content/slides/*.md
  • Frontmatter parsed with custom regex
  • Error handling for missing files with helpful feedback

Responsive Design Integration

Multi-viewport Support:
  • Desktop: Controls positioned relative to 1600x900 slide dimensions
  • Tablet/Mobile: Fallback positioning with max() functions
  • Half-screen: Buttons stay with slide content, not viewport edges

Documentation

Usage Examples

Creating a New Markdown Presentation:
  1. Create src/content/slides/my-presentation.md
  2. Add frontmatter with title and description
  3. Use --- for slide breaks, -- for vertical slides
  4. Access at /slides/markdown/my-presentation
Creating a New Astro Presentation:
  1. Create src/pages/slides/my-presentation.astro
  2. Import OneSlideDeck component
  3. Add sections with RevealJS-compatible HTML
  4. Update main index to include new presentation

API Surface

MarkdownSlideDeck Props:
typescript
interface Props {
  markdownContent: string; // Raw markdown content
  title?: string;          // Optional presentation title
}
File Structure Requirements:
  • Markdown files: src/content/slides/*.md
  • Astro presentations: src/pages/slides/*.astro
  • Both types use same RevealJS features and styling

Performance Considerations

  • Direct File Reading: Eliminates content collection overhead
  • Client-Side Rendering: RevealJS loads and renders slides dynamically
  • Responsive Images: Presentations support responsive image loading
  • PDF Export: Built-in RevealJS PDF generation capability

This implementation establishes a robust, scalable presentation system that maintains consistency across different content formats while providing powerful authoring flexibility for both technical and non-technical users.