HTML-to-PPTX Presentation Generator
Generate professional PowerPoint files by writing HTML/CSS slides and converting them to PPTX with pixel-accurate positioning.
Why HTML? LLMs excel at HTML/CSS. Instead of wrestling with PptxGenJS's coordinate system directly, write HTML slides and let the browser compute exact element positions via getBoundingClientRect().
When to Use
- User asks to create a presentation, slide deck, or .pptx file
- User needs slides for a report, proposal, roadmap, or pitch
- User wants charts, tables, or images in PowerPoint format
Do NOT use for: editing existing .pptx, converting formats, or reading/extracting from .pptx.
Prerequisites
npm install -g agent-browser
agent-browser install
npm install pptxgenjs sharp
Important: Always run agent-browser via Bash CLI, not MCP tools. Even if agent-browser MCP Connector is available, this skill requires --stdin piping and shell variable expansion that MCP tools cannot handle.
Pipeline Overview
1. Generate HTML slides (720pt x 405pt, strict element rules)
2. Validate and render via agent-browser (screenshot for visual review)
3. Extract DOM positions via agent-browser eval (getBoundingClientRect)
4. Build config.json, render placeholders, preview with `open` command
5. Assemble PPTX with PptxGenJS using extracted coordinates
Workflow
Step 1: Generate HTML Slides
Write HTML following the rules in references/html-rules.md. Use templates from references/slide-templates.md.
Key constraints:
- Body:
width: 720pt; height: 405pt; margin: 0; padding: 0 - Text MUST be in
<p>,<h1>-<h6>,<ul>/<ol>(text in<div>won't convert) - No
<br>tags, no manual bullet symbols - Web-safe fonts only (Arial recommended)
- Bottom margin: keep 36pt+ from bottom edge
- Placeholders:
<div class="placeholder" id="chart1" style="width:300pt;height:200pt;">
Save each slide as a separate HTML file under a presentation-specific directory:
# Use a URL-safe slug of the presentation title (e.g., "q4-results", "product-roadmap")
mkdir -p ./tmp/slides/{title}
# Write slide-0.html, slide-1.html, etc. into that directory
Step 2: Validate with agent-browser
CRITICAL: Set viewport to match slide dimensions before any operation.
# Set viewport to exactly 960x540 (720pt x 405pt at 96dpi)
agent-browser set viewport 960 540
agent-browser open "file://$(pwd)/tmp/slides/{title}/slide-0.html"
cat $SKILL_DIR/scripts/validate.js | agent-browser eval --stdin --json
Returns { "valid": true/false, "errors": [...] }. Fix any errors before proceeding.
Take a screenshot for visual review:
agent-browser screenshot ./tmp/slides/{title}/slide-0.png --json
Step 3: Extract DOM Positions
Use the bundled extraction script to get all element positions, styles, and placeholder coordinates:
cat $SKILL_DIR/scripts/extract-dom.js | agent-browser eval --stdin --json
The data.result field contains JSON: { background, elements, placeholders, errors }.
Save each slide's extracted data to a JSON file for the build step.
For gradient backgrounds, rasterize only the background (hide content to avoid double-rendering text):
# Hide all content, screenshot background only, then restore
agent-browser eval "document.querySelectorAll('body > *').forEach(e => e.style.visibility='hidden')"
agent-browser screenshot ./tmp/slides/{title}/slide-0-bg.png --json
agent-browser eval "document.querySelectorAll('body > *').forEach(e => e.style.visibility='')"
Step 4: Build config.json and Preview
Create a config.json that references each slide's extracted data and placeholder definitions. See references/pptxgenjs.md for config.json format.
Then render placeholders for visual preview before final PPTX assembly:
# For each slide with placeholders: render preview
agent-browser open "file://$(pwd)/tmp/slides/{title}/slide-0.html"
agent-browser set viewport 960 540
# Set placeholder data and inject Chart.js
agent-browser eval "window.__PLACEHOLDERS__ = $(jq '.slides[0].placeholders' tmp/slides/{title}/config.json)"
agent-browser eval "var s=document.createElement('script');s.src='https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js';document.head.appendChild(s)"
agent-browser wait 2000
# Render placeholders and screenshot for review
cat $SKILL_DIR/scripts/render-placeholders.js | agent-browser eval --stdin --json
agent-browser wait 1000
agent-browser screenshot ./tmp/slides/{title}/slide-0-preview.png --json
# IMPORTANT: Always open the preview image so the user can review it
open ./tmp/slides/{title}/slide-0-preview.png
You MUST run open after each screenshot to display the preview to the user. Do not skip this step — the user needs to visually confirm each slide before proceeding to PPTX assembly.
Step 5: Assemble PPTX
Once all slides are reviewed, run the bundled build script:
node $SKILL_DIR/scripts/build-pptx.js tmp/slides/{title}/config.json ./output/{title}.pptx
Placeholder System
Placeholders reserve space in HTML for native PowerPoint content (charts, tables, images). The <div> defines position/size; PptxGenJS fills the content.
<div class="placeholder" id="chart1" style="width: 300pt; height: 200pt;"></div>
Three types supported: chart (bar, line, pie, doughnut, scatter), table, and image. See references/pptxgenjs.md for complete placeholder JSON formats.
Design Consistency
The title slide (slide 0) establishes the visual language. Apply consistently:
| Element | Rule |
|---|---|
| Header bar | Same height and background color on all slides |
| Content padding | Same padding values across slides |
| Font sizes | h1: 28pt, h2: 20pt, body: 16pt, caption: 12pt |
| Colors | Primary for headers, secondary for subheadings, accent for highlights |
Scripts
All scripts are in this skill's scripts/ directory. Find the skill path and use it:
SKILL_DIR="$(dirname "$(find ~/.claude -path '*/document-skills/pptx/SKILL.md' 2>/dev/null | head -1)")"
| Script | Run with | Purpose |
|---|---|---|
validate.js |
cat $SKILL_DIR/scripts/validate.js | agent-browser eval --stdin --json |
Quick HTML validation |
extract-dom.js |
cat $SKILL_DIR/scripts/extract-dom.js | agent-browser eval --stdin --json |
Full DOM extraction |
render-placeholders.js |
cat $SKILL_DIR/scripts/render-placeholders.js | agent-browser eval --stdin --json |
Placeholder preview rendering |
build-pptx.js |
node $SKILL_DIR/scripts/build-pptx.js config.json output.pptx |
PPTX assembly |
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Background doesn't fill slide | Viewport wider than 960px, screenshot includes whitespace | agent-browser set viewport 960 540 before any operation |
| Text appears doubled/overlapping | Screenshot used as bg includes rendered text | Hide content before bg screenshot: visibility='hidden' |
| agent-browser command not found | Not installed | npm install -g agent-browser && agent-browser install |
| Text missing in PPTX | Text directly in <div> |
Wrap in <p> or <h1>-<h6> |
| Gradient background blank | Not rasterized | Hide content, screenshot, set bgImagePath in config |
| Overflow validation fails | Content exceeds 720pt x 405pt | Reduce font sizes, padding, or content |
| Bottom margin error | Text within 0.5in of bottom | Add padding-bottom: 48pt to content area |
| PPTX file corrupted | Inset box-shadow used | Use outer shadows only |
Reference Files
- HTML Rules - Complete HTML generation constraints
- Slide Templates - Templates for each slide type
- PptxGenJS Conversion - Config format, placeholder JSON, and conversion details