Skip to content
Deckard
Esc
navigateopen⌘Jpreview
On this page

Token contract

Every --slide-* custom property declared in @thebuilder/deckard-core/styles.css, its neutral default, and which part of the runtime reads it.

@thebuilder/deckard-core/styles.css declares every --slide-* token with a neutral default, inside @layer base. A theme redefines them on the canvas and wins, because it ships unlayered CSS and everything in the contract sits in the base layer.

The runtime depends on these names and never on a preset, so a deck with no theme still renders: neutral sizes, app surfaces, no decoration.

@import "tailwindcss";
@import "@thebuilder/deckard-core/styles.css";

Type

TokenDefaultReads it
--slide-font-headingvar(--font-sans)Headings and figures
--slide-font-bodyvar(--font-sans)Body copy and the chrome
--slide-font-monovar(--font-mono)Code
--slide-title-size6remHero and breaker headlines
--slide-heading-size4.25remThe h1 on a content slide
--slide-subheading-size2.625remAn h2 inside a body
--slide-lead-size2.625remThe sentence under a headline
--slide-body-size2remBullet copy, the main text of a slide
--slide-code-size1.75remType inside a CodeBlock
--slide-support-size1.75remCaptions, grid copy, metadata rows
--slide-label-size1.5remEyebrows and other uppercase labels
--slide-label-tracking0.25emLetter spacing on those labels

Every size is set against the 1920x1080 canvas at a 16px root, so 1rem is 16 canvas pixels and the scale reads the way it does on a projector rather than the way it does in a browser window. Multiply a default above by 16 to get what it measures on a canvas 1080 pixels tall.

Line height rides along as a unitless multiplier in the blocks, so changing a size token keeps its leading proportional. Never pair a size token with a fixed leading-7.

Spacing

TokenDefaultWhat it controls
--slide-padding-inline7remThe gutter on both sides of the frame, and inside the canvas header
--slide-padding-block6remTop and bottom padding when the slide has no header or footer
--slide-content-gap2.75remThe rhythm between blocks inside a slide
--slide-item-gap1.625remThe rhythm between rows inside one block
--slide-rail-size24remThe fixed side column on HeroSplitSlide and ProseSlide

The frame those gutters leave is the whole slide, left aligned and uncapped: content starts at the left margin and runs to the right one. Blocks cap their own measure where prose needs it, and anything that wants to be centred centres itself.

Figures

TokenDefaultWhat it controls
--slide-figure-size7.5remA StatGrid figure
--slide-figure-unit-size3.5remThe unit suffix on it
--slide-meter-size0.875remThe height of the proportion bar under it

Surfaces

TokenDefaultWhat it controls
--slide-radius0.75remCards, code blocks, small panels
--slide-radius-lg1.25remThe ContentSlideCard panel
--slide-surfacevar(--card)The raised surface a panel paints
--slide-surface-mutedvar(--muted)FeatureGrid cards, secondary panels
--slide-surface-bordervar(--border)Panel borders, the StatGrid rule
--slide-surface-shadownoneDepth under a panel, where the theme wants it
--slide-accent-softcolor-mix(in oklab, var(--primary) 10%, transparent)The tint on a CardGrid accent card and a highlighted DataTable row

Backgrounds

SlideBackground renders one empty element carrying data-slide-background, and the theme paints each variant with these:

TokenDefaultWhat it controls
--slide-grid-colortransparentThe rule color for the grid variant
--slide-grid-size44pxThe grid pitch
--slide-rulevar(--border)Hairlines a theme draws on the sheet, such as corner brackets
--slide-scanlinenoneA repeating line mask over the canvas
--slide-hatchnoneA diagonal fill, which the spotlight variant uses
--slide-halononeThe text-shadow a theme puts behind display type
--slide-motion-color-1var(--background)The ground colour of a motion background
--slide-motion-color-2var(--muted)The colour its bands and its wash are mixed toward
--slide-motion-color-3var(--accent)The second mix, which lands on the highlights
--slide-motion-speed1How fast the field moves, clamped by the runtime to a usable range

There is no radial wash, veil, glow or spotlight token. The textures a theme has are flat colour, a rule, a grid, and a hatch, because a radial gradient reads as a smudge on a projector and as grey on paper.

The four --slide-motion-* tokens are read off the canvas by the animated background, and only for a variant the theme names in motion. See Motion backgrounds. A theme sets them per variant and per color mode the way it sets everything else it paints:

