Build a slide deck that tells a story
Activated Cloud✓ Officialactivated/build-slide-deck
Free · MIT
About
Plans and builds a PowerPoint deck (.pptx) that tells one story: storyline first as a list of action titles, then one message per slide with the chart, table or few words that prove it, speaker notes, sources and slide numbers, built with python-pptx (on the owner's template when there is one) and checked by rendering every slide. Use for board updates, pitches, proposals, reviews and training decks. Not for long-form documents (see write-structured-report) or standalone chart images (see make-clear-charts).
Documentation
Build a slide deck that tells a story
A good deck is an argument: read only the slide titles and you get the whole case. This skill writes that storyline first, then builds each slide with python-pptx so every title is a takeaway and everything on the slide supports it. The standard: one message per slide, nothing smaller than 14 pt on a presented deck, every number sourced, editable native charts, and a render check of every slide before the owner sees it.
When to use
- "Make a deck for Thursday's board meeting."
- "Turn this report into slides."
- "I need a pitch deck / proposal deck / quarterly review."
- "Put these numbers into our company template."
What you need
- Audience and purpose: who is in the room, what decision or action you want from them.
- Format: presented live (sparse slides, the talk carries detail) or sent to be read (denser, full sentences). Ask if unclear.
- Length: roughly one slide per one to two minutes of talking; a 20-minute slot is 10 to 15 slides plus appendix.
- The owner's template (.pptx or .potx) and brand colours, if any. Their template beats your styling.
- Content and data: the report, numbers, charts (see
make-clear-charts), with sources.
Set up once
python3 -c "import pptx" 2>/dev/null || python3 -m pip install --user --break-system-packages python-pptx
(Debian needs --break-system-packages together with --user; it installs only into your home folder. The import name is pptx.)
Method
Write the storyline before any slide. Pick a structure from
references/storylines.md(situation, complication, resolution; problem and solution; status review; pitch; proposal). Write the governing message in one sentence: what you want the audience to believe or do.Draft the ghost deck: titles only. One full-sentence action title per slide, at most two lines (about 15 words): "West missed target by 44% and needs a fix in Q4", not "West region". Read the titles top to bottom. If they do not tell the story on their own, rework them before building anything. Show the list to the owner for a quick yes when the deck matters.
Shape the deck.
- Title slide.
- The answer up front: an executive summary slide with the recommendation and the ask.
- Two to five sections, each a claim with the slides that prove it.
- Next steps or the decision needed, with owners and dates.
- Appendix for detail people may ask about.
Design each slide around its proof. One chart, one table, one diagram or three to five short bullets (about 12 words each), never all of them. Bigger is better: one chart filling the slide beats four small ones. Rules:
- 16:9 (13.333 x 7.5 in), margins at least 0.5 in.
- Title 28 to 32 pt, body 18 to 24 pt for presented decks; never under 14 pt (12 pt for reading decks).
- One font family, one accent colour, greys for everything else; the accent marks what the title talks about.
- Source line on every slide with data (10 pt, bottom left), slide number bottom right.
- Speaker notes hold the talk track: what to say, the numbers behind the claim, likely questions.
Build with
execute_code(absolute paths; tested starting point, more inreferences/pptx-recipes.md):
from pathlib import Path
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.chart.data import CategoryChartData
from pptx.enum.chart import XL_CHART_TYPE
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
OUT = Path("/home/user/Desktop/Ada - Work space/q3-review/2026-10-05_q3-review-deck_v01.pptx")
OUT.parent.mkdir(parents=True, exist_ok=True)
INK, ACCENT, GREY, LIGHT = RGBColor(0x1F, 0x2A, 0x44), RGBColor(0x00, 0x72, 0xB2), RGBColor(0x6B, 0x72, 0x80), RGBColor(0xD1, 0xD5, 0xDB)
FONT, W, H, M = "Arial", Inches(13.333), Inches(7.5), Inches(0.6)
prs = Presentation() # owner template: Presentation("/home/user/.../template.pptx")
prs.slide_width, prs.slide_height = W, H
TITLE_ONLY = prs.slide_layouts[5]
def style(tf, size, color=INK, bold=False):
for p in tf.paragraphs:
for r in p.runs:
r.font.name, r.font.size, r.font.bold, r.font.color.rgb = FONT, Pt(size), bold, color
def slide(title, notes=""):
s = prs.slides.add_slide(TITLE_ONLY)
t = s.shapes.title
t.left, t.top, t.width, t.height = M, Inches(0.35), W - 2 * M, Inches(1.1)
t.text = title
t.text_frame.word_wrap, t.text_frame.vertical_anchor = True, MSO_ANCHOR.TOP
t.text_frame.paragraphs[0].alignment = PP_ALIGN.LEFT
style(t.text_frame, 28, bold=True)
if notes:
s.notes_slide.notes_text_frame.text = notes
return s
def text(s, lines, left, top, width, height, size=20, color=INK, bullets=False):
tf = s.shapes.add_textbox(left, top, width, height).text_frame
tf.word_wrap = True
for i, line in enumerate(lines):
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
p.text = ("• " if bullets else "") + line
p.space_after = Pt(10)
style(tf, size, color)
def footer(s, source):
n = len(prs.slides)
text(s, [source], M, H - Inches(0.55), Inches(10), Inches(0.35), size=10, color=GREY)
text(s, [str(n)], W - M - Inches(0.6), H - Inches(0.55), Inches(0.6), Inches(0.35), size=10, color=GREY)
s = slide("Q3 2026 sales review")
s.shapes.title.top = Inches(2.4); style(s.shapes.title.text_frame, 40, bold=True)
text(s, ["Recommendation and plan for Q4 | Ada, 5 October 2026"], M, Inches(3.6), Inches(11), Inches(0.6), 18, GREY)
s = slide("Q3 beat target by 2%, but West missed by 44% and needs a fix in Q4",
notes="Lead with the answer. Ask for the decision on the last slide.")
text(s, ["North (+30%) and South (+20%) beat target; West missed by 44%.",
"West's gap is Pro units: 28 sold against 60 planned.",
"Decision today: approve a two-month West promotion (£3,000)."],
M, Inches(1.7), W - 2 * M, Inches(4), size=22, bullets=True)
footer(s, "Source: shop orders, 1 Jul to 30 Sep 2026")
s = slide("West is the only region below target")
cd = CategoryChartData(); cd.categories = ["North", "South", "West"]
cd.add_series("Revenue vs target", (0.30, 0.20, -0.44))
ch = s.shapes.add_chart(XL_CHART_TYPE.BAR_CLUSTERED, M, Inches(1.6), Inches(8), Inches(4.9), cd).chart
ch.has_legend = False
plot = ch.plots[0]; plot.has_data_labels = True
plot.data_labels.number_format, plot.data_labels.number_format_is_linked = "0%", False
plot.data_labels.font.size = Pt(14)
ch.value_axis.has_major_gridlines = False
ch.category_axis.tick_labels.font.size = Pt(14)
for i, point in enumerate(plot.series[0].points):
point.format.fill.solid(); point.format.fill.fore_color.rgb = ACCENT if i == 2 else LIGHT
footer(s, "Source: shop orders; targets from the Q3 plan")
prs.save(OUT)
for i, sl in enumerate(Presentation(OUT).slides, start=1): # ghost-deck check
print(i, sl.shapes.title.text if sl.shapes.title is not None else "(no title)")
Render and look at every slide (next section). Fix overflow, overlaps and anything under 14 pt, then render again.
Deliver the .pptx and a PDF copy (for reading on a phone), both in the job folder. Tell the owner the paths, slide count, and which slides need their input.
Checking the output
command -v soffice || sudo apt-get install -y --no-install-recommends libreoffice-impress-nogui
python3 -c "import pypdfium2" 2>/dev/null || python3 -m pip install --user --break-system-packages pypdfium2
cd "/home/user/Desktop/Ada - Work space/q3-review" && mkdir -p check
soffice -env:UserInstallation=file:///tmp/lo-check --headless --convert-to pdf --outdir check 2026-10-05_q3-review-deck_v01.pptx
python3 -c "import pypdfium2 as p; d=p.PdfDocument('check/2026-10-05_q3-review-deck_v01.pdf'); [d[i].render(scale=1).to_pil().save(f'check/slide{i+1:02d}.png') for i in range(len(d))]; print(len(d), 'slides')"
Look at each slide image with vision_analyze (decks under 15 slides: all of them; longer: title, summary, every chart slide and a sample). Ask: "Is any text cut off, overflowing its box or overlapping? Is anything too small to read from the back of a room? Does the chart support the title?" python-pptx does not shrink text to fit, so overflow only shows up in the render. Copy check/*.pdf to the job folder as the PDF version if it looks right, then delete check/.
Output
YYYY-MM-DD_topic-deck_v01.pptxand the PDF copy in the job folder.- Message: paths, slide count, the storyline (the titles, numbered), what needs the owner (missing numbers, approvals, speaker).
Checks before you finish
- Titles alone tell the story; every title is a full-sentence takeaway.
- Each slide has one message and one main visual; nothing under 14 pt on a presented deck.
- Every number matches its source and has a source line; charts are native and editable where possible.
- No text overflows or overlaps in the rendered slides.
- The ask (decision, approval, next step) is explicit near the start and on the last content slide.
- Speaker notes on every content slide of a presented deck.
Pitfalls
- Building slides before the storyline. You end up with a pile of facts. Write the titles first.
- Topic titles ("Q3 results", "Next steps"). Say the point.
- Walls of text. If a slide needs more than about 40 words, split it or move the detail to notes or the appendix.
- Overflowing text boxes. Nothing warns you; only the render shows it.
- Pasting chart images when the owner will want to edit numbers. Use native charts unless the chart type needs matplotlib.
- Ignoring the template. With an owner template, use its layouts and placeholders (
references/pptx-recipes.md) instead of drawing your own boxes over them. - Placeholder positions in the default template are sized for 4:3. After switching to 16:9, move and resize the title as in the snippet, or use blank layouts.
See also: write-structured-report (the same storyline as a document), make-clear-charts, make-pdf-document.
Versions
Listed from the source repository.
Reviews
No reviews yet. Be the first.
