CommunityImagegithub.com

html-first-deck

Build a polished, on-brand PowerPoint deck the reliable way: PROTOTYPE the design in HTML/CSS first (fast to iterate on layout, hierarchy, spacing, color), then generate a NATIVE, fully EDITABLE .pptx with python-pptx (real text boxes, shapes, and tables — NOT slide images and NOT a fragile template). Use this whenever the user wants to create or substantially redesign a slide deck / presentation / pitch deck / board deck / investor deck / status deck / readout / .pptx — or says "make me slides", "build a deck", "turn this into a presentation", or "design a deck I can edit in PowerPoint". Trigger even if they don't say "HTML"; this is the recommended way to build any custom-designed deck. ALSO trigger for a deck that stays in the browser — an HTML slideshow, deck-in-HTML, or web-based presentation — in which case run Phase 1 only and skip the .pptx step. ALSO covers the diagrams INSIDE a slide (flow, pipeline, funnel, architecture, process, "X flows into Y"): draw them by...

What is html-first-deck?

html-first-deck is a Claude Code agent skill that build a polished, on-brand PowerPoint deck the reliable way: PROTOTYPE the design in HTML/CSS first (fast to iterate on layout, hierarchy, spacing, color), then generate a NATIVE, fully EDITABLE .pptx with python-pptx (real text boxes, shapes, and tables — NOT slide images and NOT a fragile template). Use this whenever the user wants to create or substantially redesign a slide deck / presentation / pitch deck / board deck / investor deck / status deck / readout / .pptx — or says "make me slides", "build a deck", "turn this into a presentation", or "design a deck I can edit in PowerPoint". Trigger even if they don't say "HTML"; this is the recommended way to build any custom-designed deck. ALSO trigger for a deck that stays in the browser — an HTML slideshow, deck-in-HTML, or web-based presentation — in which case run Phase 1 only and skip the .pptx step. ALSO covers the diagrams INSIDE a slide (flow, pipeline, funnel, architecture, process, "X flows into Y"): draw them by...

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

Installed? Explore more Image skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

Ask in your favorite AI

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

Preview

From the skill README

html-first-deck preview

Documentation

HTML-First Deck

A two-phase way to build a custom, great-looking deck that the user can still edit in PowerPoint.

Why this workflow

PowerPoint is the worst place to iterate on visual design — slow, fiddly, hard to keep consistent. HTML/CSS is the best place to iterate: you can lay out a slide, preview it, and refine spacing, color, and hierarchy in seconds. But the final artifact has to be a real, editable .pptx so the user can tweak wording and branding in their own tool (an image-per-slide export is useless to them).

So: design in HTML, deliver in native PPTX. You get fast iteration AND an editable result.

What is in this folder:

FileWhat it is
references/html_prototype.htmlPhase 1 starting point: three slides, brand colors as CSS variables, slideshow navigation
scripts/pptx_helpers.pyPhase 2 helper library: geometry, palette, header / footer / card / pill / takeaway components
scripts/render_diagram.pyRenders a code-drawn SVG to a high-resolution PNG
scripts/render_pptx.ps1Exports every slide of a .pptx to PNG for checking (Windows + PowerPoint)
examples/example_deck.pyA runnable three-slide deck built on the helpers
examples/example_diagram.pyA runnable diagram generator showing every technique in the diagram section

Voice — write titles like a consultant, not a copywriter

Read this before writing a single title, sub-header or body line. Getting the layout right and the words wrong produces a deck the user has to rewrite, which is worse than no deck. This applies to anything that goes in front of a client or a colleague — decks, HTML slideshows, dashboards, status reports, one-pagers.

The default LLM register for slides is punchy ad-agency copy. Most business audiences do not want it. Unless the user asks for something else, write in the register of a senior consultant writing for an executive audience: plain, declarative, specific, and finished in one pass.

The rule

A title states what the slide is. It does not perform.

