Generate professional .pptx files using TypeScript JSX via @pixel/pptx.
.tsx file that exports a deck variable.pptxdeno run --allow-read --allow-write --config {baseDir}/scripts/deno.json {baseDir}/scripts/generate.ts slides.tsx output.pptx [--json]
.tsx slide file (must export const deck = ...).pptx filename--json — structured JSON output for agent consumptionThese rules are distilled from the three masters of presentation design:
Follow these rules for every deck:
layout defaults on .slideWidth={u.in(13.33)} slideHeight={u.in(7.5)} on . The library defaults to 4:3 which looks wrong on modern screens.The library uses fixed-size containers. Text that exceeds the container will overflow and be clipped. Follow these rules strictly:
grow instead of fixed h whenever possible. Inside |
or , prefer grow={1} over explicit h={u.in(X)}. The layout engine will calculate the correct size.0.3 (title) + 4×0.3 (bullets) + 0.6 (padding) = 1.8in. Always round up. with explicit h: count lines × 0.3in + padding × 2. If the result > h, increase h or cut text. with tight heights inside . Use with grow instead — it flexes to fit content.```tsx
{/ ✅ GOOD: grow handles height automatically /}
{/ ❌ BAD: fixed h too small, text will overflow /}
```
Tables are the most common source of overflow. Follow these rules:
cols values must sum to ≤ 11.33in.```tsx
// ✅ GOOD: 2.5 + 4.0 + 4.0 = 10.5in < 11.33in
// ❌ BAD: Column splits equally, table gets squeezed
```
0.45 + 4×0.45 = 2.25in minimum.| Style | Background | Primary | Accent | Text |
|---|---|---|---|---|
| ------- | ----------- | --------- | -------- | ------ |
| Dark (OpenAI) | 0D0D0D | 10A37F | 6E42D3 | FFFFFF |
| Warm (Anthropic) | FDF6EE | D97706 | 1F4E79 | 1C1917 |
| Clean (Apple) | FFFFFF | 007AFF | FF3B30 | 1D1D1F |
| Academic (Stanford) | FFFFFF | 8C1515 | 2E2D29 | 2E2D29 |
| Neutral (Stripe) | F6F9FC | 635BFF | 0A2540 | 0A2540 |
Pick one palette. Don't mix.
The library has no built-in page numbers. Use on every slide:
// Helper — put at top of every .tsx file
const TOTAL = 10; // update to actual slide count
// ⚠️ CRITICAL: <Positioned> coordinates are RELATIVE TO CONTENT AREA (after slide padding).
// For 16:9 slide (13.33 x 7.5in) with padding 0.8/1.0/0.7/1.0:
// Content area = (13.33 - 1.0 - 1.0) x (7.5 - 0.8 - 0.7) = 11.33 x 6.0in
// Bottom-right page number: x = contentWidth - 1.0, y = contentHeight - 0.4
const PageNum = ({ n }: { n: number }) => (
<Positioned x={u.in(10.33)} y={u.in(5.6)} w={u.in(1)} h={u.in(0.4)}>
<Text.P style={{ fontSize: u.font(12), fontColor: textColor, align: "right" }}>
{n} / {TOTAL}
</Text.P>
</Positioned>
);
// Use on every slide — LAST child of <Slide> (renders on top, never occluded)
<Slide background={bg}>
<Column>
{/* ...slide content... */}
</Column>
<PageNum n={1} /> {/* ← MUST be last child for highest z-order */}
</Slide>
Rules:
is relative to content area, NOT the slide origin. Calculate: slideWidth - leftPadding - rightPadding = contentWidth. Position within those bounds. as the last child of — PPTX has no z-index, rendering order = declaration order. Last element renders on top, so page numbers are never occluded by other content.Create a .tsx file. It must export a deck variable:
/** @jsxImportSource @pixel/pptx */
import { Align, clr, Presentation, Slide, Text, u } from "@pixel/pptx";
export const deck = (
<Presentation title="My Deck" slideWidth={u.in(13.33)} slideHeight={u.in(7.5)}>
<Slide background={{ kind: "solid", color: clr.hex("F7F4EE") }}>
<Align x="center" y="center" w={u.in(8)} h={u.in(1.5)}>
<Text.P style={{ fontSize: u.font(32), bold: true }}>
Hello, World!
</Text.P>
</Align>
</Slide>
</Presentation>
);
| Component | Purpose |
|---|---|
| ----------- | --------- |
| Root container. Props: title, layout |
| Single slide. Props: background, layout |
| Horizontal flex layout. Has , |
| Vertical flex layout. Has , |
| Overlapping layers |
| Center/align a single child |
| Absolute positioning |
| Component | Purpose | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ----------- | --------- | ||||||||||||
| Multi-paragraph text body. Props: gap, style | ||||||||||||
| Single paragraph | ||||||||||||
| Inline text run | ||||||||||||
, , | Inline formatting | ||||||||||||
| Hyperlink | ||||||||||||
| Shape: rect, roundRect, ellipse, etc. | ||||||||||||
| Embed image (Uint8Array) | ||||||||||||
|