# PPTX

> Use this skill any time a .pptx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx file (even if the extracted content will be used elsewhere, like in an email or summary); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates, layouts, speaker notes, or comments. Trigger whenever the user mentions "deck," "slides," "presentation," or references a .pptx filename, regardless of what they plan to do with the content afterward. If a .pptx file needs to be opened, created, or touched, use this skill.

- Skill: `onyx-dot-app/pptx` (Agent Skill, multi-file: 66 files)
- Install (CLI): `npx skillmds@latest add onyx-dot-app/pptx`
- Raw SKILL.md: https://api.skillmd.com/api/skills/onyx-dot-app/pptx/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- License: Proprietary. LICENSE.txt has complete terms
- Author: onyx-dot-app (https://skillmd.com/u/onyx-dot-app)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/onyx-dot-app/pptx

---


# PPTX Skill

> **Path convention**: All commands run from the **session workspace** (your working directory). Never `cd` into the skill directory. Prefix all skill scripts with `.opencode/skills/pptx/`. All generated files (unpacked dirs, output presentations, thumbnails, PDFs, images) go in `outputs/`.

## Quick Reference

| Task | Guide |
|------|-------|
| Read/analyze content | `python -m markitdown presentation.pptx` |
| Edit or create from template | Read [editing.md](editing.md) |
| Create from scratch | Read [components.md](components.md) — tested layout library (default); [pptxgenjs.md](pptxgenjs.md) for raw layouts it doesn't cover |
| Charts from real data | Read [charts.md](charts.md) — deck-styled matplotlib PNGs via `scripts/chart.py` |
| Icons | `node .opencode/skills/pptx/scripts/icon.js <name> --color <hex>` — see [pptxgenjs.md](pptxgenjs.md#icons) |
| Lint layout (QA step 0) | `python .opencode/skills/pptx/scripts/lint.py outputs/output.pptx` |

---

## Build Order (required)

**A complete, text-viable deck must exist in `outputs/` before any asset
enhancement.** The worst outcome is a turn that ends with no deck at all.

1. **Draft**: full deck — all slides, titles, text content, layout structure —
   written and saved to `outputs/<name>.pptx`. On the template-editing path
   this means an EARLY first clean+pack (see [editing.md](editing.md)): edits
   stranded in `outputs/unpacked/` deliver nothing.
2. **Enhance in passes** (charts, icons), re-saving the deck after each pass.
3. **Lint + QA** last (see QA below).

If time or context is running short at any point, stop enhancing and ship the
current saved deck. Never spend the start of a turn generating or inspecting
assets for a deck that doesn't exist yet.

## Reading Source Materials (context discipline)

Read attached/source documents as **text** (`python -m markitdown file.pdf`).
**Never `view_image` source pages or figure crops** — image reads exhaust the
model context in a handful of pages and the turn dies before the deck is
built. To reuse figures from a source PDF, extract the embedded images
directly — no rendering, no cropping, no visual inspection:

```bash
mkdir -p outputs/figs/
pdfimages -png -p source.pdf outputs/figs/fig   # fig-<page>-<n>.png per image
python -c "from PIL import Image; import glob; [print(p, Image.open(p).size) for p in sorted(glob.glob('outputs/figs/*.png'))]"
```

Pick figures by page number and pixel size (real figures are large; icons and
logos are small), place them directly, and verify them in the rendered-slide
QA pass at the end — never by viewing each extraction.

---

## Choosing a Template (do this first)

Starting from a well-designed template produces far better decks than building from scratch. There are **two distinct sources** of templates — check both:

```bash
# User-uploaded templates (the user's own / brand decks) — list these first
ls user_library/*.pptx attachments/*.pptx 2>/dev/null
# Built-in templates (generic starting points provided by Onyx)
ls /workspace/templates/pptx/*.pptx 2>/dev/null
```

- **If any templates are found**: present them to the user as two clearly labeled groups — **"Your uploaded templates"** (from `user_library/` and `attachments/`) and **"Built-in templates"** (from `/workspace/templates/pptx/`) — listing the user's own templates first. Briefly describe each (use `thumbnail.py` to preview if helpful), then **ask which one to use** — or whether to build from scratch — and wait for their answer before building. Never silently pick one: a user's own brand deck and a generic built-in are not interchangeable, and only the user knows which they want. This is one of the few cases where you should pause and ask rather than act autonomously.
- **If none are found**: build from scratch, and let the user know they can upload a brand/template `.pptx` for a more polished, on-brand result next time.

Once a template is chosen, follow [editing.md](editing.md). Use the from-scratch path only when the user has no template or explicitly opts for it — default to the tested layout library ([components.md](components.md)), and fall back to raw [pptxgenjs.md](pptxgenjs.md) for layouts it doesn't cover.

---

## Reading Content

```bash
# Text extraction
python -m markitdown presentation.pptx

# Visual overview
python .opencode/skills/pptx/scripts/thumbnail.py presentation.pptx

# Raw XML
python .opencode/skills/pptx/scripts/office/unpack.py presentation.pptx outputs/unpacked/
```

---

## Editing Workflow

**Read [editing.md](editing.md) for full details.**

1. Analyze template with `thumbnail.py`
2. Unpack → manipulate slides → edit content → clean → pack

---

## Creating from Scratch

**Read [pptxgenjs.md](pptxgenjs.md) for full details.**

Use only when no template is available or the user explicitly chooses to build from scratch (see [Choosing a Template](#choosing-a-template-do-this-first)).

---

## Design Ideas

> **These ideas are for building from scratch.** When developing on a chosen template, **inherit the template's existing palette, fonts, and layout language** — match it, don't redesign it. Skip the palette/font suggestions below and reuse what the template already defines.

**Don't create boring slides.** Plain bullets on a white background won't impress anyone. Consider ideas from this list for each slide.

### Before Starting

- **Pick a bold, content-informed color palette**: The palette should feel designed for THIS topic. If swapping your colors into a completely different presentation would still "work," you haven't made specific enough choices.
- **Dominance over equality**: One color should dominate (60-70% visual weight), with 1-2 supporting tones and one sharp accent. Never give all colors equal weight.
- **Dark/light contrast**: Dark backgrounds for title + conclusion slides, light for content ("sandwich" structure). Or commit to dark throughout for a premium feel.
- **Commit to a visual motif**: Pick ONE distinctive element and repeat it — rounded image frames, icons in colored circles, thick single-side borders. Carry it across every slide.

### Color Palettes

Choose colors that match your topic — don't default to generic blue. Use these palettes as inspiration:

| Theme | Primary | Secondary | Accent |
|-------|---------|-----------|--------|
| **Midnight Executive** | `1E2761` (navy) | `CADCFC` (ice blue) | `FFFFFF` (white) |
| **Forest & Moss** | `2C5F2D` (forest) | `97BC62` (moss) | `F5F5F5` (cream) |
| **Coral Energy** | `F96167` (coral) | `F9E795` (gold) | `2F3C7E` (navy) |
| **Warm Terracotta** | `B85042` (terracotta) | `E7E8D1` (sand) | `A7BEAE` (sage) |
| **Ocean Gradient** | `065A82` (deep blue) | `1C7293` (teal) | `21295C` (midnight) |
| **Charcoal Minimal** | `36454F` (charcoal) | `F2F2F2` (off-white) | `212121` (black) |
| **Teal Trust** | `028090` (teal) | `00A896` (seafoam) | `02C39A` (mint) |
| **Berry & Cream** | `6D2E46` (berry) | `A26769` (dusty rose) | `ECE2D0` (cream) |
| **Sage Calm** | `84B59F` (sage) | `69A297` (eucalyptus) | `50808E` (slate) |
| **Cherry Bold** | `990011` (cherry) | `FCF6F5` (off-white) | `2F3C7E` (navy) |

### For Each Slide

**Every slide needs a visual element** — image, chart, icon, or shape. Text-only slides are forgettable. Render deck-styled charts from real data with [charts.md](charts.md); render icons zero-setup with `scripts/icon.js` (see [pptxgenjs.md](pptxgenjs.md#icons)).

**Layout options:**
- Two-column (text left, illustration on right)
- Icon + text rows (icon in colored circle, bold header, description below)
- 2x2 or 2x3 grid (image on one side, grid of content blocks on other)
- Half-bleed image (full left or right side) with content overlay

**Data display:**
- Large stat callouts (big numbers 60-72pt with small labels below)
- Comparison columns (before/after, pros/cons, side-by-side options)
- Timeline or process flow (numbered steps, arrows)

**Visual polish:**
- Icons in small colored circles next to section headers
- Italic accent text for key stats or taglines

### Typography

**Choose an interesting font pairing** — don't default to Arial. Pick a header font with personality and pair it with a clean body font.

**When building from scratch, use only the fonts below** — any other font name silently falls back and renders wrong. Available: **Inter, Montserrat, Lato, EB Garamond** and **Fira Code** (monospace), plus **Calibri, Cambria, Arial, Times New Roman** (rendered via metric-compatible substitutes — Carlito/Caladea/Liberation).

**When editing a template, this list does not apply — keep the template's own fonts.** A template may use fonts not listed here (e.g. Kabel, Garet); these are typically embedded in the file and render correctly even though they aren't installed system-wide. Do not swap them for an installed font — that destroys the template's design. `clean.py`/`pack.py` preserve embedded fonts (`ppt/fonts/`), so they survive the edit cycle.

| Header Font | Body Font | Feel |
|-------------|-----------|------|
| Montserrat | Inter | Modern, geometric — strong default for a contemporary deck |
| Inter | Inter | Minimal; lean on weight (Bold/Light) for hierarchy |
| EB Garamond | Inter | Editorial — elegant serif title over a clean sans body |
| Lato | Lato | Professional, corporate-safe |
| Cambria | Calibri | Classic Office serif/sans pairing |
| Arial Black | Inter | Bold, high-impact headlines |

Use **Fira Code** for code samples or dense numeric/stat blocks.

| Element | Size |
|---------|------|
| Slide title | 36-44pt bold |
| Section header | 20-24pt bold |
| Body text | 14-16pt |
| Captions | 10-12pt muted |

### Spacing

- 0.5" minimum margins
- 0.3-0.5" between content blocks
- Leave breathing room—don't fill every inch

### Avoid (Common Mistakes)

- **Don't repeat the same layout** — vary columns, cards, and callouts across slides
- **Don't center body text** — left-align paragraphs and lists; center only titles
- **Don't skimp on size contrast** — titles need 36pt+ to stand out from 14-16pt body
- **Don't default to blue** — pick colors that reflect the specific topic
- **Don't mix spacing randomly** — choose 0.3" or 0.5" gaps and use consistently
- **Don't style one slide and leave the rest plain** — commit fully or keep it simple throughout
- **Don't create text-only slides** — add images, icons, charts, or visual elements; avoid plain title + bullets
- **Don't forget text box padding** — when aligning lines or shapes with text edges, set `margin: 0` on the text box or offset the shape to account for padding
- **Don't use low-contrast elements** — icons AND text need strong contrast against the background; avoid light text on light backgrounds or dark text on dark backgrounds
- **NEVER use accent lines under titles** — these are a hallmark of AI-generated slides; use whitespace or background color instead

---

## QA (Required)

**Assume there are problems. Your job is to find them.**

Your first render is almost never correct. Approach QA as a bug hunt, not a confirmation step. If you found zero issues on first inspection, you weren't looking hard enough.

### Layout Lint (run first)

Before any render or vision pass, run the deterministic layout linter — it catches the mechanical defects (off-slide shapes, sub-margin text, text overflow, overlapping text frames, low-contrast explicit colors, unknown fonts) with exact slide/shape references:

```bash
python .opencode/skills/pptx/scripts/lint.py outputs/output.pptx
```

- Exit 0 with `LINT_CLEAN` means no findings; nonzero means ERRORs exist.
- **Fix every ERROR** before moving on. Review each WARN and fix it unless the layout is genuinely intentional (e.g., a deliberate design overlap).
- Re-run after each fix batch — it's instant, so lint until clean before spending time on rendering.
- The linter checks contrast only for explicit solid colors; text over images/gradients is skipped and noted — verify those visually.
- **Content-dense decks** (analytical / consulting-style slides with intentionally tight margins) generate many advisory MARGIN warnings under the default profile. For those, lint with `--profile dense` (relaxes the margin warning from 0.5" to 0.25"); error-level checks are unchanged. Tight margins on a dense analytical slide are a deliberate style, not a defect — don't strip density to silence standard-profile MARGIN warnings.

### Content QA

```bash
python -m markitdown output.pptx
```

Check for missing content, typos, wrong order.

**When using templates, check for leftover placeholder text:**

```bash
python -m markitdown output.pptx | grep -iE "xxxx|lorem|ipsum|this.*(page|slide).*layout"
```

If grep returns results, fix them before declaring success.

### Visual QA

**⚠️ USE SUBAGENTS** — even for 2-3 slides. You've been staring at the code and will see what you expect, not what's there. Subagents have fresh eyes.

Convert slides to images (see [Converting to Images](#converting-to-images)), then use this prompt:

```
Visually inspect these slides. Assume there are issues — find them.

Look for:
- Decorative lines positioned for single-line text but title wrapped to two lines
- Source citations or footers colliding with content above
- Elements too close (< 0.3" gaps) or cards/sections nearly touching
- Uneven gaps (large empty area in one place, cramped in another)
- Columns or similar elements not aligned consistently
- Low-contrast text over images or gradients (the linter can't check these)
- Low-contrast icons (e.g., dark icons on dark backgrounds without a contrasting circle)
- Text boxes too narrow causing excessive wrapping
- Inconsistent styling across slides (fonts, colors, motifs drifting)
- Leftover placeholder content

For each slide, list issues or areas of concern, even if minor.

Read and analyze these images:
1. /path/to/slide-01.jpg (Expected: [brief description])
2. /path/to/slide-02.jpg (Expected: [brief description])

Report ALL issues found, including minor ones.
```

### Verification Loop

1. Generate slides → **Run `lint.py`** → fix every ERROR (and unjustified WARNs)
2. Re-run lint after each fix batch — it's instant — until clean
3. Convert to images → Inspect (subagent vision pass for the final aesthetic check)
4. **List issues found** (if none found, look again more critically)
5. Fix issues → **re-run lint** (one fix often creates another problem), then re-verify affected slides
6. Repeat until lint is clean and a full visual pass reveals no new issues

**Do not declare success until you've completed at least one fix-and-verify cycle.**

---

## Converting to Images

Convert presentations to individual slide images for visual inspection:

```bash
python .opencode/skills/pptx/scripts/office/soffice.py --headless --convert-to pdf outputs/output.pptx
pdftoppm -jpeg -r 150 outputs/output.pdf outputs/slide
```

This creates `slide-01.jpg`, `slide-02.jpg`, etc.

To re-render specific slides after fixes:

```bash
pdftoppm -jpeg -r 150 -f N -l N outputs/output.pdf outputs/slide-fixed
```

---

## Dependencies

- `pip install "markitdown[pptx]"` - text extraction
- `pip install Pillow` - thumbnail grids
- `npm install -g pptxgenjs` - creating from scratch
- LibreOffice (`soffice`) - PDF conversion (auto-configured for sandboxed environments via `.opencode/skills/pptx/scripts/office/soffice.py`)
- Poppler (`pdftoppm`) - PDF to images