Name the content. If a reader scanning only the titles cannot reconstruct the argument, the titles have failed. Labelled and numbered beats clever every time — Phase 1: Refund Leakage tells a reader where they are; The one we open with tells them nothing and makes them read on to find out.

Constructions to avoid

Don'tWhy it failsDo
The dashboard reports. / It doesn't explain.The two-beat "setup then punchline". Reads as a slogan, not a finding.The current dashboard reports the numbers but does not analyze them
They dropped the last tool over dashboards. / Do not sell them another dashboard.Same two-beat, plus it lectures the reader.Why another dashboard will not solve this
The one we open with:A colon-tease that withholds the answer to create suspense.Phase 1: Refund Leakage
And here's how you fix it.Chatty, and it answers a question nobody asked.Recommended fix and build estimate
Three things. Then we go and prove it.Staccato fragments used for rhythm.What we need from this meeting
What lands on the table.Idiom in place of a description.What we deliver
Every fortnight, a finding with a number on it.A tagline where a label belongs.Timeline
Only somebody who sees all four suppliers will ever find this.Rhetorical build-up.Why our team is placed to do this work

Also avoid: rhetorical questions as headings; single-word drama (The trap, The play, The close) as the main title; sentence fragments used for punch; em-dash reveals; "Here's the thing"; anything that sounds like it wants to be read aloud.

Concretely

  • Titles: noun phrases or plain statements. Analysis method. Data requirements and delivery dates. Questions for the client. Required to run Phase 1.
  • Number the work. Phases, tiers, steps. Phase 1 through Phase 8, Tier 1/2/3. Numbering makes a program legible and gives people something to refer to in a meeting.
  • Sub-headers carry the qualifier, not a joke: Phase 1 · method, Data request · Tier 1.
  • Body copy: full sentences with subject and verb. Say the thing once, properly, rather than three times with increasing emphasis.
  • Keep the specifics. Plain does not mean vague. $8.4M year to date at a 12.5% refund rate is exactly right. Numbers, names, dates and sources are what make it credible.
  • Bold sparingly — for the one phrase in a paragraph that carries the weight, not for rhythm.
  • Quotes are the exception. A verbatim client quote can stay verbatim, and a genuinely strong single sentence can sit in a pull-quote — but state it as a finding, not as a tagline.

Percentages must carry their base

Percentages are good — keep using them. But a percentage with no base can't be judged. "The top 10% of agents" means 2 people if there are 20 agents and 100 people if there are 1,000; the reader can't tell whether it's a pattern or two individuals.

  • Put the count next to the percentage, every time it's a share of people, things or events. The top 6 agents (10% of 60) · 4,100 customers (0.5% of 820,000) · Supplier A received $12.0M, half of all refund dollars ($24.0M).
  • When the group is small, lead with the number, not the percentage. Under ~30 items, the count is the finding: 6 agents issued $18,000 beats 10% of agents issued 20%.
  • Two percentages side by side need both bases. 20% of the money on 12% of the contacts is only useful once the reader also knows it's $18,000 of $90,000 and 3,600 of 30,000 contacts.
  • Name the denominator. Say what the percentage is of: of August refund dollars, of customers who claimed at least once, of agents with 10 or more days worked.
  • Say what a percentage means in money or people when you can. "3% were paid to a different account" becomes 1,500 refunds ($150K), 3% of the year's total.
  • Share-of-whole findings deserve a share-of-whole chart, such as a 100% stacked bar, so the reader sees the half without doing arithmetic.

Self-check: for every % on the page, can the reader answer "of how many?" without leaving the sentence? If not, add the count.

The self-check before you ship

Read the deck's titles in sequence, on their own. If they read like a table of contents, it is right. If they read like a pitch, rewrite them.

Phase 1 — Prototype in HTML

