Skip to main content
A slide has two audiences. A viewer has to see the reveal and a reader has to be told what it was, and DeckTalk measures both. This guide is the four rules a slide is judged by: every cued element carries a description, every reveal changes enough of the frame to be measured, no motion is still playing half a second after its cue, and a page that animates for itself drops that motion under reduced motion. decktalk schema page prints the whole attribute table with each value, default, range and the finding it raises. Read it beside this page rather than guessing an attribute name.

The canvas is the frame’s shape

The runtime builds a canvas element, #dt-canvas, scales it to fit the window and centres it. The canvas has the shape of the project’s frame, [video] size, and a short side of 1080 CSS pixels: 1920 by 1080 for wide, the default, 1080 by 1080 for square and 1080 by 1920 for vertical. A size written in your stylesheet is therefore a size on the video frame. A page may supply its own <div id="dt-canvas"> and the runtime will use that one, which is how a deck puts one figure under all of its slides. Place every element absolutely, so one reveal never moves another. Both packaged decks do this.
deck/index.html
The runtime puts the canvas’s size on :root as --dt-width and --dt-height, and its shape as data-dt-shape, which is wide, square or vertical. The starter lays every box out across --dt-width, and stacks its slides for a square or vertical frame under a :root:not([data-dt-shape="wide"]) rule, so a square copy of a project is the project copied with size = "square" and nothing else changed. Keep every cued element out of the bottom 192 px of the canvas, where two lines of caption sit above a margin. A player draws captions there, so an element that arrives in that band can be covered at the moment it arrives. The starter’s lowest band sits at top: 720px and ends about 840 px down, clear of it. Keep a slide to about four reveals. A derivation that needs more starts a second slide.
[video] size also takes a width and a height in pixels, such as "1280x720". The page is laid out on the canvas of that shape, so a smaller or larger frame of one shape draws the same slides at another resolution, and a frame of another shape lays the page out again by its own rules.

Describe every cued element

The transcript, the audio description and every reader who cannot see the picture are written from data-describe. It is a noun phrase naming the subject of the reveal, and the runtime supplies the verb for each moment: an arrival “appears”, a step back “steps back”, a return “comes to the front” and a departure “leaves”.
deck/index.html
An element that has a cue, carries no data-describe and draws no text of its own earns PAGE_NO_DESCRIPTION, which is an error. A shape with no words in it is the usual cause, because a filled block is the one kind of reveal that is easy to see and impossible to read. Three attributes go with it.
  • data-describe="" is a decorative element, said out loud in the markup. An empty description clears the finding and writes no transcript line.
  • data-describe-out replaces the departure sentence when leaving means something of its own, such as the lesson’s data-describe-out="the hand-written rules drop away".
  • data-describe-class gives a description to each class change, as cue:description pairs separated by a vertical bar. A data-class change with no description beside it earns PAGE_CLASS_UNDESCRIBED, because the transcript would otherwise lose the change entirely.

Make the change large enough to measure

DeckTalk compares two frames on brightness alone. A pixel counts as changed when its luma differs by more than verify.probe_diff_luma, which is 40 out of 255 by default, and what is judged is the share of the frame those pixels cover. On a 1920 by 1080 frame, 0.3 percent is about 6,220 pixels and 0.1 percent about 2,073, which is roughly an eighty by eighty solid block and a forty-six by forty-six one. Design against the larger number, because a reveal that only just clears the floor on a frozen frame can fall under it once the encoder has been over it. Three consequences follow from comparing luma and nothing else.
  • A colour swap of the same lightness changes nothing. It is also invisible to a viewer with low vision. Change the lightness as well as the hue.
  • A line of text is too thin. A line of 44 px type is a few hundred pixels of ink. Give a small reveal a filled container that arrives with it, which is why both packaged decks reveal filled cards, pills and bands rather than text on white.
  • An outline is too thin. Fill the shape.
