slide-creator
Slide Creator — HTML → PDF Presentations
Build slide decks as HTML, export to pixel-perfect 16:9 PDF via headless Chromium.
Output format: PDF — not Microsoft PowerPoint (.pptx). The PDF preserves exact layout, fonts, and colors across all devices.
Why HTML → PDF
- CSS layout (Grid/Flexbox) is far more flexible than any PPT editor
- Full web typography, gradients, SVG, animations (print degrades gracefully)
- Git-friendly, reproducible, scriptable
- One command → PDF with exact 16:9 page dimensions
Workflow
0. Identify Scenario (new)
Before any other step, identify the presentation scenario. Read references/content-scaffolding.md for full templates.
| Scenario keyword | Template to use |
|---|---|
| pitch / investor / fundraising | pitch-deck |
| conference / keynote / summit / talk | conference-keynote |
| product launch / launch event | product-launch |
| report / research / analysis | research-report |
| (none of the above) | ask user which scenario fits best |
Each template defines: slide count, page titles, required content per page.
0.5 Bilingual Layout (if needed)
If the audience is bilingual (e.g. HK, Singapore, global Chinese conference), or the user mentions Chinese + English:
- Use the
bilinguallayout mode fromreferences/content-scaffolding.md - Heading: English (large) + Chinese subtitle (smaller, --text-muted)
- Body bullets: Chinese first, English parenthetical optional
- Avoid pure English-only decks for HK/TW/SG audiences
1. Plan the Deck
Define slide count and content per slide based on the scenario template. Each slide = one <section class="slide">.
1.5 Art Direction (run this before building)
Run this step whenever the user hasn't provided a specific visual style. Read
skills/slide-creator/references/art-direction.mdfor the full style taxonomy, CSS token templates, and style-brief output format.
Step A — Ask 4 questions:
- Audience & setting: who is this for, and in what context (investors / internal team / public keynote)?
- Mood keywords: how should the audience feel (authoritative / energetic / friendly / geeky-modern)?
- Brand constraints: any required brand colors, logo, or fonts?
- Reference material: any template references to align with (images, web links, existing deck screenshots)?
Step B — Generate a visual style picker page: Do NOT present style options as text descriptions — users can't evaluate styles from words alone.
Reference handling rules:
- If user provides image files/screenshots: sample palette (primary/surface/accent), inspect layout density, corner radius, typography tone from the visual.
- If user provides web URLs: use
web_fetchto extract design cues. Critical — follow this extraction protocol to avoid misreading the style:- Ignore the brand name / domain name — never infer visual style from the product's industry or name (e.g. "Neo" does NOT mean neon, "Opera" does NOT mean European luxury).
- Read copy tone & vocabulary — the words used on the page reveal mood (e.g. "surgical precision", "quiet confidence" → restrained; "unleash", "radically" → bold/aggressive).
- Extract explicit color vocabulary — look for CSS keywords in the fetched text, or color names mentioned in body text / alt tags. Warm vs cool, light vs dark, muted vs saturated.
- Infer layout density — count words per section; sparse = editorial/luxury, dense = technical/functional.
- Identify decorative motifs — mentioned or implied (e.g. geometry, gradients, photography, illustration, line art, brutalism).
- Cross-check against art-direction.md — find the closest matching template, then describe the delta (e.g. "Style G but warmer, replace blue with burnt orange, add subtle grid lines").
- When uncertain: be conservative — under-promise the style match and present 3 options where Option A is your best interpretation, B is safer/cleaner, C is more experimental. Never confidently assert a style that contradicts the actual visual evidence.
- If user provides both: prioritize image cues first, URL cues second.
- If no references are provided: use built-in style taxonomy defaults.
Then:
- Create
output/style-picker/index.html— a single page with 3 side-by-side mini slide previews (16:9 aspect ratio), each fully rendered with real CSS (colors, fonts, layout, decorative elements). Each preview must look like an actual slide, not a color chip. - Build these 3 options as: (A) Reference-faithful, (B) Safer corporate variant, (C) Bolder creative variant.
preview_servethe directory and show the preview URL.- Each card has a label below: style name + one-line description.
- Add
onclickhighlight so the user can click to indicate their choice.
User picks by saying "Choose A" / "I want B" / "Blend A+C" etc.
Step C — Generate style-brief.md:
Once user selects a style, write a style-brief.md (template in art-direction.md) in the project directory.
All subsequent HTML/CSS work must follow this brief.
Step D — Ask for brand assets (logo / colors): After user picks a style, ask:
"Do you have a logo or brand color to include? You can upload an image file, and I’ll embed the logo across the slides."
If logo uploaded: embed as base64 in HTML (use base64.b64encode in bash), place in top-left or top-right corner at ≤60px height.
If brand color given: override --accent in CSS token block with user's color.
2. Choose a Theme
If Art Direction was completed, the style-brief.md is the theme spec — skip this table.
Otherwise, use as a quick fallback:
| Style | Background | Accent | Font | Mood |
|---|---|---|---|---|
| Dark tech | #000 / #0a0a0a |
bright orange/blue/green | Inter, Space Grotesk | Bold, modern |
| Light clean | #fff / #f8f8f8 |
navy, teal, coral | Inter, DM Sans | Professional, minimal |
| Gradient | dark gradient | vibrant accent | Any sans-serif | Creative, energetic |
| Corporate | #1a1a2e / white |
brand color | system fonts | Trustworthy, formal |
| Playful | soft pastels | warm pop colors | Nunito, Poppins | Friendly, casual |
3. Build HTML + CSS
Create a project directory with index.html + styles.css.
Start from assets/base.css — structural skeleton (slide dimensions, print rules, layout helpers) with NO colors or fonts. Layer your theme on top:
/* Example theme layer — customize freely */
body {
font-family: 'Inter', sans-serif;
color: #fff;
background: #000;
}
.slide { background: #0a0a0a; }
.slide-tag { background: rgba(0,120,255,0.15); color: #0078ff; }
.card { background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); }
Mandatory structural rules (in base.css — don't remove):
.slide { width: 1280px; height: 720px; page-break-after: always; overflow: hidden; }
@page { size: 1280px 720px; margin: 0; }
Key rules:
- Use
pxunits — nevervh/vw/rem/%for slide dimensions - Google Fonts: use
<link>in<head>, export script waits for network idle - Viewport meta:
<meta name="viewport" content="width=1280"> - Content must fit within 720px height — overflow is clipped
4. Preview (Optional)
Use preview_serve to preview in browser before exporting.
5. Export to PDF
python3 skills/slide-creator/scripts/export_pdf.py --dir <project-dir> --output output/<name>.pdf
Options:
--dir— directory containingindex.html(required)--output/-o— output PDF path (default:<dir>/deck.pdf)--width— slide width in px (default: 1280)--height— slide height in px (default: 720)
6. Verify
The script prints slide count and confirms output path. Extra check:
import fitz
doc = fitz.open("output/deck.pdf")
print(f"Pages: {doc.page_count}")
for p in doc:
r = p.rect
print(f" {r.width*96/72:.0f}x{r.height*96/72:.0f}px")
Style Guidelines
- No default brand — every deck gets a theme tailored to its content
- Prefer Art Direction-first workflow (questions → references → user pick →
style-brief.md) - Ask the user for preference: dark/light, accent color, font, mood
- Each slide should have clear visual hierarchy: tag → title → content
- Keep text concise — slides are visual, not documents
- Use
.bg-glowwith theme-colored radial gradients for depth - Do not use
web_searchfor style exploration by default; prioritize user-provided reference images/links plus templates inart-direction.md. - When the user provides reference links, use
web_fetchto extract design cues (color tone / voice / layout), but implement final CSS using local templates and token variables. - When the user asks for "art-direction advice / style advice", always generate a visual style-picker preview page (3 options) — never rely on text-only descriptions.
- Build HTML strictly against chosen style brief, then export PDF (do not skip brief unless user explicitly opts out)
- After style is chosen, always ask about logo / brand assets before building
- For HK/TW/SG/bilingual audiences, default to bilingual layout unless user says English only
Style Microtweaks (after style is chosen)
If user says "change primary color to red" / "switch the font" / "increase corner radius", do NOT restart art direction.
Instead, directly patch the --accent / --font-head / --radius CSS variable in styles.css.
Only restart art direction if user wants a completely different style.
Gotchas
- Chromium deps: first run needs
python3 -m playwright install chromium && python3 -m playwright install-deps chromium - Fonts: Google Fonts need HTTP — the export script starts a local server automatically
- Emoji rendering: headless Chromium may lack emoji fonts — use SVG icons instead
- Large images: embed as base64 or use relative paths (local server serves the project dir)
- Slide overflow: content exceeding 720px height is clipped — design within bounds