Skip to content
Deckard
Esc
↑↓navigate↵open⌘Jpreview
On this page

Slide metadata

Every field on a slide definition and on a slide module, what each one does, and what the deck defaults resolve them to.

A slide is a body and optional metadata. Two shapes carry it: an entry in the slides array, and a module in deck/slides/*.slide.tsx.

SlideDefinition

An entry in the array. body is required, everything else is optional.

PropType
bodyReactNode

The slide itself.

TypeReactNode
slug?string

Fixes the URL at /slides/<slug>. Lowercase letters, digits, and hyphens, unique across the deck, never all digits. Without one the slide is served at its 1-based position.

Typestring
title?string

Names the slide in the header, the command center, and the presenter flow. A slide without one shows "Slide 4". Never becomes a slug.

Typestring
notes?string

Speaker-only text. Reaches the presenter window and never renders in the deck.

Typestring
stepCount?number

How many reveal steps the slide has. Pair it with SlideStep.

Typenumber
Default0
counted?boolean

false leaves the slide out of the slide count. The footer drops its counter and holds the progress bar where the last counted slide left it, the presenter shows "Not counted", slide lists show a dot for its number, and its share card prints no position. It keeps its URL and its place in the deck.

Typeboolean
Defaulttrue
header?"visible" | "hidden" | "auto"

"auto" renders in the default layout and hides in fullscreen.

Type"visible" | "hidden" | "auto"
Defaultthe deck's header.mode
footer?"visible" | "hidden" | "counter"

"counter" is a deprecated alias for "visible".

Type"visible" | "hidden" | "counter"
Defaultthe deck's footer.mode
layout?"default" | "fullscreen"

fullscreen hands the whole canvas to the slide so media can bleed to every edge.

Type"default" | "fullscreen"
Default"default"
background?"default" | "none" | "spotlight" | "grid" | "accent" | "hero" | "breaker" | "statement" | "closing" | a name the theme adds

Which background variant the theme paints. "hero", "breaker", "statement", and "closing" are roles: a theme that paints the role in motion shows its field, and every other theme renders the role's fallback variant. The canvas and SlideBackground carry the resolved name on data-slide-background, and deckard validate reports a name neither the runtime nor the theme paints.

Type"default" | "none" | "spotlight" | "grid" | "accent" | "hero" | "breaker" | "statement" | "closing" | a name the theme adds
Default"default"
motion?"auto" | "frozen"

"frozen" holds this slide's motion background on one fixed frame. A slide whose background is painted in CSS is unaffected.

Type"auto" | "frozen"
Defaultthe deck's motion
sourcePath?string

Set by slideFromModule and discoverSlides. deckard validate checks it is a file on disk.

Typestring

Use layout, header, footer, and background to change the frame for one slide, rather than special-casing the route.

Background roles

A role names the moment a slide is. Mark the cover, the section breaks, the one sentence, and the last slide with their role, and each theme renders them its own way.

Role Use it on Without a motion field it renders as
"hero" The opening slide "default"
"breaker" A section break "spotlight"
"statement" The one sentence a deck is built around "default"
"closing" The last slide "accent"

The fallbacks are slideBackgroundRoles in @thebuilder/deckard-core, and resolveBackground(theme, background) is what applies them. A theme that lists a role in its motion map keeps the role’s name on data-slide-background and paints its field, as described in motion backgrounds.

{ slug: "intro", background: "hero", body: <HeroSlide … /> },
{ slug: "part-two", background: "breaker", body: <BreakerSlide … /> },
{ slug: "thanks", background: "closing", body: <HeroSplitSlide … /> },

SlideMeta

What a slide module exports as meta. Same fields as SlideDefinition minus body and sourcePath, plus one:

PropType
order?number

Sorts this module inside the discovered group when discoverSlides runs with sort: "order". It cannot move the group. discoverSlides consumes it and leaves it off the definition, so a module can never push itself past a manual slide.

Typenumber

notes can be exported either on meta or as its own top-level export. The top-level export wins.

import type { SlideMeta } from "@thebuilder/deckard-core"

export const meta: SlideMeta = { slug: "pricing", title: "Pricing", order: 20 }
export const notes = "Pause on the middle tier."

export default async function PricingSlide() {
  return <PricingTable plans={await loadPlans()} />
}

A module has to export its component as default. Discovery throws naming the file when it does not, and when the module is async, because an eager glob hands back a promise instead of the exports.

Slide ids

The slides array defines deck order. resolveSlides never reorders it, so moving a slide in the array is the only way to move it in the deck.

Ids are matched exactly. A slugged slide is served only at its slug and never at its position, so there is one URL per slide.

The deck fails to build on a duplicate slug, an empty slug, a slug with characters unsafe in a URL path, or a slug made only of digits.

Backup slides

Mark the slides kept after the close with counted: false, so the count the room sees ends on the last slide you present.

{ slug: "thanks", background: "closing", body: <HeroSplitSlide … /> },
{ slug: "backup-pricing", title: "Backup: pricing", counted: false, body: <PricingTable /> },

With twelve presented slides and two backups, the closing slide reads “12 of 12” and the backups show no counter.

Step reveals

{
  title: "The argument",
  stepCount: 3,
  body: (
    <OpenContentSlide eyebrow="Three parts" title="The argument">
      <SlideStep step={0}>Everyone agrees on the premise.</SlideStep>
      <SlideStep step={1}>Almost nobody acts on it.</SlideStep>
      <SlideStep step={2}>Here is what that costs.</SlideStep>
    </OpenContentSlide>
  ),
}

step is zero-based, and a step shows once the current step reaches it. Hidden steps stay in the layout, so a stepped slide takes its full height from the first step and deckard check-overflow measures it fully revealed.

The current step also rides in the URL as ?step=, read on the client, which is how the presenter preview stays in sync without making the route dynamic.

Was this page helpful?