| name | Claude-design-html-to-pptx |
| description | Convert an existing HTML slide deck into an editable .pptx that fully preserves layout, typography, colors, assets, and visual style. Use when the user gives you an HTML file containing <section> slides (or CSS-styled slide markup) and asks for a PowerPoint version that can still be edited — not a deck of screenshots. The output uses native text boxes, shapes, and images laid over pre-baked gradient backgrounds. Handles the CSS↔PPTX unit conversions (px→inches, CSS letter-spacing→charSpacing), gradient-fill limitations (bakes to PNGs), photo-to-panel transitions, Chinese typography, and the Windows/macOS PPTX→PDF→image verification loop. |
HTML → PPTX (editable)
Convert an HTML slide deck to an editable .pptx while preserving as much of the original design as possible. Output must be editable native PowerPoint content — text boxes, shapes, images — not rasterized slide images.
This skill is for the case where the user already has a finished HTML deck (typically one <section class="slide"> per slide at a fixed canvas size like 1920×1080) and wants a PowerPoint they can hand-edit.
When to reach for this vs. other approaches
| Situation | Use |
|---|
| User has HTML and wants editable .pptx | This skill |
| User wants a .pptx from scratch / plain content | pptx skill (pptxgenjs / python-pptx) |
| User explicitly wants pixel-perfect screenshots | Headless-browser → image-per-slide; then embed as slide backgrounds |
| User wants to edit an existing .pptx | pptx skill editing workflow (unpack / pack XML) |
If the user says "完全还原" / "pixel-perfect" / "must look identical", clarify first: editable native shapes approximate — screenshots are exact but uneditable. They usually want editable.
Tooling
Node / npm (global):
npm install -g pptxgenjs sharp
Python:
pip install pillow pymupdf markitdown[pptx]
If pptxgenjs is installed globally but Node can't find it in the project directory, run with NODE_PATH set to the global modules path:
NODE_PATH="$(npm root -g)" node build/build.js
For HTML → PPTX → PDF on Windows use the PowerPoint COM script in templates/to_pdf.template.ps1. On macOS/Linux use LibreOffice: soffice --headless --convert-to pdf deck.pptx.
For PDF → images prefer PyMuPDF (fitz) on Windows — pdftoppm can fail silently on non-ASCII or spaced paths.
The workflow
-
Read the HTML. Enumerate distinct slide archetypes (cover, chapter, person, grid, content, close, etc.). Extract design tokens: colors from :root CSS vars, font families, the canvas size (usually 1920×1080).
-
Generate gradient backgrounds. pptxgenjs has no gradient fill. Use Pillow to bake one PNG per distinct background variant (see templates/gen_bg.template.py). Put scatter/stars, dune silhouettes, and vignettes into the same PNG — anything too decorative to be worth recreating as native shapes.
-
Generate photo-transition overlays if the deck has photos that blend into content panels (see templates/gen_fade.template.py). Always prefer a single gradient PNG over stacked translucent rectangles — stacks show visible bands.
-
Write build.js with pptxgenjs. Structure:
- Custom slide layout sized to match the HTML canvas exactly (e.g.
width: 13.333, height: 7.5 for 1920×1080).
- A
C color map and F font map from the CSS tokens.
- Unit helpers (
px, pt, ls) — see reference.md.
- Shared chrome helpers:
brandLogo, footer, cornerRow, etc. — one per recurring element.
- One function per slide archetype. Call them in order.
pres.writeFile({ fileName: ... }).
-
Export & verify. PPTX → PDF → per-slide JPG → look at each slide. If you have a lot of slides (> 6), delegate the visual inspection to a subagent — you'll miss problems on your own output.
-
Fix & re-export. Build → verify → fix → re-verify until a full pass reveals no new issues. Kill POWERPNT.EXE between Windows exports to release the file handle.
The rules that matter (read reference.md for details)
R1 · Coordinate math
For a 1920×1080 HTML canvas mapped to a 13.333×7.5″ slide:
IN_PER_PX = 13.333 / 1920 → 1 CSS px = 1/144 in for x/y/w/h
- 1 CSS px = 0.5 pt for font sizes (this is the key conversion — a 280px headline is 140pt)
R2 · CSS letter-spacing → pptxgenjs charSpacing ⚠️ HIGH-RISK
pptxgenjs charSpacing is points, absolute. It is not em, not a ratio, not 100ths of a point.
const ls = (em, fontPt) => em * fontPt;
s.addText("AGENDA", { fontSize: 44, charSpacing: ls(0.1, 44) });
Getting this wrong is the single biggest failure mode of HTML→PPTX conversions. A value of 20 at a 12pt font produces ~1.7em of tracking — every Latin label wraps character-by-character, every Chinese title spreads into a banner row. Always compute from em × fontPt.
R3 · Gradients are baked images
pptxgenjs doesn't do gradient fills. Pre-render gradient backgrounds as PNGs in Pillow. For gradient text fills (-webkit-background-clip: text), pick a solid color near the midpoint of the gradient — PowerPoint gradient text via XML injection is possible but rarely worth it.
R4 · Photo → panel transition
One gradient PNG overlay, not stacked rectangles:
s.addImage({ path: "photo_fade.png", x: edge - 280, y: 0, w: 340, h: slideH });
rect(s, { x: edge-260, ..., fill: { color:"3A1F14", transparency:70 }});
rect(s, { x: edge-130, ..., fill: { color:"3A1F14", transparency:45 }});
rect(s, { x: edge- 30, ..., fill: { color:"3A1F14", transparency:15 }});
R5 · Footer-over-photo legibility
If the original design floats a footer over a photo, photo brightness varies and footers will become invisible on bright ones. Add a bottom-scrim gradient PNG (transparent at top → opaque at bottom) over the photo area behind the footer.
R6 · Outlined / stroked text
CSS -webkit-text-stroke has no pptxgenjs equivalent. Approximate with solid fill at 70–85% transparency for a ghostly outline feel. Works well for giant chapter numerals.
R7 · Rich-text for inline emphasis
s.addText([
{ text: "首战沙海,", options: {} },
{ text: "传承安泰荣光", options: { color: C.gold_bright, bold: true } },
{ text: "。", options: {} },
], { x, y, w, h, fontFace: F.serif, fontSize: 17, ... });
Each run carries its own color, bold, italic, breakLine.
R8 · Common pptxgenjs pitfalls
- Colors:
"FF0000", never "#FF0000" — leading # corrupts the file
- Don't encode alpha in hex (
"00000020" corrupts); use transparency (0–100) or opacity (0–1)
- Don't reuse option objects between calls (pptxgenjs mutates) — build fresh with a factory function
breakLine: true between array items; don't rely on "\n" alone
- Each
pptxgen() instance writes one file — don't reuse
Starter templates
The templates/ folder has copy-and-adapt starters. Read a template, don't blindly copy — paths and the color/font maps always need project-specific edits.
| File | Purpose |
|---|
templates/build.template.js | pptxgenjs skeleton with layout, helpers (px, pt, ls), shared chrome stubs |
templates/gen_bg.template.py | Pillow gradient-background generator (desert/ink/cream variants) |
templates/gen_fade.template.py | Horizontal + vertical fade PNG overlays for photo blends |
templates/to_pdf.template.ps1 | Windows: PowerPoint COM PPTX → PDF |
templates/pdf2img.template.py | PyMuPDF PDF → per-slide JPG for visual QA |
Quick sanity checklist before declaring success