Skip to content

Authoring decks

A deck is a directory with a deck.yaml and an optional slides/ folder for bespoke *.slide.tsx modules. The schema is intentionally small: 90% of slides should fit one of the declarative kinds, and the rest get a manifest-backed TSX escape hatch.

After pnpm run build in @alexbayerl/djed-deck-schema, the canonical JSON Schema is emitted to node_modules/@alexbayerl/djed-deck-schema/dist/deck.schema.json (or packages/deck-schema/dist/deck.schema.json in-repo). Scaffolds from djed-deck new include # yaml-language-server: $schema=.../dist/deck.schema.json so editors and agents resolve completions against that file.

kindRequired fieldsOptional
coverid, label, titleeyebrow, subtitle
contentid, label, titleeyebrow, subtitle, body[], ruledItems[], quote, quoteCite
statsid, label, title, stats[]{value,label}caption per stat, eyebrow
quoteid, label, quotecite, eyebrow
funnelid, label, title, bands[]eyebrow, subtitle
timelineid, label, title, markers[]{marker,body}eyebrow, orientation
ringsid, label, title, rings[]{label, diameter, caption?}lede, centerValue, centerCaption, size, aside[] (title/body cards)
matrixid, label, title, x{low,high}, y{low,high}, dots[]eyebrow, subtitle — dots use normalized x/y in 0..1
hbarsid, label, title, bars[]{label, value, hint?}eyebrow, subtitle, total
pipelineid, label, title, stages[]{label, hint?}eyebrow
tsxid, label, fileprops

body is always an array of paragraphs. Each paragraph supports the bounded inline grammar:

{{accent:highlighted text}}

No nesting, no other markup — anything richer must move to a TSX slide.

slug: "acme-investor"
theme: "atlas"
meta:
title: "Acme — Series A"
width: 1920
height: 1080
slides:
- id: "cover"
label: "Cover"
kind: "cover"
eyebrow: "ACME · 2026"
title: "Evidence-first AI for {{accent:critical infrastructure}}."
subtitle: "Series A · April 2026"
- id: "stats"
label: "Why now"
kind: "stats"
title: "The market in three numbers."
stats:
- value: "$4.2B"
label: "TAM by 2028"
- value: "63%"
label: "Procurement attempts blocked by audit gaps"
- value: "11"
label: "Active design partners"
- id: "ask"
label: "Ask"
kind: "tsx"
file: "./slides/ask.slide.tsx"
props:
raise: "$12M"

A tsx slide must reference a file under the deck’s slides/ directory. At build time the path is normalized to a deterministic manifest key that is resolved through import.meta.glob() in the renderer. There is no runtime variable-path imports anywhere — that’s a hard architectural boundary.

decks/acme-investor/slides/ask.slide.tsx
import { Slide, Frame, Eyebrow, Title, Body, Stat } from "@alexbayerl/djed-deck-kit";
export default function Ask({ raise }: { raise: string }) {
return (
<Slide label="Ask">
<Frame>
<Eyebrow>The ask</Eyebrow>
<Title>{raise} to lock in the next 18 months.</Title>
<Body paragraphs={["12 design partners. 4 enterprise pilots. 1 thesis."]} />
<Stat value={raise} label="Series A" />
</Frame>
</Slide>
);
}
Terminal window
djed-deck validate ./decks/acme-investor # schema + IR diagnostics
djed-deck dev ./decks/acme-investor # live preview
djed-deck build ./decks/acme-investor # multi-page screen build
djed-deck build ./decks/acme-investor --target print
djed-deck pdf ./decks/acme-investor --out acme.pdf

validate returns structured diagnostics with paths, severities, and hints — designed to be consumed by editors and agents, not just humans.