Skip to content

Commit e821c2d

Browse files
PyCon Italia 2026 (#27)
* PyCon italia 2026 * add monty section * add prefect logo * Add Agents→Production slides to pycon-italy deck - Add ArchitectureDiagram, FeatureGrid, PromptRound, ProposalExplanation components - Add 'production' tab with 16 slides covering: - Progressive architecture diagrams (temporal/6-step/8-step/logfire) - Both Logfire free tier feature slides - Enterprise slide - Agent Optimization demo with 20Q LLM vs LLM (4 prompt rounds) - Two white-background slides (Enterprise + 20 Questions rules) now match * Include optimization slides and logfire slides * add guido slide --------- Co-authored-by: Bill Easton <williamseaston@gmail.com>
1 parent 2e2a2b4 commit e821c2d

34 files changed

Lines changed: 9967 additions & 0 deletions
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
---
2+
name: deckx
3+
description: Create a deck with deckx. Use when the user mentions "deckx", "deck" or "slides", asks to build a slide deck from MDX, asks to convert a brand palette into a deck stylesheet, or asks how to convert a deckx HTML deck into a PDF. Covers project layout, deckx.toml config, deck.mdx authoring, custom React components, the styles.css token contract, and the Chrome headless PDF command.
4+
---
5+
6+
# deckx
7+
8+
`deckx` builds a single, self-contained HTML slide deck from one MDX file plus a CSS theme and an optional folder of React components. The HTML is print-ready and converts to PDF via Chrome headless.
9+
10+
## Installation
11+
12+
```bash
13+
bun init -y # if you don't already have a package.json initialized
14+
bun add @samuelcolvin/deckx
15+
```
16+
17+
The npm package is `@samuelcolvin/deckx`; the installed CLI binary is `deckx`. `npm i` / `pnpm add` work the same way. Inside `deck.mdx` you always import from `"deckx"` (a Vite alias, not the npm name).
18+
19+
## Project layout
20+
21+
```
22+
my-deck/
23+
├── deckx.toml # config: title, theme, tabs, footer, paths (all optional)
24+
├── deck.mdx # the slides
25+
├── styles.css # CSS variable overrides (optional)
26+
└── components/ # optional custom React/TSX components
27+
└── Hello.tsx
28+
```
29+
30+
```bash
31+
bunx deckx dev # dev server with HMR on http://localhost:5173/
32+
bunx deckx html # build to ./dist/index.html
33+
bunx deckx pdf # build HTML, then ./dist/deck.pdf via Chrome headless
34+
```
35+
36+
`html` and `pdf` accept an optional output-path positional - e.g. `bunx deckx pdf my-deck.pdf` or `bunx deckx html out/slides.html`. Use `--dir <dir>` to point at a build directory other than the current one. To convert an existing HTML file to PDF without rebuilding, use `bunx deckx html-to-pdf <input.html> <output.pdf>`.
37+
38+
## `deckx.toml`
39+
40+
All fields are optional - a deck with only `deck.mdx` works.
41+
42+
```toml
43+
title = "My Deck - April 2026" # browser tab title
44+
45+
# light | dark | markdown-light | markdown-dark (default: light)
46+
# "markdown-*" variants render source-style decorations on top:
47+
# heading "#"/"##" prefixes, "**" strong markers, traffic-light dots,
48+
# mono slide counter, diamond bullets.
49+
theme = "light"
50+
51+
# Small footer rendered bottom-right of every slide.
52+
footer = "Confidential - do not share"
53+
54+
# Path to a favicon for the browser tab. .svg / .png / .ico / .jpg.
55+
# Inlined as a data URI so the deck stays self-contained.
56+
favicon = "assets/favicon.svg"
57+
58+
# Path overrides (defaults shown).
59+
mdx = "deck.mdx"
60+
styles = "styles.css"
61+
components = "components"
62+
63+
# Optional tab nav. When present, <Slide tab="..."> highlights the matching tab.
64+
tabs = [
65+
{ id = "intro", label = "Intro" },
66+
{ id = "details", label = "Details" },
67+
]
68+
```
69+
70+
If `tabs` is omitted, the `tab` prop on `<Slide>` is ignored and slides render with a plain title topbar.
71+
72+
## `deck.mdx`
73+
74+
Standard MDX. Import `Slide` from `"deckx"`, plus any custom components from `./components/`.
75+
76+
```mdx
77+
import { Slide } from "deckx";
78+
import Hello from "./components/Hello.tsx";
79+
80+
<Slide layout="title" title="Investor Deck / April 2026">
81+
82+
### Section Label
83+
84+
# My Deck
85+
86+
## A subtitle
87+
88+
</Slide>
89+
90+
<Slide tab="intro">
91+
92+
# Hello world
93+
94+
- Bullet one
95+
- Bullet two
96+
97+
<Hello name="friend" />
98+
99+
</Slide>
100+
101+
<Slide layout="statement">
102+
103+
# One big idea.
104+
105+
</Slide>
106+
```
107+
108+
**Do NOT wrap slides in `<div className="deck">`** - deckx adds it for you.
109+
110+
### `<Slide>` props
111+
112+
All props are optional. A bare `<Slide>` renders a regular content slide using the deck theme.
113+
114+
#### `layout` - structural layout (default `'content'`)
115+
116+
Picks how the slide arranges its body. Adds a `.<value>-slide` class to the `.slide` element so you can target each variant from `styles.css`.
117+
118+
- `'content'` (default) - regular slide. Headings, paragraphs, bullets, code, and tables flow top-down inside `.slide-body`. Use for the bulk of your deck.
119+
- `'title'` - cover / section slide. Bottom-aligns the hero; h1 is 4rem with tight letter-spacing; h2 renders in `--accent`. Pair with a leading `### Section Label` for a mono uppercase eyebrow.
120+
- `'statement'` - centered one-liner. The body is centered both vertically and horizontally; h1 is 3.4rem; paragraphs cap at 80% width. Use for transitions between sections or "one bold idea" beats.
121+
122+
#### `theme` - color variant (defaults to the deck theme)
123+
124+
Per-slide palette override.
125+
126+
- Omit (or pass `'dark'`) - the slide inherits the deck-level `theme` from `deckx.toml`.
127+
- `'light'` - forces a single slide onto the light palette (`--bg-light`, `--color-text-light`, `--color-heading-light`) regardless of the deck theme. Useful when one slide needs to break out - e.g. a screenshot of a light-themed UI on an otherwise dark deck. Adds `.light-slide` to the slide.
128+
129+
There is no inverse override: on a light deck, `theme="dark"` has no effect. If you need a single dark slide on a light deck, target it from CSS with a custom `id` or class.
130+
131+
#### Other props
132+
133+
- `tab` - string matching an `id` from the `tabs` array in `deckx.toml`. Replaces the plain topbar title with the tab bar, with this slide's tab highlighted. Clicking any tab in any slide jumps to the first slide whose `tab` matches. If `tabs` is not configured, the prop is silently ignored.
134+
- `title` - plain text rendered in the topbar when `tab` is not set. Also drives `document.title`, so the browser tab updates as the active slide changes. Ignored when `tab` is set.
135+
- `space` - `'tight'` reduces bullet/paragraph spacing (use when a slide is close to overflowing); `'wide'` increases padding and line-height (use for slides with very little text where you want generous breathing room).
136+
- `fontSize` - `'large'` bumps body text from 1.15rem to 1.35rem, and h1/h2 proportionally. Useful for slides that need to read from the back of a room.
137+
- `id` - sets the HTML `id` on the underlying `<section>`. Useful for targeting one slide from `styles.css` (`.slide#hero { ... }`) without adding a custom class.
138+
139+
### MDX gotchas
140+
141+
- Use `-` (hyphen-minus), never `` (em dash).
142+
- Keep blank lines around block elements inside a slide, but **not** between the last block and the closing `</Slide>` - MDX misparses a trailing blank.
143+
- `### Section Label` at the top of a slide renders as a diamond + uppercase mono label.
144+
- Slides must fit **279.4mm × 157.2mm** (16:9). If overflowing, try `space="tight"` first, then drop content.
145+
146+
### Images
147+
148+
Place images in your project (e.g. `assets/`) and import as ES modules:
149+
150+
```mdx
151+
import logo from "./assets/logo.png";
152+
<img src={logo} alt="Logo" />
153+
```
154+
155+
Vite inlines them into the final HTML. The markdown `![alt](path)` syntax does **not** get inlined - always use `<img src={imported} />`.
156+
157+
### Code blocks
158+
159+
Fenced code blocks are syntax-highlighted at build time by [Shiki](https://shiki.style). Tag the fence with a language so tokens get coloured:
160+
161+
````mdx
162+
```ts
163+
export function greet(name: string): string {
164+
return `hello, ${name}`;
165+
}
166+
```
167+
````
168+
169+
All Shiki bundled languages work without any per-deck configuration. Highlighting happens during the build, so the output HTML carries only inline-styled `<span>`s - no grammar files, no runtime highlighter.
170+
171+
Both a light and a dark theme are emitted into every code block. deckx switches between them automatically based on the slide's effective theme: dark deck themes (`dark`, `markdown-dark`) use the dark code theme, and `<Slide theme="light">` always shows the light code theme even inside a dark deck. Pick the two themes in `deckx.toml`:
172+
173+
```toml
174+
code_light_theme = "github-light" # default
175+
code_dark_theme = "github-dark" # default
176+
```
177+
178+
Browse the available themes at <https://shiki.style/themes>.
179+
180+
## Custom components
181+
182+
Any `.tsx` file in `components/` (or wherever `deckx.toml` `components` points) can be imported into `deck.mdx` with a relative path:
183+
184+
```tsx
185+
// components/Hello.tsx
186+
export default function Hello({ name }: { name: string }) {
187+
return <p>Hello, {name}!</p>;
188+
}
189+
```
190+
191+
```mdx
192+
import Hello from "./components/Hello.tsx";
193+
<Hello name="world" />
194+
```
195+
196+
React 19 is available. Components see the same CSS variables your `styles.css` defines, so to stay on-brand, read from variables (`color: var(--accent)`) rather than hard-coding colors.
197+
198+
## Authoring `styles.css`
199+
200+
The base stylesheet handles all layout, typography, slide dimensions, the topbar, transitions, and the PDF `@page` setup. `styles.css` only needs to override CSS variables on `:root` to set brand tokens.
201+
202+
### Variable contract
203+
204+
Backgrounds:
205+
206+
- `--bg-deck` (default `#0d0d0d`) - background outside the slide, presenter mode only.
207+
- `--bg-slide` (default `#1a1a1a`) - default slide background.
208+
- `--bg-light` (default `#ffffff`) - slide bg for `light` / `markdown-light` decks and `<Slide theme="light">`.
209+
- `--surface` (default `#2a2a2a`) - inline code background, table headers.
210+
211+
Text:
212+
213+
- `--color-text` (default white @ 85%) - body text on dark slides.
214+
- `--color-heading` (default `#ffffff`) - h1, h2, h4, strong on dark slides.
215+
- `--color-muted` (default `#8f888e`) - heading prefixes, slide counter, subdued UI.
216+
- `--color-text-light` (default `#2a2230`) - body text on light slides.
217+
- `--color-heading-light` (default `#1a1018`) - headings on light slides.
218+
219+
Accents:
220+
221+
- `--accent` (default `#4a9eff`) - primary accent: bullets, h3, links, blockquote bar.
222+
- `--accent-secondary` (default `#ff6b6b`) - em, link hover.
223+
- `--accent-tertiary` (default `#b388ff`) - hr gradient stop.
224+
- `--accent-aqua` (default `#4ad7c5`) - inline code text, active tab, topbar tabs.
225+
226+
Fonts:
227+
228+
- `--font-body` (default system sans stack) - body and headings, unless `--font-heading` overrides.
229+
- `--font-heading` (default inherits body) - headings.
230+
- `--font-mono` (default system mono) - inline code, code blocks, tabs, counter, h3.
231+
- `--font-terminal` (default inherits body) - body inside `.slide-body`.
232+
233+
### CSS class hooks
234+
235+
For finer control beyond the variable contract, target these classes from `styles.css`. Most decks won't need them - prefer overriding variables first.
236+
237+
Slide structure:
238+
239+
- `.deck-presenter` - outermost wrapper. Owns the viewport background and the fit-to-window scaling transform.
240+
- `.deck` - inner slide stream (direct child of `.deck-presenter`).
241+
- `.slide` - a single slide (`<section>`). Sized 16:9, holds topbar + content + optional footer.
242+
- `.slide-topbar` - 48px window-chrome bar at the top of every slide.
243+
- `.slide-content` - padded body wrapper below the topbar (this is what `--slide-padding` applies to).
244+
- `.slide-body` - inner MDX content container, descendant of `.slide-content`.
245+
- `.slide-footer` - bottom-right footer text, rendered when `footer` is set in `deckx.toml`.
246+
247+
Slide modifiers (added to `.slide` based on props):
248+
249+
- `.title-slide` - `layout="title"`, bottom-aligned hero.
250+
- `.statement-slide` - `layout="statement"`, centered hero.
251+
- `.light-slide` - `theme="light"`, forces the light palette.
252+
- `.space-tight` / `.space-wide` - vertical spacing density.
253+
- `.font-large` - bumps body text size.
254+
255+
Topbar - left (traffic lights + title/tabs):
256+
257+
- `.topbar-dots` - the traffic-light anchor (clicking jumps to slide 1). Only visible under `markdown-*` themes.
258+
- `.topbar-dot` plus `.topbar-dot--red` / `.topbar-dot--yellow` / `.topbar-dot--green` - individual dots.
259+
- `.topbar-title` - plain title text, shown when `<Slide title="...">` is set without a `tab`.
260+
261+
Topbar - tabs (rendered when `<Slide tab="...">` is set and `tabs` are configured in `deckx.toml`):
262+
263+
- `.topbar-tabs` - the tab bar container.
264+
- `.topbar-tab-group` - per-tab wrapper containing the link plus its leading separator.
265+
- `.topbar-tab-sep` - the `` glyph between tabs.
266+
- `.topbar-tab` - the tab link.
267+
- `.topbar-tab--active` - applied to the currently-selected tab.
268+
269+
Topbar - right (prev/next + counter):
270+
271+
- `.topbar-nav` - container holding the prev/next buttons and slide counter.
272+
- `.topbar-nav-btn` plus `.topbar-nav-prev` / `.topbar-nav-next` - the nav buttons (auto-hidden in print).
273+
- `.topbar-nav-counter` - the `01/12` slide counter.
274+
275+
Deck-level theme classes (applied to both `<html>` and `.deck-presenter` based on `theme` in `deckx.toml`):
276+
277+
- `.theme-light` / `.theme-dark` / `.theme-markdown-light` / `.theme-markdown-dark`
278+
279+
MDX content inside `.slide-body` renders as plain HTML (`h1`-`h4`, `p`, `ul`, `ol`, `pre`, `code`, `table`, `blockquote`, `a`, `img`, `hr`) - target those tags directly with `.slide <tag>` selectors rather than expecting deckx to add wrapper classes.
280+
281+
### Mapping a brand palette
282+
283+
1. Pick the **most distinctive** brand color, assign to `--accent`. Pick a warm counterpoint as `--accent-secondary`.
284+
2. Pick a slightly off-white for `--color-heading` (pure white reads sterile under projector light).
285+
3. Pick a tinted dark for `--bg-slide` (pure black is harsh).
286+
4. For light slides, pick a tinted light bg (cream, eggshell, lavender - not pure white) plus a near-black text color → `--bg-light` / `--color-heading-light` / `--color-text-light`.
287+
5. For custom fonts, self-host woff2 files in `assets/` and declare them with `@font-face` in `styles.css`, then point `--font-body` / `--font-mono` at the family. Do **not** use `@import url(...)` from Google Fonts - CSS spec requires @import to come before all other statements, which Vite's CSS bundling routinely violates when concatenating the base stylesheet with yours. `@font-face` can appear anywhere. Use [Google Webfonts Helper](https://gwfh.mranftl.com/fonts) to download woff2 files; Vite inlines them into the deck, keeping it self-contained for offline / PDF export.
288+
289+
```css
290+
@font-face {
291+
font-family: 'YourFont';
292+
src: url('./assets/YourFont-Regular.woff2') format('woff2');
293+
font-weight: 400;
294+
font-display: swap;
295+
}
296+
297+
:root {
298+
--bg-slide: #...; /* tinted dark */
299+
--bg-light: #...; /* tinted light */
300+
--surface: #...;
301+
302+
--color-heading: #...; /* off-white */
303+
--color-text: rgba(...);
304+
--color-heading-light: #...;
305+
--color-text-light: #...;
306+
307+
--accent: #...; /* signature brand color */
308+
--accent-secondary: #...; /* warm counterpoint */
309+
310+
--font-body: 'YourFont', system-ui, sans-serif;
311+
--font-mono: 'YourFontMono', ui-monospace, monospace;
312+
}
313+
```
314+
315+
## Building & PDF
316+
317+
`bunx deckx pdf` is the easy path: it builds the HTML, prints the exact Chrome command it's about to run, then runs it. Output lands at `./dist/deck.pdf`.
318+
319+
If Chrome / Chromium can't be found, copy the printed command and run it yourself with the right binary path. On Linux deckx auto-detects `google-chrome`, `google-chrome-stable`, `chromium`, or `chromium-browser`.
320+
321+
Paper size in the printed command matches the slide dimensions (11in × 6.1875in = 16:9). If you override `--slide-width` / `--slide-height` in `styles.css`, edit the `--paper-*` flags to match before running.
322+
323+
To spot-check the PDF (requires `pdftoppm` from poppler):
324+
325+
```bash
326+
mkdir -p ./tmp && pdftoppm -r 100 ./dist/deck.pdf ./tmp/page -png
327+
```
328+
329+
One PNG per slide lands in `./tmp/`, gitignore that path.

2026-05-pycon-italy/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.projects/cache
2+
.projects/vault
3+
.env
4+
.env.local
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.14

2026-05-pycon-italy/Makefile

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
.DEFAULT_GOAL := pdf
2+
VERSION := 1
3+
4+
.PHONY: install
5+
install: ## Install dependencies
6+
cd deck && bun install
7+
uv sync --frozen
8+
9+
.PHONY: format
10+
format: ## Format Python code
11+
uv run ruff format
12+
uv run ruff check --fix --fix-only
13+
14+
.PHONY: lint
15+
lint: ## Lint Python code with ruff and basedpyright (strict)
16+
uv run ruff format --check
17+
uv run ruff check
18+
19+
.PHONY: typecheck
20+
typecheck: ## Run basedpyright over the Python code
21+
uv run basedpyright
22+
23+
.PHONY: dev
24+
dev: ## Start the dev server with live reload
25+
cd deck && bunx deckx dev
26+
27+
.PHONY: html
28+
html: ## Build the deck to deck-v$(VERSION).html
29+
rm -f deck-v*
30+
cd deck && bun run typecheck && bunx deckx html ../deck-v$(VERSION).html
31+
@echo "Slides written to deck-v$(VERSION).html"
32+
33+
.PHONY: pdf
34+
pdf: html ## Build the deck to deck-v$(VERSION).pdf
35+
cd deck && bunx deckx html-to-pdf ../deck-v$(VERSION).html ../deck-v$(VERSION).pdf
36+
@echo "PDF written to deck-v$(VERSION).pdf"

2026-05-pycon-italy/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# PyCon Italia
2+
3+
Bologna, 2026-04-29
4+
5+
* [Talk schedule](https://2026.pycon.it/en/keynotes/duable-agents-long-running-ai-workflows-in-a-flakey-world)
6+
* [Deck PDF](deck-v1.pdf)
7+
* [Pydantic AI + temporal docs](https://pydantic.dev/docs/ai/integrations/durable_execution/temporal/)

0 commit comments

Comments
 (0)