Markdown to HTML presentations — structured, validated, and speaker-ready.
MD-Slides converts structured Markdown files into self-contained HTML presentations with keyboard navigation, speaker view, syntax-highlighted code blocks, and a flexible theme system. Write slides in plain text; present anywhere a browser runs.
Requirements: Java 11 or higher.
# Download the JAR and the feature tour (a self-contained 21-slide demo)
curl -L https://github.com/TJMSolns/MD-Slides/releases/latest/download/md-slides.jar -o md-slides.jar
curl -L https://github.com/TJMSolns/MD-Slides/releases/latest/download/feature-tour.md -o feature-tour.md
# Render and open
java -jar md-slides.jar render feature-tour --theme dark
open feature-tour/index.html # macOS
xdg-open feature-tour/index.html # Linux
start feature-tour/index.html # WindowsPress S to open speaker view — the feature tour has notes on every slide.
The feature tour exercises every template, content type, image, speaker notes, code block, two-column layout, theme, and validation rule. It's the fastest way to see what MD-Slides can do.
Once you've seen the tour, create your own deck — my-talk.md anywhere next to the JAR:
java -jar md-slides.jar render my-talk --theme lightmy-talk/ is a self-contained output directory — index.html plus all copied assets. See Writing slides below for the full syntax.
Slides are separated by ---. Each slide opens with a frontmatter block declaring its template.
---
template: title
---
# My Talk Title
## A concise subtitle
**Author Name**
---
template: content
---
## First Section
What you want to say, kept to the point.
Key ideas:
- One idea per bullet
- Short phrases, not sentences
- Three to five bullets is plenty
---
template: content
---
## Code Example
```scala
case class Slide(id: SlideId, template: Template, slots: Map[SlotName, SlotContent])MD-Slides validates slide density so your audience can actually read what you write.
Render it:
```bash
java -jar md-slides.jar render my-talk --theme dark
| Key | Action |
|---|---|
→ or Space |
Next slide |
← |
Previous slide |
Home |
First slide |
End |
Last slide |
S |
Open speaker view |
---
template: title
---
# Main Title
## Optional subtitle
**Optional author**Constraints: title max 2 lines; subtitle max 2 lines; author max 80 characters.
---
template: content
---
## Slide Heading
Body content: markdown, lists, code blocks, images.Constraints: heading max 80 characters; body max 12 lines, max 150 words.
MD-Slides reports all constraint violations together — you see every problem in one pass, not one at a time.
Press S during a presentation to open a synchronized speaker window showing your notes, the next slide heading, and an elapsed timer.
Add notes to any slide with an HTML comment:
<!-- Speaker notes: The key point here is X. Don't forget to mention Y. -->Output: my-talk/index.html (audience), my-talk/speaker.html (presenter).
java -jar md-slides.jar render my-talk --theme light # default
java -jar md-slides.jar render my-talk --theme darkCreate a JSON file anywhere and pass its path as the theme:
java -jar md-slides.jar render my-talk --theme ./themes/mytheme/theme.jsonMinimal theme schema:
{
"name": "mytheme",
"version": "1.0.0",
"background": { "color": "#ffffff" },
"colors": {
"text": "#333333",
"heading": "#000000",
"accent": "#0066cc",
"link": "#0066cc",
"linkHover": "#0044aa",
"codeBackground": "#f5f5f5",
"codeText": "#333333"
},
"fonts": {
"body": "Arial, sans-serif",
"heading": "Arial, sans-serif",
"code": "monospace"
},
"spacing": {
"slideMargin": "2rem",
"headingMargin": "1rem 0",
"paragraphMargin": "0.5rem 0",
"lineHeight": "1.6"
},
"syntax": {
"keyword": "#0000ff",
"string": "#00aa00",
"comment": "#888888",
"function": "#aa00aa",
"number": "#aa5500",
"operator": "#333333"
},
"slideCounter": {
"color": "#666666",
"background": "rgba(255,255,255,0.9)",
"fontSize": "0.9rem"
}
}Per-template background images are also supported — see docs/decisions/pdr/PDR-013-directory-based-theme-architecture.md.
MD-Slides applies configuration in priority order (highest wins):
- CLI flags (
--theme dark) - Project config (
.mdslides/config.jsoncommitted with your repo) - Global config (
~/.mdslides/config.jsonfor personal preferences) - Built-in defaults
Example project config:
{
"theme": "dark",
"outputDir": "dist"
}MD-Slides validates every slide before rendering. Structure errors, density violations, and accessibility problems are all collected and shown together:
✗ Validation failed:
- Slide 3: body exceeds max 12 lines (has 17)
- Slide 5: heading exceeds max 80 characters (has 94)
- Slide 7: image is missing alt text
Fix them all at once rather than in a loop of one-error-at-a-time.
MD-Slides is built with Mill. See CONTRIBUTING.md for full setup instructions.
git clone https://github.com/TJMSolns/MD-Slides.git
cd MD-Slides
# Install Mill (macOS/Linux)
curl -L https://github.com/com-lihaoyi/mill/releases/download/0.11.6/0.11.6 > mill
chmod +x mill && sudo mv mill /usr/local/bin/mill
# Compile and test
mill __.compile
mill __.test
# Build a standalone JAR
mill cli.assembly
# → out/cli/assembly.dest/out.jarMD-Slides is a three-module Mill project. Dependencies flow in one direction only: cli → infrastructure → domain.
┌─────────────────────────────────────────────────────┐
│ cli │
│ Cats Effect IOApp · Decline argument parsing │
│ Wires modules. Contains no business logic. │
└───────────────────────────┬─────────────────────────┘
│
┌───────────────────────────▼─────────────────────────┐
│ infrastructure │
│ Markdown parser (Flexmark anticorruption layer) │
│ HTML renderer (Scalatags) │
│ Theme loader (Circe) · Asset copier (os-lib) │
└───────────────────────────┬─────────────────────────┘
│
┌───────────────────────────▼─────────────────────────┐
│ domain │
│ Pure functions — no I/O, no side effects │
│ Slide · SlideDeck · Template · Theme │
│ Validation pipeline · Error accumulation │
└─────────────────────────────────────────────────────┘
Domain layer is pure Scala 3 — opaque types, smart constructors, Either[NonEmptyList[ValidationError], A] for error accumulation, no cats-effect. It has no knowledge of files, HTML, or markdown.
Infrastructure layer is where all I/O lives. Third-party libraries (Flexmark, Circe) are wrapped behind domain-facing interfaces so they never leak into domain types.
CLI layer is thin: parse arguments, call infrastructure, handle errors, exit with the right code.
See CONTRIBUTING.md and docs/decisions/ for a deeper walkthrough.
| Dependency | Purpose |
|---|---|
| Scala 3.3.1 LTS | Language |
| Mill 0.11.6 | Build tool |
| Cats Core 2.10.0 | Functional primitives (Either, NonEmptyList) |
| Cats Effect 3.5.4 | Effect system for I/O |
| Decline 2.4.1 | CLI argument parsing |
| Flexmark 0.64.8 | Markdown parsing |
| Scalatags 0.12.0 | Type-safe HTML generation |
| Circe 0.14.6 | JSON (theme files) |
| os-lib 0.9.3 | File I/O |
| MUnit 0.7.29 | Test framework |
| ScalaCheck 1.17.0 | Property-based testing |
Contributions are welcome. See CONTRIBUTING.md for the full guide, including how to add templates, themes, and features while keeping the domain layer clean.
Please read the Code of Conduct before participating.
MIT — Copyright © 2025 TJM Solutions LLC