Themes and fonts
Seven presets and four font roles. A story declares one line and looks like a different film.
Every frame on this page is drawn in the colours the renderer actually reaches for, and every specimen is set in the typeface it names. None of it is an impression of the result.
What a theme is
A complete set of decisions, not a palette. A light theme does not just have a pale background — it needs its label outline inverted with it, because a dark halo on a light page makes every label look bruised. And then, against the obvious symmetry, its caption slab stays dark, because a cream slab on a cream page has no edge and the caption stops reading as a caption. Those two decisions point in opposite directions and neither is visible in a colour picker, which is the reason the presets exist.
A document declares theme: at the root. It can name a preset, override single values, or both. Anything it does not mention keeps the default, so a document with no theme block renders exactly as it did before themes existed.
theme:
preset: forestThat is the whole of it for most documents.
The seven presets
Chosen by subject rather than by surface. Dark, light and dark-with-serifs is a range of backgrounds; these are a range of things to be about, which is how an author actually picks one.
midnightAI, science, technologyThe default, and the only one that is a default rather than a decision. Cyan on navy reads as instrument panel, which is right for a system diagram and wrong for almost everything about people.
paperHistory, cartography, archiveslightA printed page: warm cream, rust accent, Georgia throughout. The one preset where the type is the oldest thing in the frame, which is what makes an 1840s coastline look like it belongs to its own century.
editorialLong-form reportingGold on near-black, and the largest display scale of the seven. Built for a frame that opens with one sentence and expects you to read it.
roseRelationships, psychologyWarm plum with a dusty rose accent, and the only preset that pairs two faces by default: a serif says the large things, a sans carries the explanation under them. A sentence about someone's life should not read in the same voice as a chart axis.
forestNature, ecology, wellnessMoss on deep green, chosen to stay green over a satellite basemap rather than disappear into one. A serif display keeps it from reading as a wellness app.
arcticMedicine, research, measurementlightLight like paper but cool and sans — a clinical page rather than a printed one. Its label halo and caption slab are inverted with it; a light theme that forgets either has invisible labels.
monoMinimal technical explainersNo colour at all, and the smallest display scale, so nothing in the frame claims more than it is. Monochrome is about colour and not typeface — the caption stays in the sans, because a paragraph of narration set in a monospace is a chore to read.
Changing one value
Any colour in a theme can be set by name, on top of a preset or on its own. The names are the parts of a frame rather than abstractions, so there is nothing to learn beyond what you are looking at.
theme:
preset: forest
kicker: "#D9B36C" # the small line above a scene title
counter: "rgba(255,255,255,0.08)"page | Behind everything, including the bands above and below a map |
kicker | The small line above a scene title |
title | The scene title, and chart values |
dek | The subtitle beside the title |
caption | Reserved for a host drawing its own caption furniture |
legend | The legend along the bottom, and chart categories |
note | The footnote under the legend |
counter | The scene number, large and nearly transparent |
track | The progress rail |
fill | The part of the rail already played |
label | Annotation labels |
halo | The outline that keeps a label readable over a basemap |
slab | The caption slab |
slabInk | The words on the caption slab |
halo and slab are the two to be careful with. If you lighten the page, lighten the halo with it or every label goes dark-on-dark. Do not lighten the slab to match: it was tried on paper, and a cream panel on a cream page turned the caption into a line that appeared to belong to the annotation behind it. Both light presets keep the slab reversed out for that reason.
Fonts have four roles
A theme used to carry one typeface, which meant a document could be a serif document or a sans document and nothing else. The pairing that makes editorial design work — a serif that argues over a sans that explains — was not expressible at all.
So fonts names a stack per role, and the roles are the same four words scale and emphasis already use. An annotation marked emphasis: display takes the display size and the display face without being told twice.
theme:
preset: rose
fonts:
display: '"Playfair Display", Georgia, serif'
body: 'Inter, system-ui, sans-serif'
webfonts: ["Playfair Display", "Inter"]display | The largest annotations`emphasis: display` — a single sentence that is the whole frame. |
title | Scene titles, the kicker above them, the scene counterAnd any annotation marked `emphasis: title`. |
body | The subtitle beside a title, chart values, every unmarked annotationAbsent emphasis means body, so this is most of a document. |
caption | The caption slab, the legend, the footnote, chart categories, the scale barThe smallest type in the frame, and the only role that is read at length. |
A role a theme leaves out falls back to font, the single stack that was there before. Set only font and the whole document is in one face, which is what every example story still does.
A font the machine does not have
This is the part worth reading twice. A canvas cannot wait for a typeface it was not told about. Ask for Caveat before it has loaded and the frame is drawn in whatever the browser substituted — no error, no warning, nothing wrong with the file. It is the one typographic failure that leaves no trace in the output, and the only way to catch it by eye is to already know what Caveat was supposed to look like.
So a document says which faces it needs, and the player loads them before it draws. Every preset stack is a system face, so nothing is fetched unless you ask for something.
theme:
fonts:
display: '"Caveat", cursive'
webfonts: ["Caveat"] # without this line, you get a substituteThe linter catches the half of this it can see without a browser: a face named but not declared, a face declared but named by no role, a preset that does not exist, a role that is not a role. The player catches the other half, where a face was declared, requested, and never arrived — and an export refuses to write a film rather than bake in a substitute for twenty minutes.
Families are fetched from Google Fonts by name, and only for a document that asked. A name Google does not have stays missing and is reported rather than quietly replaced. Keep the list short: each family is a request the viewer waits for before the first frame.
Pairings worth stealing
Each of these is set below in the real face, over the real page colour, with the theme block that produces it. Copy the block.
The field notebook
A survey in the handwriting of the person who walked it. Handwritten display, Georgia underneath — the hand carries the feeling and the serif carries the words.
theme:
preset: paper
fonts:
display: '"Caveat", cursive'
webfonts: ["Caveat"]
scale:
display: 3.4The feature opener
A high-contrast serif over a neutral sans, on the warm plum of rose. The pairing editorial design is built on: one face argues, the other explains.
theme:
preset: rose
fonts:
display: '"Playfair Display", Georgia, serif'
title: '"Playfair Display", Georgia, serif'
webfonts: ["Playfair Display"]The nature journal
A brush hand on deep green. Kalam has thicker strokes than Caveat, so it survives being laid over a satellite basemap where a thin hand would break up.
theme:
preset: forest
fonts:
display: '"Kalam", cursive'
webfonts: ["Kalam"]Soft history
Fraunces has the warmth of a woodblock face without the costume. Set over Lora it reads old without reading antique, which is the difference between history and a theme park.
theme:
preset: paper
fonts:
display: '"Fraunces", Georgia, serif'
title: '"Fraunces", Georgia, serif'
body: '"Lora", Georgia, serif'
caption: '"Lora", Georgia, serif'
webfonts: ["Fraunces", "Lora"]The technical explainer
A geometric sans for the headings and a monospace for the captions, with no colour anywhere. The one place a monospace caption earns itself: when the caption is naming a thing you would type.
theme:
preset: mono
fonts:
display: '"Space Grotesk", system-ui, sans-serif'
title: '"Space Grotesk", system-ui, sans-serif'
caption: '"JetBrains Mono", ui-monospace, monospace'
webfonts: ["Space Grotesk", "JetBrains Mono"]Keep the caption short if you set it in a monospace. A monospace is wider per character and harder to read at length, so a two-line narration becomes three lines and a chore.
Handwriting on dark
Patrick Hand is an even, upright hand — closer to lettering than to script — so it holds its shape in light type on a dark page where a looser hand would turn to lace.
theme:
preset: midnight
fonts:
display: '"Patrick Hand", cursive'
webfonts: ["Patrick Hand"]One weight only. A document that asks for bold gets a synthetic bold, which on a handwritten face looks like a smudge.
Four ways to make it worse
A handwritten face for the body or the caption
A hand is lovely at 60px and illegible at 12. The caption is the only role read at length and the only one laid over a basemap, so it is the worst possible place for a face with uneven strokes. Put the hand on display and leave the rest alone.
Two display faces
If the headline and the subtitle are both doing something expressive they compete, and the frame has no quiet part. One face with something to say, one that gets out of the way.
A face the frame is too small for
A high-contrast display serif — Playfair, Didot — has hairlines that vanish when the type is small or the export is 720p. Use it where the scale is above about 2.5, and check it at the resolution you are actually exporting.
Five webfonts
Each one is a request the viewer waits through before anything is drawn, and in an export it is time added to every run. Two is usually the answer. Three is a reason.
Size is a separate dial
scale sets how large each emphasis is drawn, as a multiple of the body size. Themes differ here as much as they do in colour: editorial opens at 2.8 and mono at 2.3, which is most of why one feels like a magazine and the other like a diagram.
theme:
preset: paper
scale:
display: 5.0 # a presentation, read from across a room
title: 2.9A document with no map, where the words are the whole frame, usually wants a much larger display than a map story does — the example presentations run between 4 and 5. One caveat: a kind: chart annotation labels itself at a fixed size and ignores this, which the linter will tell you about if you raise the scale past 2.