← Corpus / reach-edu-hub / plan
16:9 Export Variant (Play-UI) and the PPT/PDF Pipeline
Plan for the fixed-aspect, no-scroll, no-animation variant of each strategy pitch deck — the Play-UI conversion per the DidiDecks discipline — plus a native-shapes PPT exporter tuned for clean Google Slides import, and PDF via the shell's print route. Written 2026-07-28 while building the Scroll-UI decks, so the conversion is anticipated, not retrofitted.
- Path
- plans/Sixteen-Nine-Export-Variant-and-PPT-Pipeline.md
- Authors
- Michael Staton
- Augmented with
- Claude Code (Fable 5)
- Tags
- Play-UI · Export · PPT · PDF · Google-Slides · pptxgenjs · Deck-Iteration · reach-edu
16:9 Export Variant and the PPT/PDF Pipeline
What the operator asked for
A variant of each pitch deck that is “more or less the same” but fits exactly into 16:9 with no scroll behavior or animations, in preparation for export to PDF and PPT — where the PPT must be near-perfect because the team works in Google Slides (imports from PPT with few glitches when the markup is elegant).
Why this is the Play-UI phase, by the book
The dididecks discipline already names this exactly (repo CLAUDE.md): every deck has two coordinated implementations —
- Scroll-UI (built 2026-07-28): responsive sections, scroll-snap, inline scripts allowed. This is where we design.
- Play-UI: standalone per-slide files at
src/components/slides/{variant}/{slot}-{slug}.astro, rendered by the shell’s/play/[deck]/[variant]/[slot]route insideSlideCanvas— rigid 1920×1080, no responsive CSS, no JS in the slide — which letterboxes to any viewport and exports to PDF without hydration. The shell already ships/play/.../print.astrofor the PDF path.
So “the 16:9 variant” = the Play-UI conversion of each *-p1 variant,
slot-for-slot per slides.ts (recreate, don’t extract — per
/api/slide-decompose discipline).
The load-bearing anticipation: content as data
Every Scroll-UI section built in this pass keeps its content in typed
frontmatter arrays (stats, kpis, rows, steps, tiers, arc) plus a
headline/eyebrow — not prose baked into markup. That makes three consumers
possible from one source:
- Scroll-UI section (exists)
- Play-UI slide (this plan)
- PPT exporter (this plan)
Refactor step (do first): lift each deck’s content arrays out of the
section frontmatter into src/layouts/sections/{deck}/content.ts (typed,
per-slot exports), and make sections import from it. Citations already live in
per-deck citations.ts with canonical hex codes and build-time [n]
indexing — the exporter reuses them as-is.
PPT pipeline (the part that must be perfect)
Approach: generate native PPT shapes with pptxgenjs — never raster.
Google Slides imports native text boxes, autoshapes, and tables with high
fidelity; it mangles embedded images-of-text and ignores exotic effects.
- One Node script per client:
scripts/export-pptx.mjsreadingcontent.ts+citations.ts+slides.tsper deck → one.pptxper deck (and optionally one combined). - Layout: 13.333in × 7.5in (16:9). A small layout vocabulary mirroring the deck primitives: cover, statement (centered quote), stat-grid (2–4 cols of stat cards as rounded rectangles + text), list-cards, table-wall, sources.
- Tokens → PPT theme: export in light mode values from
theme.cssTier-2 tokens (background, text, muted, accent, secondary). Gradient headline spans render as solid accent color in PPT (gradient-filled text does not survive Slides import). - Fonts: map to Google-Slides-native faces (custom webfonts do not embed through Slides import). Heading → a Slides-safe geometric sans; body → Arial/Inter fallback. Record the mapping in DESIGN.md when it exists.
- Citations in PPT: inline
[n]as superscript text runs (pptxgenjssuperscript: true); each deck ends with paginated Sources slides — a PPT can’t scroll, so chunk the ordered citation list ~10–12 per 16:9 slide (Sources 1/3,2/3…). Hex codes stay in the speaker notes per slide for traceability. - No animations, no transitions — by construction.
- Verification loop: import the generated PPT into Google Slides and walk it (browser-drive per the verification blueprint) before it ever goes to the team; glitches found → fix the generator, not the file.
PDF pipeline
Primary: the shell’s /play/[deck]/[variant]/print route (all slides,
1920×1080, no hydration) → headless Chromium print-to-PDF (Playwright), one
PDF per deck. The Sources slides use the same pagination as PPT (no scroll in
print). Fallback: pptx → PDF via LibreOffice headless if a single toolchain is
preferred.
Sequencing
- Content-module refactor — lift arrays to
content.tsper deck; wire Scroll-UI sections to import them (no visual change; verify with build + smoke). - Play-UI exemplar — convert
upward-mobilityslot-by-slot (src/components/slides/um-p1/{slot}-{slug}.astro), register in the shell chooser; operator walks it. - PPT exporter exemplar —
export-pptx.mjsfor upward-mobility; import into Google Slides; iterate until clean. - Fan out both to the remaining seven decks (mechanical once the vocabulary is proven).
- PDF batch via the print route.
Constraints to respect (learned this pass)
- Sources lists exceed one screen — pagination is a first-class layout, not
an afterthought (Scroll-UI got
SourcesPager; Play/PPT get numbered Sources slides). - Header/chrome: Play-UI and exports have no site chrome at all — the
scroll deck’s auto-hide header and
c-toggle are Scroll-UI-only concerns. - Overwhelm slides (KPI walls, money maps) must be re-typeset for fixed 1920×1080 — auto-fit grids don’t exist in PPT; the content.ts arrays make per-slot column counts an explicit exporter parameter.