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-in
— a, 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 textStylename (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.
Links
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:
External links
An external href is a URL of any scheme, spelled as an ordinary <a href>.
Internal links
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.