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.
Slide kinds
Section titled “Slide kinds”kind | Required fields | Optional |
|---|---|---|
cover | id, label, title | eyebrow, subtitle |
content | id, label, title | eyebrow, subtitle, body[], ruledItems[], quote, quoteCite |
stats | id, label, title, stats[]{value,label} | caption per stat, eyebrow |
quote | id, label, quote | cite, eyebrow |
funnel | id, label, title, bands[] | eyebrow, subtitle |
timeline | id, label, title, markers[]{marker,body} | eyebrow, orientation |
rings | id, label, title, rings[]{label, diameter, caption?} | lede, centerValue, centerCaption, size, aside[] (title/body cards) |
matrix | id, label, title, x{low,high}, y{low,high}, dots[] | eyebrow, subtitle — dots use normalized x/y in 0..1 |
hbars | id, label, title, bars[]{label, value, hint?} | eyebrow, subtitle, total |
pipeline | id, label, title, stages[]{label, hint?} | eyebrow |
tsx | id, label, file | props |
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.
Example
Section titled “Example”slug: "acme-investor"theme: "atlas"meta: title: "Acme — Series A" width: 1920 height: 1080slides: - 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"TSX escape hatches
Section titled “TSX escape hatches”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.
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> );}Validation and previews
Section titled “Validation and previews”djed-deck validate ./decks/acme-investor # schema + IR diagnosticsdjed-deck dev ./decks/acme-investor # live previewdjed-deck build ./decks/acme-investor # multi-page screen builddjed-deck build ./decks/acme-investor --target printdjed-deck pdf ./decks/acme-investor --out acme.pdfvalidate returns structured diagnostics with paths, severities, and
hints — designed to be consumed by editors and agents, not just humans.