Build deck.html with one .slide div per slide on a 16:9 canvas (1280×720). Put the brand colors in :root CSS variables (pull them from the user's template — see "Brand" below). Lay out headers, section titles, cards, callouts, tables. Iterate here until the design is right — preview by opening it / screenshotting it, and get the user's sign-off on structure and look.

references/html_prototype.html is a ready starting point (cover, a header + three-card + takeaway slide, a picture slide, footer) with the CSS-variable palette. Read and adapt it.

This phase is the design sandbox. Don't perfect every word — nail the layout system (the header/ footer chrome, the card style, the type scale, the accent usage) because Phase 2 mirrors it.

Make the prototype navigable like a slideshow

Always add this tiny script before </body> so the HTML preview advances like PowerPoint — click (or →/↓/Space) goes to the next slide, ←/↑ goes back. It makes Phase 1 review feel like presenting and is HTML-only (nothing to carry into Phase 2).

Highlighting text must never turn the page. People select text on slides to copy it. A drag ends in a click event, so a plain click handler jumps to the next slide mid-copy. The script below ignores the click when the mouse moved more than 5px between press and release, or when text is selected. A real click still advances.

A click must advance from the slide that was clicked, never from a guess based on scroll position. The guess picks the wrong slide on some window sizes, so a click jumps two slides. Keys (→/←) can still use scroll position, since there is nothing to click.

<script>
  /* Preview like a slideshow: click or →/↓/Space = next slide, ←/↑ = previous. Selecting text does not advance. */
  (function(){
    var slides=[].slice.call(document.querySelectorAll('.slide'));
    function next(){ var y=scrollY+10; return slides.find(function(s){return s.offsetTop>y;}) || null; }
    function prev(){ var y=scrollY-10, p=null; slides.forEach(function(s){ if(s.offsetTop<y) p=s; }); return p; }
    function to(s){ if(s) s.scrollIntoView({behavior:'smooth',block:'start'}); }
    var downX=0,downY=0;
    addEventListener('mousedown',function(e){ downX=e.clientX; downY=e.clientY; });
    addEventListener('click',function(e){
      if(e.target.closest('a,button,input,textarea,select'))return;
      /* highlighting text, or dragging the mouse, is not a click on the slide */
      var sel=window.getSelection&&String(window.getSelection()); if(sel&&sel.trim())return;
      if(Math.abs(e.clientX-downX)>5||Math.abs(e.clientY-downY)>5)return;
      /* advance from the slide that was CLICKED, not from a guess based on scroll position */
      var s=e.target.closest('.slide'); to(s ? slides[slides.indexOf(s)+1] : next());
    });
    addEventListener('keydown',function(e){
      if(['ArrowRight','ArrowDown',' ','PageDown'].indexOf(e.key)>-1){e.preventDefault();to(next());}
      else if(['ArrowLeft','ArrowUp','PageUp'].indexOf(e.key)>-1){e.preventDefault();to(prev());}
    });
  })();
</script>

If the deck is staying in the browser (an HTML slideshow), stop here: Phase 1 is the deliverable.

Phase 2 — Generate the native editable PPTX

Recreate the approved design with python-pptx so every element is editable. Use the bundled helper library scripts/pptx_helpers.py — copy it into the deck folder. It provides 16:9 geometry, a brand palette block, and the components you'll reuse on every slide:

  • new_prs(), blank_slide(prs), body_bg(slide)
  • add_header(slide, title, meta), add_footer(slide, left, right)
  • add_section_title, add_subtitle
  • add_text(...) — supports a string, a list of paragraphs, OR mixed-styled runs: [[("Bold bit ", {"bold": True}), ("normal bit", {"color": MUTED})]]
  • add_rect, add_card (top/left accent stripe), add_pill, bulleted, add_takeaway
  • add_image(slide, path, x, y, w) — places a picture at its own proportions
  • save_deck(prs, path) — saves, and switches to a new filename if the file is locked/open

Structure (see examples/example_deck.py for a runnable three-slide example):

from pptx_helpers import *
def slide_intro(prs): ...      # one builder function per slide
def slide_cards(prs): ...
def main():
    prs = new_prs()
    slide_intro(prs); slide_cards(prs)
    print(save_deck(prs, DECK_DIR / "My Deck.pptx"))

Hard-won rules (these prevent the common failures)

  • Never overwrite a file the user has open. PowerPoint locks the file; a plain prs.save() then throws PermissionError. save_deck() handles this by bumping to v2, v3… Keep the working copy somewhere obvious and tell the user which file is newest.
  • Build everything as shapes/text, not a picture. That's the whole point — they must be able to edit it. Use real tables (slide.shapes.add_table) for dense comparisons.
  • Set fonts explicitly on every run (python-pptx won't inherit a theme font reliably).
  • Kill default shadows on autoshapes (shape.shadow.inherit = False) and clear their default text — the helpers already do this.
  • One builder per slide + a main() that lists them in order. Renumber footers there.

Preview / verify loop (do this — don't ship unseen)

After each build, render slides to PNG and actually look at them; fix overflow/spacing; rebuild.

  • Windows (PowerPoint installed):
    powershell -NoProfile -File scripts/render_pptx.ps1 -pptx "My Deck.pptx" -outdir preview
    
    Writes preview/s1.png, s2.png… It opens the file read-only, so it works even while the user has the deck open, and it leaves PowerPoint running if it was already running.
  • No PowerPoint — libreoffice --headless --convert-to pdf (or to PNGs) if available.

Then read the PNGs, adjust the builders, re-run. Iterate until clean.

Brand (colors + fonts)

Don't default to one palette. Pull the brand from the user's context: read a .pptx template's theme (ppt/theme/theme1.xml → <a:clrScheme>), the company's site/CSS, or ask for 2–3 colors. Set them once in the :root variables (HTML) and the BRAND block at the top of pptx_helpers.py (PPTX) so the two phases match. Set WORDMARK_PATH to a logo PNG to brand the header, or leave None. The colors that ship in both files are a neutral placeholder; replace them.

Diagrams inside slides — draw them with code, don't generate them

For the hero visual on a slide (a flow, pipeline, funnel, architecture or process diagram), don't hand-place dozens of PowerPoint shapes and don't use an AI image generator. Image models are built for photos and art. For diagrams they fail: blurry text, misspelled labels, shapes that don't line up, colors that drift off-brand, and nothing is editable.

Instead, write an SVG (a vector drawing defined in code: <path>, <circle>, <rect>, <text>, with gradients and filters) and render it to a high-resolution PNG. A computer draws every line to spec, so it's pixel-sharp, exactly on-brand, repeatable, and trivially editable (change a hex code or a word and re-render).

An analogy for the user: asking an image model for a diagram is like asking an artist to freehand a blueprint from memory. Writing code to draw it is CAD — a ruler and compass — exact every time.

Use it for: pipeline / flow / funnel diagrams, architecture diagrams, process maps, concept figures, "system X → Y → Z" visuals, before/after and comparison layouts — and to rescue a diagram that came out of an image generator looking off. The same method works for a figure going into a report or a web page, not only a slide.

Don't use it for: photorealistic imagery, illustration or art, photo editing, or charts drawn from data (use a real charting tool for those).

examples/example_diagram.py is a ready-to-adapt generator that implements every technique below in about 130 lines. Copy it and change the geometry, labels and palette.

1. Choose the layout and the FINAL proportions first

Decide the width-to-height ratio of the finished image and draw natively at that ratio. Set the SVG viewBox to it (for example 0 0 1500 460 for a wide band under a slide title).

Never stretch or squash a finished PNG to fit a space — that is what turns circles into ovals. If the picture needs to be shorter or taller, change the canvas height in the code and re-render so the shapes stay correct. If the user gives a screenshot of the space, read its width and height.

2. Write an SVG generator (Python)

Build the SVG as a string in a small Python script (loops make repeated elements, like "hundreds of lines", easy) and write it to a .svg file. Building blocks:

  • Shapes from math, not eyeballing. A regular hexagon is six points on a circle: pt = (cx + r*cos(a), cy + r*sin(a)) for a in 0, 60, … 300°. Circles and rounded rectangles are primitives. This is why the shapes come out exact.
  • Cubic bezier curves (M x0 y0 C c1x c1y, c2x c2y, x1 y1) for flowing connectors.
  • Text as <text> with font-weight, letter-spacing, text-anchor.

3. Use the deck's palette

Define the palette as named constants at the top of the script — the same hex codes as the deck's :root variables and the BRAND block — and reuse them everywhere. Passing exact hex codes is what keeps the picture on-brand, and it makes recoloring a finished diagram a 30-second edit.

A palette usually takes this shape: a background, a dark ink, a muted gray, a hairline, one or two accents, and white for cards.

4. The techniques that make it look finished

  • Layered translucent curves for density and flow. Draw 100–150 thin bezier lines, each at low opacity (about 0.10–0.22) with slight random jitter in its control points. Stacked, they read as a glowing funnel or fan. This is the hero effect. Seed the random generator so every run matches.
  • Soft glow from an SVG filter: <feGaussianBlur stdDeviation="6"> merged over the shape, plus a faint, large radial-gradient halo behind key nodes.
  • Gradients for fills (linear on flat shapes, radial on circular nodes) add depth.
  • A faint oversized watermark word (for example "100s") at very low opacity, for scale.
  • Consistent nodes: hexagon or circle hubs with a 1px outline; clean cards = white rounded rectangle, thin border, a 6px accent stripe on the left, bold name + small muted sub-label.

5. Render to a crisp PNG

python scripts/render_diagram.py diagram.svg diagram.png        # 2x resolution
python scripts/render_diagram.py diagram.svg diagram.png 3      # 3x, for large screens or print

The script opens the SVG in headless Chromium at the size of its viewBox and saves a high-resolution screenshot. (Needs pip install playwright and playwright install chromium. Libraries such as cairosvg are often missing and render filters poorly; a browser is the reliable path.)

If you only have a browser-automation tool instead of the script: put the SVG in an HTML page with its CSS width and height set to twice the viewBox, serve the folder over HTTP (python -m http.server — file:// pages are often blocked), size the window to match and take a screenshot.

6. Background: blend or transparent

The clean default is to fill the SVG background with the color of the slide it will sit on, so the edges disappear and soft shadows and glows look right. If the picture must sit on more than one background, leave the background rectangle out of the SVG and add --transparent when rendering.

7. Fonts

Put the deck's font first in the SVG's font-family, with safe fallbacks after it: font-family="Aptos, 'Segoe UI', Arial, sans-serif". The picture is rendered with the fonts installed on the machine doing the rendering.

8. Put it on the slide

  • PowerPoint: add_image(slide, "diagram.png", x, y, w) from the helpers. It sets only the width, so the height follows the picture's proportions and nothing distorts. (With python-pptx directly: slide.shapes.add_picture(path, left, top, width=...) and leave the height out.)
  • HTML prototype: <img src="diagram.png" style="width:100%;height:auto">, or paste the SVG in.

Optional: keep the labels editable in PowerPoint

If the user wants to change the wording themselves, bake only the hard-to-draw art (the funnel, hexagon, glow) into the picture and add the labels as native PowerPoint text boxes on top, positioned with a canvas-to-inches map (x_in = LEFT + canvas_x / canvas_w * width_in). The tradeoff: labels inside shapes look most polished baked in; standalone captions are fine as editable text. Ask which they prefer.

Related

If the HTML deck is already final and you want it converted rather than rebuilt by hand, the companion html-to-pptx skill (https://github.com/ImperaBusinessServices/html-to-pptx) converts an HTML deck straight into native PowerPoint shapes.

Credit

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

Related Skills