Raising verify.probe_diff_luma does not help, and its published hazard says why: a high value counts only a change that crosses most of the range, so a reveal that fades in, or a light element on a light slide, reads as no change at all.

Keep every motion under half a second

MEASURABLE_SPAN_SECONDS is 0.5, and it is the one ceiling the page contract is built around. A cue still moving half a second after it fires cannot be measured, because that is the frame the next cue is read from. No value any attribute admits can reach it on its own.

The entrance

data-in-style is the closed word that says how an element arrives, and each word declares its own length. data-in-seconds overrides that length. It runs from 0.12 to 0.44 in steps of 0.04, which is three to eleven frames at 25 fps, the slowest rate a film is recorded at, and a value off that grid earns PAGE_BAD_VALUE. It governs the entrance alone and never when the entrance starts. An element whose measured motion still runs past 0.5 seconds earns PAGE_MOTION_OVERRUN, which is an error. Since the longest entrance a page may write is 0.44 seconds, the two ways to reach it are motion.scale above one and a staggered container.

The stagger

data-stagger spreads one cue across a container’s children, from 0.04 to 0.2 seconds apart in steps of 0.04. The total is exact arithmetic: the last child starts one step per earlier child after the cue, then plays its own entrance.
The lesson example staggers four tiles at 0.04 with a fade entrance, so its total is 0.04 × 3 + 0.24, which is 0.36 seconds and clears the ceiling.
deck/lesson.html
The same four tiles at 0.2 with a draw entrance would total 0.2 × 3 + 0.44, which is 1.04 seconds, and DeckTalk measures that exactly and reports PAGE_STAGGER_OVERRUN as an error. Fix it by shortening the step, choosing a shorter entrance, or splitting the container across two cues. A container that carries data-stagger and has no children spreads one entrance over nothing, which is PAGE_STAGGER_EMPTY.

The other spans

data-words="appear" shows a line one word at a time from nothing, and a line of more than eight words takes longer than one cue can carry, which earns PAGE_APPEAR_TOO_LONG. Shorten the line, or use highlight, which starts every word dim and brightens each one as it is said.

Honour reduced motion

motion.reduce renders the whole film as the variant a viewer who asks for less motion should be given. The recorder asks Chromium for a page that prefers reduced motion, the runtime puts the class dt-reduced on the root element, and every entrance the runtime owns becomes a plain fade with no travel, no scale and no overshoot. The film keeps its length, its captions and its cue times, so the reduced build and the ordinary build are the same film with the motion taken out.
There is no flag for it, because no flag duplicates a settings key. motion.scale is the companion setting, a multiplier from 0.25 to 4 on every declared span. A scale that carries a span past half a second earns PAGE_MOTION_OVERRUN, so slowing a deck down is judged like any other overrun. The runtime can only reduce the motion it owns. A data-class hands the change to the page’s own stylesheet, and that stylesheet owes the reduced form. The lesson example writes both halves.
deck/lesson.html
DeckTalk lays every slide out once, adds each class named in data-class, and reads the longest animation or transition the stylesheet gives that element. Under a reduced render, a class that still animates earns PAGE_CLASS_NOT_REDUCED, which is an error and names the class. It can only be raised under a reduced render, so judge the page that way at least once:

Look at the slide before you record it

decktalk check judges the written files and the pages together, opens no recording, spends nothing and prices what a voiced build would cost. It freezes each slide either side of each cue and reports the changed share. Every finding on this page except PAGE_APPEAR_TOO_LONG, which needs the voice’s clock and comes from record, comes out of that one command. --no-frames keeps the browser and drops the freeze comparison, and --no-pages judges the written files with no browser at all and then says which judgements it could not reach. decktalk storyboard freezes every slide at every cue onto one page, build/storyboard.html, and narrows to --section, --slide, --after, --before and --at. It is the human checkpoint before anything is bought, and it is where unrendered maths, overlapping elements and text running off the frame become obvious.

Next