Skip to content

Markup syntax

Keystone is not a markup language and doesn't invent one. You write your book in plain Markdown — the same # headings, *emphasis*, - lists, [links](url), and ![images](path) you'd use anywhere. Keystone reads it with Pandoc, so every rule of Pandoc's Markdown — itself a superset of CommonMark — is yours to use. Those references are where you learn the basics; nothing on this page replaces them.

What Keystone does add is a vocabulary, and it hangs that vocabulary on two constructs Pandoc's Markdown already provides: the fenced div and the bracketed span. Both are generic containers — a way to attach a name and attributes to a chunk of your text. On their own they mean nothing. Keystone gives the names meaning: write ::: aside and the aside name resolves to a shortcut that styles the block. The div and span are the grammar (Pandoc's); the names you put in them are the words (Keystone's). So the rest of this manual is, in effect, a tour of which names exist — and this page is just the two forms that carry them, plus the attribute syntax they share.

Fenced divs

A fenced div wraps one or more blocks — paragraphs, lists, images, other divs — in a named container. Open it with a line of three or more colons and the name, put your content beneath, and close it with a bare fence:

::: aside
This whole paragraph is set apart as an aside.
:::

The name (aside) is what tells Keystone how to style the block — it names a shortcut. Pandoc builds the container; Keystone reads the name and styles it.

Three rules about fences trip people up, because breaking any of them produces no error — just wrong output.

Leave a blank line above the opener

An opening fence needs a blank line before it whenever a paragraph sits directly above. This is the ordinary Markdown paragraph rule, not a fence quirk: only a blank line ends a paragraph — a lone newline just continues it — so without one Pandoc folds the ::: into the prose above as a continuation line, no div forms, and the fence prints verbatim:

The scene ends here.
::: aside            ← no blank line above; the whole block prints verbatim
A note that never becomes an aside.
:::

One blank line is all it takes:

The scene ends here.

::: aside
A note that becomes an aside.
:::

Three colons, minimum

The fence needs three or more colons. Two is not a fence: Pandoc reads the line as ordinary text, no container is created, and the literal characters print in your book.

:: aside ::     ← not a fence; prints verbatim
::: aside       ← a fence

More than three is fine and sometimes clearer — see nesting below.

Keep fences flush left

A fence must sit at the left margin. Give it any leading space and Pandoc stops seeing a fence — the line reverts to ordinary text (four or more spaces make it an indented code block). This holds inside another div too, so resist the instinct to indent nested divs the way you'd indent nested code — nesting stays flush-left:

::: aside
::: quote
Nested, and both fences stay at the margin.
:::
:::

When flush-left fences make the nesting hard to read, give the outer fence more colons than the inner. This is purely for your eyes: a closing fence is any bare colon line, and it closes the innermost div still open — the counts needn't match. The extra colons only make it obvious which opener a closer belongs to:

:::: aside
::: quote
Now the outer pair stands out from the inner.
:::
::::

Bracketed spans

A span is the inline counterpart — it names a run inside a paragraph without breaking the flow. Wrap the text in square brackets and follow it immediately — no space — with an attribute block in braces:

A word in [bold]{.font style="bold"} mid-sentence.

The .font class names the shortcut, exactly as the div name does; a span just carries it in the Pandoc attribute form covered next. Not every shortcut works inline — a figure is inherently a block — so each reference entry notes whether a span form exists.

Attributes: classes, identifiers, values

Both forms carry the same attribute block — Pandoc's syntax for attaching a class, an identifier, and key–value pairs to an element. In braces, in any order:

{#opening .font family="eb-garamond" size="large" style="italic"}
  • .class — a class, prefixed with a dot. This is the shortcut name. An element may carry several classes, but only the first that names a shortcut takes effect — stacking two shortcut classes won't combine them, so nest the divs instead.
  • #identifier — a unique id, prefixed with a hash. Use it as a link target ([jump](#opening)) or to name an element a shortcut acts on.
  • key=value — an attribute that tunes the shortcut. Quote any value containing spaces (title="A Long Name"); single bare words need no quotes (size=large).

The bare ::: aside fence is shorthand for the full ::: {.aside} — a name with no dot is read as a class. Reach for the full brace form when you need an id or attributes alongside the name:

::: {#epigraph .font family="eb-garamond" style="italic"}
A passage with an id and two attributes.
:::

Closing and nesting

Every opener needs its closer. A div's closing fence is a line of bare colons (no name); a span closes with ] immediately before its {…}. Forget a span's bracket and it silently fails — Pandoc reads the [ and everything after as plain text. A div is the exception:

An unclosed div is the one structural slip Pandoc does report, with a line number — see Unclosed div. Every other malformed fence or span is silent, so the discipline is simple: close what you open, and keep fences flush left. What Keystone can and can't catch here is spelled out in Mistakes Keystone can't catch.