Attributes

Blocks and marks carry a fixed vocabulary of attributes — plus one escape hatch, data. The vocabulary is closed by design: the model stays predictable, and every adapter knows exactly what it must round-trip. Attribute persistence is tiered — some attributes survive only in syntaxes that can store them.

The vocabulary

Attribute On Description
id any block Link-target id; persisted only when targeted or pinned (see Links).
src, alt void (media) blocks Media source (a reference, never bytes) and accessible description.
type void (media) blocks Explicit media type (MIME); usually inferred from the src extension.
width, height void (media) blocks Intrinsic dimensions in CSS px (integers) — a content fact; presentation sizing stays in style.
poster video blocks Poster frame (the src reference grammar).
controls, loop, muted audio/video blocks Playback flags. controls defaults true; the others false.
narration any block { src?, start, end } — a narration clip: this block’s slice of the work’s narration audio, in seconds. src defaults to meta.narration.
list text blocks { type: "ordered" | "unordered", depth? }list metadata.
grid grid blocks { headerRows?, headerCols? } — the leading N rows / first N cells render as <th> (a11y + Markdown table headers).
colspan, rowspan grid cells Cell spans.
href marks Link target — an external URL or an internal ref.
rt marks Ruby annotation text (rendered <rt> inside a ruby mark; adapters without ruby may render it parenthesized or drop it).
title marks A link title ([text](href "title")<a title>) — CommonMark parity.
lang any block Language override (BCP-47).
dir any block Direction override — "ltr" or "rtl".
origin any block (in translation documents) A block ref (^id) into the original this block translates — the alignment anchor.
originRev any block (in translation documents) The origin block’s revision marker when last aligned — staleness tracking.
rev any block or mark A revision id (config.revisions[].id) — “touched in this pass.” On ins / del marks it yields full tracked changes.
data any block or mark Custom escape hatch — a free { key: value } map.

Attributes split by where they live:

BlockAttrs = { id?, src?, alt?, type?, width?, height?, poster?,
               controls?, loop?, muted?, narration?,
               list?, grid?, lang?, dir?, origin?, originRev?, rev?, data? }
MarkAttrs  = { href?, rt?, title?, rev?, data? }

An inline image carries its src / alt on the run’s object, not in an attribute bag — MarkAttrs stays media-free.

A MarkUse is a bare id when it carries no attributes, and the object form { id, attrs } when it does — see runs.

Tiered persistence

Content and kind round-trip losslessly through every syntax. Attributes do not — a round-trip preserves content and kind, but not necessarily every attribute:

  • data, rev, and on-demand ids persist only in syntaxes that can store attributes — HTML (data-*, native id, data-rev), FDX (natively), JSON (trivially).
  • The media attributes follow the same tiers — full in html/json, src/alt only in Markdown, alt-text-or-nothing in the plain-text syntaxes. The full table is on the Media page.
  • Content-derived syntaxes (plain text, Fountain, most of Markdown) have nowhere to put an attribute without polluting the text, so they simply don’t carry it.

This is a deliberate trade: the price of a plain-text or Fountain file that stays readable and editable in any tool is that it can’t silently smuggle metadata. The things a reader actually sees — the words and their block kinds — always survive.

A note on origin vs src

Both are block attributes and both look like references, but they are unrelated:

  • src is a media source, on a void media block.
  • origin is a translation alignment anchor, on a block in a translation document — a block ref into the original.

origin / originRev were renamed from an earlier src / srcRev precisely to remove that collision. See Translations.