Prezzer decks as software
A Prezzer deck is a small Bun + React application. Slides are components, in-slide reveals are "beats" of a per-slide state machine, and prezzer build bakes everything — markup, styles, scripts, images, self-hosted fonts — into a single dist/index.html that presents from file:// with no network.
This file carries the workflow and the map. The references/ directory carries exact API tables, verified code patterns, and gotchas; read the file that matches the work before guessing at an API.
| Reference | Read when |
|---|---|
| references/api.md | Writing slides: Deck props, SlideDef fields and defaults, Beat, hooks, keyboard/touch map, hash deep links |
| references/chrome.md | Placing headers, stat rails, badges, starfields, art layers; the .prezzer-* CSS class inventory |
| references/theming.md | Colors, glow, fonts, z-layers, createTheme, --prezzer-* CSS variables, self-hosting fonts for offline |
| references/motion.md | Slide transition personalities, spring presets, the reveal/stagger/glow variant catalog |
| references/widgets.md | Beat-driven diagrams (pull) or self-timed demos that claim the spacebar (push), with a verified working example |
| references/authoring.md | Shaping the story: outline, acts, timing budget, compression path, speaker notes, the facts.ts pattern |
| references/verification.md | Before handing off: the check/build gates, the dev-server asset gotcha and its verified fix, offline and browser passes |
Start from the supported path
bun create prezzer my-talk
cd my-talk
bun dev
The generated repository is the contract: index.html is the entry, src/main.tsx mounts <Deck>, src/slides.tsx is the deck registry, src/index.css wires Tailwind 4 and the font stacks, and bunfig.toml registers the serve plugins. Scripts: bun dev (serve with HMR), bun run check (format, lint, typecheck), bun run build (bake the artifact). Keep the registry in src/slides.tsx until one file per slide genuinely improves navigation.
Entry points — import from the right place
| Import | Provides |
|---|---|
prezzer | Deck, DeckProvider, Beat, useBeat/useDeck/useDenyMode/useDeckTheme, SlideDef/ActDef types, theme tokens, motion variants |
prezzer/chrome | SlideHeader, StatRail, CreedChip, RolloutBadge, Starfield, SlideArt (plus the shell overlays for custom shells) |
prezzer/widgets | DeckWidgetHandle, useWidgetRegistration (plus SlideWidgetProvider for manual composition) — not re-exported from the root |
prezzer/theme, prezzer/motion | Focused imports of the same tokens and variants the root exports |
prezzer/styles.css | The shell and chrome styles; import once in the entry |
prezzer/bun-plugin | Keeps linked local dev on one React/Motion instance |
The table and the references are curated working sets, not exhaustive export inventories — references/api.md and references/chrome.md go deeper, and packages/engine/src is the final word.
Minimal deck
import { Beat, Deck, type SlideDef } from "prezzer";
import "prezzer/styles.css";
function Thesis() {
return (
<main>
<h1>context is the product</h1>
<Beat at={1}>retrieval decides what the model can become.</Beat>
</main>
);
}
const slides: SlideDef[] = [
{
id: "S1",
title: "the thesis",
beats: 2,
notes: ["pause before the reveal"],
component: Thesis,
},
];
export function Talk() {
return <Deck slides={slides} />;
}
A beat count includes the initial state: beats: 2 means one in-slide advance. <Beat at={n}> gates ordinary content; useBeat() drives diagrams and any scene whose whole state changes together.
Shape the story before the components
A strong deck has a factual outline, a timing budget, a narrative arc, a compression path, speaker notes, and explicit demo fallbacks before visual implementation begins. Mark deep-dive slides with deep: true so the presenter sees what compresses first. Review every claim against its source repository or live system — slides are visual anchors; narration carries the detail. One load-bearing idea per slide beats shrinking text to fit. The full workflow, including the facts.ts single-source pattern for live numbers, is in references/authoring.md.
Gotchas that bite fastest
- Serve with
prezzer dev, never plainbun index.html. The bare HTML server SPA-fallbacks every unmatched path, so<img src="/art/x.png">,fetch("/shots/a.json"), and CSSurl(/fonts/x.woff2)return the HTML page while the bake works fine. The starter'sbun devscript already runsprezzer dev, which servespublic/correctly; on prezzer 0.1.0 (predates the command) use the verifieddev.tsfallback in references/verification.md. - The bake inlines only literal asset paths.
prezzer buildstring-matches rooted paths against the built output; a runtime-constructed path like`/art/${id}.png`is never inlined and silently breaks offline. Keep asset paths literal. <Beat>wrappers are motion-transformed, so they become the containing block for absolutely-positioned children.bottom-anchoring lands at the wrapper's flow position, not the slide bottom. Prefer flow layout inside beats.- Hash routing is positional and mixed-index.
#16is the sixteenth slide, not outline idS16, and the beat suffix is 0-indexed —#4.2is slide four with two reveals already fired.
Verify before handing off
bun run check
bun run build
The built dist/index.html is the product — open it from file:// and drive every slide, beat, widget, note, grid entry, and deny state there, not just in dev. Turn networking off once to prove the deck is self-contained. The full checklist, the browser screenshot recipe, and the CLI flags are in references/verification.md.