Design System
The design layer gives generated decks a consistent visual language: tokens for palette, typography, shadows, radii and spacing; a token-resolving style facade; geometry-safe layout primitives; and opinionated recipes for whole slides.
DesignTokens
from power_pptx.design.tokens import DesignTokens
tokens = DesignTokens.from_dict({
"palette": {
"primary": "#0B5CFF",
"surface": "#FFFFFF",
"ink": "#0D0D0D",
},
"typography": {
"heading": {"family": "Inter", "size": 44.0, "bold": True},
"body": {"family": "Inter", "size": 18.0},
},
"shadows": {"card": {"blur": 18.0, "distance": 4.0, "alpha": 0.18}},
"radii": {"card": 8.0}, # bare floats are points; ints are EMU
"spacings": {"sm": 8.0, "md": 16.0},
})
# ...or from YAML (needs pyyaml) or extracted from an existing .pptx
tokens_from_yaml = DesignTokens.from_yaml("brand.yaml")
tokens_from_deck = DesignTokens.from_pptx("brand-deck.pptx") # accent1..6, fontsGotcha: bare-int sizes in typography are interpreted as EMU, not points — use floats (44.0) or Pt(44).
The shape.style facade
from power_pptx import Presentation, BBox
from power_pptx.enum.shapes import MSO_SHAPE
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6]) # Blank
shape = slide.shapes.add_shape(
MSO_SHAPE.ROUNDED_RECTANGLE, *BBox.from_inches(1, 1, 3, 2)
)
# Attribute setters fan out to the low-level fill/line/font/effect proxies
shape.style.fill = tokens.palette["primary"]
shape.style.line = tokens.palette["ink"]
shape.style.text_color = tokens.palette["surface"]
shape.style.font = tokens.typography["body"]
shape.style.shadow = tokens.shadows["card"] # assign None to clearGrid and Stack
Layout primitives compute geometry from the slide's real dimensions, so you can't walk off the right edge:
from power_pptx.design.layout import Grid, Stack
from power_pptx.util import Inches, Pt
# Grid computes cells; place() moves an existing shape into a cell span
grid = Grid(slide, cols=3, rows=2, gutter=Pt(12), margin=Pt(48))
for i in range(3):
cell = grid.cell(col=i, row=0) # a Box — splats into add_*
slide.shapes.add_text(*cell, text=f"Tile {i + 1}", align="center")
# Stack walks a cursor down (or across) as you place shapes
stack = Stack(direction="vertical", gap=Pt(8),
left=Inches(1), top=Inches(4), width=Inches(8))
caption = slide.shapes.add_text(BBox.from_inches(1, 4, 8, 0.6), text="Caption")
stack.place(caption, height=Pt(28))Slide recipes
from power_pptx.design.recipes import (
title_slide, bullet_slide, kpi_slide, quote_slide, image_hero_slide,
)
title_slide(prs, title="Q4 Review", subtitle="Engineering", tokens=tokens)
bullet_slide(prs, title="Highlights",
bullets=["Shipped v2.10", "Lint pass clean"], tokens=tokens)
kpi_slide(prs, title="KPIs",
kpis=[{"label": "ARR", "value": "$4.2M", "delta": +0.08}],
tokens=tokens)
quote_slide(prs, quote="Ship it.", attribution="The team", tokens=tokens)
image_hero_slide(prs, title="Big news", image="hero.png", tokens=tokens)Recipes use the Blank layout, so slide.shapes.title is None — address shapes by index.
Shape-level building blocks
Token-driven cards that return small dataclasses exposing their constituent shapes for further tweaks:
from power_pptx import (
add_kpi_card, add_progress_bar, add_gauge,
add_status_pill, add_stat_strip, add_article_card,
)
from power_pptx.util import Inches
canvas = prs.slides.add_slide(prs.slide_layouts[6])
kpi = add_kpi_card(canvas, left=Inches(0.5), top=Inches(0.5),
width=Inches(2.5), height=Inches(1.9),
label="MRR", value="$120k", delta={"delta": +0.08},
tokens=tokens)
kpi.card, kpi.value_box # constituent shapes stay tweakable
add_progress_bar(canvas, left=Inches(0.5), top=Inches(2.8),
width=Inches(6), height=Inches(0.3),
fraction=0.72, tokens=tokens)
add_gauge(canvas, left=Inches(0.5), top=Inches(3.4),
width=Inches(4), height=Inches(0.3),
fraction=0.6, target=0.8, tokens=tokens)
add_status_pill(canvas, left=Inches(0.5), top=Inches(4.1),
width=Inches(1.6), height=Inches(0.5),
text="On track", tokens=tokens)
add_stat_strip(canvas, left=Inches(0.5), top=Inches(4.9),
width=Inches(9), height=Inches(1.6),
items=[{"label": "ARR", "value": "$182M"},
{"label": "NDR", "value": "131%", "delta": +0.03}],
tokens=tokens)
add_article_card(canvas, left=Inches(7.2), top=Inches(0.5),
width=Inches(2.5), height=Inches(3),
title="Q4 wrap", blurb="Highlights from the quarter",
cta_text="Read more", tokens=tokens)Figure adapters
from power_pptx import add_matplotlib_figure, add_svg_figure
from power_pptx.util import Inches
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(4, 3))
ax.plot([1, 2, 3, 4], [2, 4, 3, 5])
add_matplotlib_figure(slide, fig, Inches(1), Inches(1), Inches(4), Inches(3))
add_svg_figure(slide, "diagram.svg", Inches(6), Inches(1), Inches(3))
# add_plotly_figure(slide, fig, ...) works the same way (needs kaleido);
# all figure helpers raise FigureBackendUnavailable when the backend is missingThird-party deps import lazily; a missing backend raisesFigureBackendUnavailable naming the exact pip package to install.