Attributes

Blocks, marks, inline objects, and grid cells carry a fixed vocabulary of attributes — plus one escape hatch, data. The vocabulary is closed by design: the model stays predictable, and every consumer 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
class any block, mark, inline object, or grid cell Authored CSS classes, space-separated. These are element identity for publisher styles, not ScriptPub block kinds (a kind’s own selector classes are consumed, not stored here).
style any block, mark, inline object, or grid cell Safe element-local CSS declaration text, serialized as the HTML style attribute — direct formatting for one occurrence, including per-instance break-* fragmentation.
role any block, mark, inline object, or grid cell HTML/ARIA role.
epubType any block, mark, inline object, or grid cell EPUB structural semantics, serialized as epub:type; a space-separated token list.
aria any block, mark, inline object, or grid cell ARIA attributes keyed without the aria- prefix, for example { "label": "Chapter ornament" }.
lang any block, mark, inline object, or grid cell Language override (BCP-47).
dir any block, mark, inline object, or grid cell Direction override — "ltr", "rtl", or "auto".
data any block, mark, inline object, or grid cell Custom escape hatch — a free { key: value } map, serialized as safe data-* attributes.
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?, start?, value? } — flat list metadata; start carries an ordered list’s first ordinal and value an explicit item ordinal.
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; converters without ruby may render it parenthesized or drop it).
title marks A link title ([text](href "title")<a title>) — CommonMark parity.
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.

There is deliberately no pagination attribute: forced breaks, keeps, and their instance overrides are real CSS break-before / break-after / break-inside declarations — in the document sheet per kind, or in an instance’s style attribute for one occurrence.

Attributes split by where they live:

ElementAttrs = { class?, style?, role?, epubType?, aria?, lang?, dir?, data? }
BlockAttrs   = ElementAttrs & { id?, src?, alt?, type?, width?, height?,
                                poster?, controls?, loop?, muted?, narration?,
                                list?, grid?, origin?, originRev?, rev? }
MarkAttrs    = ElementAttrs & { href?, rt?, title?, rev? }
InlineObject = ElementAttrs & { type: "image", src, alt?, title?, width?, height? }
CellAttrs    = ElementAttrs & { colspan?, rowspan?, header?, align? }

An inline image carries its src / alt on the run’s object, not in a mark attribute bag. It still shares ElementAttrs, so a publisher’s drop-cap image can retain its class, role, EPUB semantics, ARIA, language, direction, and custom data independently of the paragraph and any enclosing link.

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

Tiered persistence

The .spub.html file stores the complete vocabulary natively — class, style, role, epub:type, aria-*, lang, dir, id, data-*, data-rev, data-origin, data-narration, and the media attributes are all plain HTML attributes.

Foreign syntaxes do not. Content and kind round-trip losslessly through every conversion, but attributes are tiered — a round-trip preserves content and kind, not necessarily every attribute:

  • Attribute-capable formats (FDX partially, office formats partially) keep what they can express.
  • 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. The media attributes degrade the same way — src/alt only in Markdown, alt-text-or-nothing in the plain-text syntaxes.

This is a deliberate trade: the price of a plain-text or Fountain export 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.

See Translations.