Deck config
Every field defineDeck accepts, what it resolves them into, and the markup the deck header and footer render inside the canvas.
defineDeck takes a DeckConfig and returns a resolved Deck. It resolves the canvas, resolves every slide into an id, a number, a URL and its chrome defaults, and validates the theme.
import { defineDeck } from "@thebuilder/deckard-core"
import { meridian } from "@thebuilder/deckard-themes"
import { slides } from "@/deck/slides"
export const deck = defineDeck({
canvas: { fit: "contain", height: 1080, mode: "fixed", width: 1920 },
description: "An engineering review built from editable slide patterns.",
footer: { mode: "visible" },
header: { brand: "Deckard", href: "/", meta: "March 2026", mode: "auto" },
slides,
theme: meridian,
title: "Deckard",
})
DeckConfig
titlestring
The deck title. Used as the document title and in slide metadata.
stringdescriptionstring
One line describing the deck.
stringslidesSlideDefinition[]
The deck, in order. Never reordered.
SlideDefinition[]headerDeckHeaderConfig
The header inside the canvas: brand, link, optional meta line, and the default header mode.
DeckHeaderConfigfooterDeckFooterConfig
The footer inside the canvas: the default footer mode and whether the progress element exists.
DeckFooterConfigcanvas?Partial<DeckCanvasConfig>
Overrides on the fixed coordinate space.
Partial<DeckCanvasConfig>1920x1080, fit contain, margin 0theme?SlideTheme
The theme object. A deck without one renders in neutral defaults.
SlideThememotion?"auto" | "frozen"
The default every slide takes. "frozen" holds every motion background in the deck on one fixed frame. Reduced motion, the capture commands, and the presenter preview freeze one either way.
"auto" | "frozen""auto"routes?DeckRoutesConfig
Route prefixes for slides and presenter mode. Set presenter to false when the deck has no presenter page.
DeckRoutesConfig{ slides: "/slides", presenter: "/presenter" }canvas
mode?"fixed"
The only mode.
"fixed""fixed"width?number
Logical pixels. Has to be positive.
number1920height?number
Logical pixels. Has to be positive.
number1080fit?"contain"
How the canvas fits the window.
"contain""contain"margin?number
Browser pixels between the canvas and the window edge. Zero or more.
number0resolveCanvas throws naming the field when width or height is not a positive number, or when margin is negative.
header
brandstring
The name at the left of the header. Rendered as a link.
stringhrefstring
Where the brand links to.
stringmode"visible" | "hidden" | "auto"
The deck-wide default. A slide can override it.
"visible" | "hidden" | "auto"meta?string
One line of standing detail at the right of the header, printed as written: "March 2026", "Rev. C", "Internal".
string"auto" renders the header in the default layout and hides it in a fullscreen layout.
footer
mode"visible" | "hidden" | "counter"
The deck-wide default. A slide can override it.
"visible" | "hidden" | "counter"progress?boolean
Whether the progress element exists at all. Setting false drops the element rather than hiding it, so no theme can paint it back.
booleantrue"counter" is a deprecated alias for "visible". A deck that sets it resolves to "visible".
footer: { mode: "visible", progress: false }
theme
See Themes for the full object. defineDeck validates it: defaultColorMode: "system" needs both modes in colorModes, and a theme listing one mode pins the canvas to it and hides the light/dark toggle.
routes
routes.slides sets the prefix used by every resolved slide URL and presenter preview. routes.presenter sets the popout destination; false removes the presenter control and keyboard shortcut. Paths start with / and cannot be the site root.
routes: { slides: "/example", presenter: false }
What defineDeck returns
Deck is DeckConfig with canvas, slides, and theme resolved. Each slide becomes a ResolvedSlide:
| Field | What it holds |
|---|---|
id |
The URL segment: the slug, or the 1-based position. |
href |
The configured slide path followed by the slide id. |
index |
Zero-based position in the array. |
number |
One-based position, what the counter shows. |
title |
The authored title, or the generated Slide 4 fallback. |
authoredTitle |
Set only when the author gave the slide a title. The fallback in title names a slide in chrome but must never render as its own heading. |
header footer layout background motion |
The slide’s own value, or the deck default. |
stepCount |
Defaults to 0. |
notes slug sourcePath |
As authored. |
body |
The slide’s React node. |
Only SlideSummary (id, number, title, authoredTitle, href, stepCount) crosses into client components, because it has to survive BroadcastChannel serialization for presenter mode. The rendered slide body crosses only as children.
Deck chrome
The header and footer are painted inside the canvas, so they scale with the deck, print into the PDF, and show up in a presenter preview. The runtime renders the structure and the theme decides the look:
<header data-slide-header>
<a data-slide-header-brand href="/">Deckard</a>
<span data-slide-header-title>Themed chrome</span>
<span data-slide-header-meta>March 2026</span>
</header>
<footer data-slide-footer>
<div data-slide-progress style="--slide-progress: 0.25"></div>
<p data-slide-counter>
<span data-slide-counter-current>3</span>
<span data-slide-counter-separator> of </span>
<span data-slide-counter-total>12</span>
</p>
</footer>
The title renders only for a slide that has one, and the meta line only when the deck sets header.meta. --slide-progress is the position in the deck as a fraction, which is what a theme reads to paint a bar, a row of dots, or nothing at all.
The header row is one line inside a canvas that cannot grow. Every part clips itself rather than running off the edge, and the slide title carries by far the largest shrink factor, so it truncates before the brand loses any of itself. Both overflow checks measure the header and footer alongside the frame, so a deck finds out when its chrome asks for more room than it has.