CommunityCoding & Developmentgithub.com

html-to-pptx

Convert an HTML slide deck (one `.slide` div per slide, 1280x720) into a NATIVE, fully editable PowerPoint — real rectangles, rounded boxes, pills, circles, lines, arrows, freeform shapes, bullets, curved text and text boxes, NOT slide images. Use whenever the user says "make a PowerPoint of this HTML", "convert the deck to ppt", "I need it editable in PowerPoint", "don't just put images in PowerPoint", or hands over an HTML deck / HTML slideshow that must become a .pptx. Works on any brand.

What is html-to-pptx?

html-to-pptx is a Claude Code agent skill that convert an HTML slide deck (one `.slide` div per slide, 1280x720) into a NATIVE, fully editable PowerPoint — real rectangles, rounded boxes, pills, circles, lines, arrows, freeform shapes, bullets, curved text and text boxes, NOT slide images. Use whenever the user says "make a PowerPoint of this HTML", "convert the deck to ppt", "I need it editable in PowerPoint", "don't just put images in PowerPoint", or hands over an HTML deck / HTML slideshow that must become a .pptx. Works on any brand.

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ImperaBusinessServices/html-to-pptx/tree/HEAD

Installed? Explore more Coding & Development skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

HTML deck -> native, editable PPTX

The rule this skill enforces: never paste slide images into PowerPoint. Every square, bar, dot, pill, line and text block must be a real PowerPoint object the user can move, recolor and retype. The converter reads the rendered page (Playwright + the browser's own computed layout) instead of re-guessing the CSS, then writes python-pptx shapes at the exact same coordinates.

Run it

python scripts/html_to_pptx.py <deck.html> <out.pptx> [all | 1,3,5]

Needs: pip install -r requirements.txt and playwright install chromium. Slides are the elements matching .slide (1280x720 px). Put the .html somewhere with its images inline (data: URIs) or on disk beside it. examples/sample-deck.html is a small deck to try first.

What the page needs to look like when captured (the script does these for you): anim class removed from <html>, CSS animations/transitions off, window beforeprint fired (decks often reveal charts on print), print media, zoom 1, elements with class .mode or .jump hidden (theme toggle, slide navigator). Decks with a light/dark toggle convert in whatever theme is showing — make sure that is the one the user wants.

What becomes what

In the HTMLIn the PPTX
div / card with background, border, radiusrectangle / rounded rectangle (radius -> corner adjust)
gradient background (linear)native gradient fill
one-sided border (accent stripe)thin rectangle; hairline/dashed borders -> real lines
border-radius:999px pills, circlespill / oval
text (any block, with bold/colour runs, <br>, links)one text box per block, runs preserved, hyperlinks kept
<li>real PowerPoint bullets (marker colour kept)
inline SVG rect/circle/line/polyline/path/textrectangle, oval, connector (arrowheads kept), freeform with real Bezier curves, text box
SVG <textPath> (text on a curve)PowerPoint curved text (Arch Up/Down warp)
CSS clip-path: polygon(...) (chevrons, diagonal cuts)freeform shape; a photo inside is cropped to the polygon
<img>, photos, icon PNGspictures (they are bitmaps — that is expected)
text-transform, letter-spacing, strikethrough, opacity/alphaapplied to the runs / fills
Two-view toggle (.vtog with data-view=code and data-view=time buttons)second slide for the other view
JS animationsfinal resting state only

Hard-won rules (each cost real time — keep them)

  1. Text position is calibrated, do not "fix" it by eye. PowerPoint 100% line spacing = 1.2 x font size (Arial and Arial Black alike). A text box must be placed at top = rangeTop + (ascent-cap)*fs - lineHeight + K*fs with (ascent-cap, K) = (0.189, 0.95) for Arial and (0.385, 1.0) for Arial Black, and line_spacing = cssLineHeight / (1.2*fs). Single-line text uses spacing 100% and wrap=none with slack so it never re-wraps.
  2. Chrome on Windows renders font-family: Arial at weight 800/900 as ARIAL BLACK. The converter maps it to Arial Black (not bold). Without this every heavy headline looks too thin and the wrong width.
  3. Never swallow inline-block children into a text block. Only display:inline children are merged into one text box; inline-block/flex children are handled separately.
  4. Measure text nodes in place (Range on the node). Wrapping a node in a temp <span> changes grid/flex layout.
  5. Open the result in PowerPoint to prove it is valid. python-pptx will happily write a file PowerPoint refuses ("PowerPoint could not open the file"). Known causes: negative/NaN shape sizes (clamp, drop NaN points), and calc() inside clip-path (evaluate it). Bisect by deleting shapes in halves and re-opening.
  6. A picture with custom geometry fills its path's bounding box, not its frame. To crop a photo to a polygon, size the picture to the polygon's bounding box and set a source crop (srcRect) for the rest.
  7. Do not screenshot-capture shapes ("rasterize the odd ones"). Convert logos' SVG paths and clip-path polygons to freeforms instead. The raster branch is a last resort.
  8. Skip clipped ghost text (big faded numerals cut off by their card) — PowerPoint cannot clip text boxes.
  9. PowerPoint a:ln goes after the fill in spPr; delete the theme p:style or shapes pick up shadows.
  10. Never overwrite a .pptx the user has open — write a new file name instead.

Verify (always — never ship unseen)

Windows with PowerPoint installed:

powershell -NoProfile -File scripts/render_pptx.ps1 -pptx <out.pptx> -outdir <png_dir>     # PowerPoint export to PNG
python scripts/compare.py <deck.html> <png_dir> 1,2,3 <out.png>                           # HTML | PPTX side by side
python scripts/compare.py <deck.html> <png_dir> 6:6,7:8 <out.png>                          # html:pptx when slide numbers differ

render_pptx.ps1 leaves PowerPoint running if the user already had it open. Without PowerPoint, render the .pptx to PNGs another way (for example LibreOffice headless) into s1.png, s2.png… and run compare.py.

Read the comparison PNG, fix, rebuild. Typical clean-up after a run: text slightly wider in PowerPoint (kerning), nothing structural.

Known limits (tell the user)

  • Text placement is calibrated for Arial and Arial Black. Other fonts convert, but text may sit a few pixels high or low until that font is calibrated (measure cap height and line spacing in PowerPoint).
  • Photos, icons and logos supplied as bitmaps stay pictures. Text sits in its own box on top of its card (not grouped).
  • Animations/toggles are flattened to one frame (second view of a toggle becomes an extra slide).
  • Radial gradients and per-corner radii are approximated; box-shadows are dropped.
  • Slide size is fixed to 1280x720 px -> 13.333 x 7.5 in.

Credit

Built by Impera Business Services. MIT License — see LICENSE.

Related Skills