Marks & links

A mark is inline formatting applied to a run — bold, italic, underline, small-caps, a link, ruby annotation. Marks are applied in the model’s runs — to text runs and to object runs alike (a linked inline image or an emphasized formula keeps the mark outside the atomic object). Like block types, marks nest freely, and they are recognized by the same selector rules as blocks.

Marks follow the same two-species vocabulary as blocks: the supported HTML inline elements are built-ina, em, strong, b, i, u, s, mark, small, sub, sup, cite, q, abbr, dfn, code, kbd, samp, var, time, bdi, bdo, del, ins, ruby — with lowercase tag ids and lowercase labels (a → “link”, em → “emphasis”), present in every work undeclared. config.marks stores an array of MarkDef: custom marks (Capitalized id AND label, e.g. "NoteInline") and overrides of built-ins (the built-in’s lowercase id, a Capitalized label). The built-ins strong, em, and u carry the standard editor shortcuts (Mod-b / Mod-i / Mod-u).

MarkDef

Field Type Required Description
id string Unique mark id. One class-safe token.
label string Display label; default: the id.
selector string The mark’s CSS selector — recognition AND spelling, e.g. "em", "u.underline". Same rules as a block’s selector; the write default tag is span.
editor object { "shortcut": string } — e.g. "Mod-b".
syntax object Foreign-syntax spellings, keyed by syntax id (below).

A mark’s styling, like a block’s, is a sheet rule with the same selector — u.underline { … }.

An inline element matching no declared mark keeps an open identity: its first class token, else its tag — so a foreign <cite> or <span class="x"> round-trips without a declaration.

Foreign-syntax spellings

The per-syntax mapping used by import/export converters (never by the file itself, whose spelling is the selector):

MarkSyntax   = { match?: MarkMatch[], write?: MarkSpelling }
MarkMatch    = { construct?: string }            // a fountain marker, an fdx style, an md delimiter
MarkSpelling = { delim?: string, construct?: string }
  • delim — the Markdown / Fountain delimiter ("*", "**", "_"; a Markdown link is "[]").
  • construct — the FDX text Style name (Bold / Italic / Underline / Strikeout).

Example

{
  "id": "em",
  "label": "Italic",
  "selector": "em",
  "editor": { "shortcut": "Mod-i" },
  "syntax": {
    "md":       { "write": { "delim": "*" } },
    "fountain": { "write": { "delim": "*" } },
    "fdx":      { "write": { "construct": "Italic" } }
  }
}

A mark with no match in a foreign syntax is not inferred there, but can still be written — so a mark can round-trip through a syntax that can only express it on serialize.

A link is a mark carrying an href attribute (so its MarkUse takes the object form, { id, attrs: { href } }). It may also carry a title — the CommonMark [text](href "title") title, HTML’s <a title>; support is tiered like any attribute. There are two target species:

An external href is a URL of any scheme, spelled as an ordinary <a href>.

An internal href is a reference into the document, #-prefixed. Resolution precedence:

Form Resolves to
#^<id> A block ref — the block carrying that id.
#<section-id> A section ref — a section id, standard or custom.
#<name> / running number A structural ref to a division — by heading text or running number (“Scene 12”), resolved against the derived outline.

Structural refs need no stored id — but renaming a heading breaks links to it (as in a wiki); an editor may offer to fix references.

Footnote references

A footnote reference is still an ordinary internal block ref; it does not add a second link model. The link mark may carry the noteref token in its epubType, and the destination block may carry the footnote token — preserved or inferred semantics, not new machinery.

A reader MAY open a note reference as an anchored preview instead of moving the reading position immediately. Such a preview MUST expose an action that follows the underlying block ref to the full note. In non-previewing readers the same reference behaves as a normal internal link, so note navigation remains a portable document behavior rather than application-only sidecar data.

Ids are written on demand

A block carries a persisted id in the file only if something targets it — a link, a translation origin anchor — or the user pinned one. The parser never mints ids into the file; ephemeral editor-internal ids are separate and non-persisted. This “on-demand” rule keeps the file minimal — nothing is written that the text doesn’t need.