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
}
---
Navigation System Architecture
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
- File Reading: Direct filesystem access via Node.js
readFile
- Frontmatter Parsing: Custom regex-based YAML frontmatter extraction
- Slide Separation: Split content on
---
(horizontal) and--
(vertical) delimiters - 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:
- Create
src/content/slides/my-presentation.md
- Add frontmatter with title and description
- Use
---
for slide breaks,--
for vertical slides - Access at
/slides/markdown/my-presentation
Creating a New Astro Presentation:
- Create
src/pages/slides/my-presentation.astro
- Import
OneSlideDeck
component - Add sections with RevealJS-compatible HTML
- 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.