Films and contact sheets

The film player, contact sheets of frames, and the report card: how the system presents a finished artifact on the page.

Updated

A finished film reaches the site as three files: the web copy that plays, its poster (the film's title card) and a contact sheet of its frames. public/motion/published.json pins the cut of each published film by the hashes of its video and audio streams, and pnpm build:motion writes the registry the pages read, src/lib/motion.ts. The site publishes 39 cuts: 9 films, 6 films of the translation series and 24 knowledge base films, with the German, Spanish and Japanese cuts of one film counted among the 9.

Film player

Pick a published film. The player shows its poster until the first press, then plays the web copy with the browser's own controls.

The poster is a lazy image inside a play button, and the video has no poster attribute until it plays, because a browser fetches that attribute at page load wherever the video sits. The video preloads nothing. The first press starts the film, turns on the native controls and moves focus to the video, so the keyboard reaches those controls next. The width and height attributes reserve the 16:9 box before anything arrives, so nothing shifts. Captions are offered in the native menu and stay off until the viewer chooses them.

Import and props

import BrandFilm from '@/app/brand/BrandFilm';

<BrandFilm
  src='/media/designing-docs-film.mp4'
  poster='/media/designing-docs-poster.jpg'
  name='the Designing docs for humans film'
/>;
PropTypeDefaultWhat it does
srcstringnoneThe web copy under /media, as a root path.
posterstringnoneThe title card, shown in the play button until the first press.
namestringnoneWhat the film is called to a screen reader. The play button reads play and this name.
captionsstringnoneA WebVTT file, offered in the native controls and off until chosen.
captionsLang{ locale, label }EnglishThe captions' locale and the name the native menu shows.
autoStartbooleanfalseStarts the film on mount without moving focus. A language toggle passes it when the cut it replaces was playing.

The host sizes and frames the video. The player's own stylesheet draws only the play button and its badge, a 56 px paper plate on the poster, which moves to the lower right corner at 28 px on a phone so it never covers a title card's title.

The published films

PartValue
Video1920 by 1080 MP4 under /media, 15 to 99.8 seconds
Poster1920 px JPEG, 60 KB to 505 KB
CaptionsWebVTT under /motion/captions, on 24 of the 39 cuts
Films9 films, 6 translation series films, 24 knowledge base films

Contact sheet

A contact sheet holds two frames a second of a cut, eight to a row. Drag across the frame or the strip under it, step with the Frame control, or play the sheet at the rate it was cut at.

The sheet is one WebP. The plate shows one tile at a time as a crop of that image, sized so a 384 px tile spans the frame and moved to the tile's column and row, so scrubbing loads nothing new. Frame k is taken from the middle of the half second from k / 2 to (k + 1) / 2 seconds. pnpm build:motion counts a sheet's frames only when its size matches the grid for its cut's length, so a page never states a count it cannot read from the picture.

Import and props

import { SHEET_TILE } from '@/components/docs/demos/artifacts/film-list';

/* frame k of a sheet with `columns` tiles to a row, cropped into a box one tile wide */
const scale = box.clientWidth / SHEET_TILE.width;
const x = (k % columns) * (SHEET_TILE.width + SHEET_TILE.gap) * scale;
const y = Math.floor(k / columns) * (SHEET_TILE.height + SHEET_TILE.gap) * scale;
box.style.backgroundImage = `url(${sheet.src})`;
box.style.backgroundSize = `${sheet.width * scale}px auto`;
box.style.backgroundPosition = `${-x}px ${-y}px`;
FieldTypeWhat it holds
srcstringThe sheet's WebP under /motion/sheets.
width, heightnumberThe sheet's pixel size.
framesnumberTwo a second for the cut's length, rounded up.
columns, rowsnumberThe grid: (width + 3) / 387 and (height + 3) / 219.

The sheet's numbers

PartValue
Tile384 by 216 px
Gap3 px
Row8 tiles, 3093 px wide; the How General Translation works sheet has 6 to a row
Rate2 frames a second from frame 0
Frames30 to 200 per sheet
FileWebP, 0.2 MB to 2.4 MB, with the full PNG beside it when the PNG is at most 20 MB

Report card

The report card is the result of the /report-card page, which grades how well a site is localized. Here it stands alone with a sample report for example.com; switch its state to see the empty card, the wait and the filled card.

The card draws its whole structure from the first paint: the meter, a ghost score and the six named rows with their grade scales unlit. While the grader runs, the rows wait in place, and the report fills them without moving anything below. A filled row opens its evidence in a dialog: the captured markup, the expanded findings and a prompt to paste into a coding agent. The empty card is inert and hidden from assistive technology, so a screen reader never hears ghost rows.

Import and props

import ReportCard from '@/components/try/ReportCard';
import '@/components/try/try.css';

<div className='try-root'>
  <ReportCard report={report} state='done' />
</div>;
PropTypeWhat it does
reportReport or nullThe graded report from analyze(); null draws the empty card.
stateidle, loading, settling, done or errorThe run's phase. loading and settling hold the waiting look.

The card's stylesheet scopes every rule under .try-root and reads the toolchain's color tokens (--tc-paper, --tc-ink, --tc-hair, --tc-accent), so a host sets those or sits inside a page that does.

The card's numbers

PartValue
Score0 to 100, with a grade from A to F
Meter ticks0, 18, 38, 63, 88 and 100, the grade boundaries
Categorieshreflang tags, language declaration, locale routing, translated metadata, content language, charset and direction
Evidenceat most 4 snippets of 1200 characters and 12 findings per category

On this page