.aurora-theme .slide-background[data-slide-background="hero"] {
  --slide-motion-color-1: #0a0d1f;
  --slide-motion-color-2: #3a2f86;
  --slide-motion-color-3: #1c7c96;
  background: linear-gradient(160deg, #0a0d1f, #1c2350);
}

That background is what the slide shows while the runtime loads, when the browser has no WebGL, and when the context is lost, so paint a variant you animate as though it never would.

Media

TokenDefaultWhat it controls
--slide-media-foregroundoklch(1 0 0)Text over photography
--slide-media-foreground-mutedoklch(1 0 0 / 85%)Secondary text over photography
--slide-media-overlay-subtlelinear-gradient(to top, oklch(0 0 0 / 35%), oklch(0 0 0 / 10%), transparent)overlay="subtle"
--slide-media-overlay-mediumlinear-gradient(to top, oklch(0 0 0 / 55%), oklch(0 0 0 / 20%), transparent)overlay="medium"
--slide-media-overlay-stronglinear-gradient(to top, oklch(0 0 0 / 75%), oklch(0 0 0 / 35%), transparent)overlay="strong"

Chrome

The colours, type, and spacing of the header and footer, plus the canvas each is allowed:

TokenDefaultWhat it controls
--slide-chrome-foregroundvar(--muted-foreground)Header and footer text
--slide-chrome-emphasisvar(--foreground)The brand
--slide-chrome-bordervar(--border)The rules above the footer and below the header
--slide-chrome-sizevar(--slide-label-size)Chrome type size
--slide-chrome-tracking0.08emLetter spacing on the counter
--slide-chrome-gap1remColumn gap inside header and footer
--slide-progress-tracktransparentThe progress element's own background
--slide-progress-fillvar(--slide-chrome-border)The filled portion
--slide-header-space8remCanvas padding reserved for the header
--slide-footer-space6remCanvas padding reserved for the footer

A theme that redesigns the chrome to a different height sets --slide-header-space and --slide-footer-space, and the slide content moves with it.

Published by the runtime

These are set in the DOM rather than declared in the contract, and a theme or a block reads them:

Variable Set on What it carries
--slide-progress [data-slide-progress] Position in the deck as a fraction from 0 to 1
--slide-chrome-top the canvas --slide-header-space republished as an inset for a bleeding layout
--slide-chrome-bottom the canvas The same for the footer
--deckard-scale the viewport The measured fit, applied as a scale() transform

A fullscreen block pads against the chrome insets so the deck header and slide counter never sit on top of a caption:

className="pb-[calc(3rem+var(--slide-chrome-bottom,0px))]"

Structural attributes

The runtime renders these and the theme styles them. They are as much a part of the contract as the tokens.

Attribute On
data-slide-canvas The canvas element, which also carries the theme class
data-slide-viewport The element that scales the canvas
data-slide-frame The slide frame the overflow check measures
data-slide-background The background element, valued with the variant name
data-slide-motion The canvas inside it, valued with the field: aurora, waves, or wash
data-slide-motion-state What that canvas is doing: running, frozen, or unavailable
data-slide-header, data-slide-footer The chrome
data-slide-header-brand, -title, -meta The three header parts
data-slide-counter, -current, -separator, -total The counter
data-slide-progress The progress element, absent entirely when footer.progress is false
data-slide-surface A block that paints its own border or background
data-slide-panel The ContentSlideCard panel
data-slide-intro The eyebrow and heading block above a body
data-slide-eyebrow The uppercase label above a title
data-slide-title, data-slide-lead A slide’s own headline and the sentence under it
data-slide-kicker The label under a FocusSlide body
data-slide-hero, data-slide-hero-meta The opener and its credit row
data-slide-badge The pill above a centered opener’s title
data-slide-rail, -term, -detail The side column on HeroSplitSlide and ProseSlide
data-slide-breaker, data-slide-breaker-index The section divider and the number it carries
data-slide-accent-rule The short rule a minimal breaker or a statement opens on
data-slide-statement, -text, -source The one-sentence layout, its sentence, its attribution
data-slide-split, -main, -aside A CodeSplitSlide and its two sides
data-slide-note, -index, -text One numbered note in that rail
data-slide-prose, -body, -paragraph The label rail layout and its copy
data-slide-quote, -text, -attribution, -portrait, -name, -source A pull quote, down to the portrait
data-slide-list, -item, -marker, -text A BulletList and its three parts
data-slide-reveal, -item, -item-accent, -title, -description A stepped RevealList and its parts
data-slide-contents, -item, -index, -title, -folio A ContentsList row by row
data-slide-columns, data-slide-column, -index, -label, -title, -text A ColumnGrid and one column of it
data-slide-grid, data-slide-card, -accent, -label, -title, -body A FeatureGrid or CardGrid and its cards
data-slide-media, -item, -frame, -overlay, -caption, -credit A media layout and its parts
data-slide-table, -head, -heading, -row, -highlight, -cell A DataTable
data-slide-timeline, -item, -marker, -done, -date, -label, -detail A Timeline and one milestone
data-slide-log, -row, -tone, -time, -message, -status A LogList; -tone is valued ok, note, or alert
data-stat-grid, -item, -value, -unit, -meter, -caption A StatGrid, down to the proportion bar

These are the other half of the theme contract. Tokens are values; attributes are parts. A theme styles them, decorative ::before and ::after content included, which is how phosphor writes [x] where a list marker goes and ledger hangs a rule there instead, neither of them touching the block. A block may rewrite its markup and its class names freely as long as the attributes stay where they are.

A theme styles parts. It does not add content. A boot log, a status table, a cursor line with words in it are slide content, so they belong in a block the deck composes.

Code blocks are scoped too: [data-slide-canvas] .shiki has its shiki-painted background forced transparent, so a deck never restyles code the surrounding app renders.

Was this page helpful?