5 Rich writing
The prose surface: typography, offline math, diagrams, figures, callouts, citations, sidenotes and raw HTML passthrough.
Everything you’d reach for in a technical post works out of the box, rendered server-side and shipped offline: no CDN, no client re-run when the reader’s device flips to dark.
5.1 Prose and typography
Standard Markdown works as expected: bold, italic, strikethrough,
inline code, links, nested lists, blockquotes, and pipe
tables with per-column alignment:
| Cell language | Runs | Output |
|---|---|---|
{python} | in a warm Jupyter kernel | spliced back as blocks |
{js} | in the reader’s browser | mounted live |
{mermaid} | in the browser, lazily | a rendered diagram |
Smart typography turns straight quotes into curly ones and handles ellipses…, matching the look of a hand-written post.
Code blocks are syntax-highlighted server-side and get a hover Copy button.
A fence whose language token isn’t recognised still renders, just unhighlighted and
with no warning, so check the label if a block you expected to be highlighted looks
flat (a common cause is a typo like ```pyton). The language is read from the first
token and the rest of the info string is ignored, so a pasted ```rust,ignore still
highlights. When a block is meant to be plain (a terminal transcript, sample output),
label it text or console.
5.2 Math
Math renders server-side with KaTeX and ships fully offline (the fonts are inlined
at build time). Inline math like sits in running text; display
math stands alone; and a labelled equation is numbered and cross-referencable.
Multi-line aligned environments work too. The discrete Fourier transform
[1] is given by Equation 5.1:
5.3 Diagrams
A {mermaid} code block renders client-side with mermaid.js; the colours are
CSS-driven, so they follow the theme. Adding %%| label: fig-… and %%| fig-cap:
turns a diagram into a numbered, captioned figure you can cross-reference, just
like an image. As Figure 5.1 shows, one block model feeds three render targets:
flowchart LR Q[".tmd"] --> BM["block model"] BM --> H["HTML page"] BM --> S["website + nav"] BM --> K["book + TOC"]
Every diagram type mermaid supports works the same way (sequence, state, class, ER, gantt, and the rest); the syntax for each is in mermaid’s own documentation.
5.4 Figures and captions
A standalone image with a {#fig-…} attribute, or a code cell tagged with
#| label: fig-… and #| fig-cap:, becomes a numbered <figure>, and @fig-
cross-references resolve to its number. Figure 5.2 is drawn by a matplotlib cell
that sets no theme colours:
inline figures default to a transparent background with neutral-grey axes, so they
track the light/dark theme on their own.
5.4.1 What an image gets without asking
Point at a .png, .jpg/.jpeg, .gif or .webp and the rest is automatic. There
is nothing to configure and nothing to pre-process.

Its box is reserved before it loads. Figure 5.3 above is emitted with the
intrinsic width and height read from the file itself, so the prose below it never
jumps as the image arrives. This happens in the preview too, which is the point: a
preview that does not shift the way the built page shifts is not previewing it.
Images are copied across unchanged, so shrink or re-encode the source yourself if the byte count matters.
.svg and .avif are the exceptions to the box-reservation rule above: an SVG has no
intrinsic pixel size to state, and AVIF decoding is deliberately not compiled in, so
nothing is emitted for either.
5.5 Callouts
Fenced divs become callouts, layout grids, or generic containers.
Use a title= attribute or a leading heading to label a callout; otherwise it
falls back to the callout kind.
A callout with collapse="true" becomes a native, JavaScript-free disclosure:
The full block model (click to expand)
Every top-level element carries data-block-id (a content hash), data-sourcepos
(its source range), and, for included files, data-source-file.
5.6 Citations and cross-references
With a bibliography: in the front matter, [@key] citations become numbered
links to an auto-generated References section, formatted from the BibTeX file.
This page cites the TeXbook [2] and the FFT paper [1], and
groups with locators work too: [2, 1, p. 297]. Cross-references
resolve to labelled links across @fig-, @sec-, @tbl-, @eq- and @lst-,
carrying the resolved number when known (as @eq-dft and
@fig-scree above do): across chapters, so a @fig- in one chapter resolves
to its figure in another.
In a site or book, bibliography: can instead live once in _site.yml and be shared
by every page; a page’s own bibliography: is then merged over it. See
Configuration.
5.7 Footnotes and sidenotes
Footnotes use the standard [^label] syntax. A reference renders as a superscript
number, and the note itself renders in the right margin, beside the line that cites
it.11Like this one. Write [^label] where you want the marker, and [^label]: …
anywhere for the body; Taliesin does the numbering and the placement.Back There is no list of endnotes at the bottom of the page: a note sits where
you need it, so reading one costs a glance instead of a jump to the end and back.
Narrow the window past the point where there is a margin to put a note in and the notes fold away; tapping a number reveals that one in place. Print does the same, since paper has no reader to tap.
Notes are for asides short enough to sit in a narrow column, and a definition carrying block content (a list, a quote, a code block) is flattened to its text with a warning, because a margin note can only hold inline content.
For an aside with no reference to hang it off, a ::: {.column-margin} block floats
into the same margin column on a wide screen and folds into the flow on a narrow one:
web-native marginalia a PDF can’t do. .column-margin is the only spelling. Footnotes
and margin blocks share one column, so the two never collide.
This is a margin note. On a wide screen it sits in the margin, aligned to where it was written; narrow the window and it collapses to an indented note. Math works here too: .
The opposite move on the same axis is to make a block wider than the reading column:
::: {.column-page} widens up to the page width, for content the ~70-character measure
is simply wrong for.
| Class | What it does | Reach for it when |
|---|---|---|
.column-margin | Floats the block into the right margin on a wide screen, folds it into the flow on a narrow one | An aside with no footnote marker to hang it off |
.column-page | Widens the block up to the full page width | A wide table, a multi-panel plot, a diagram whose labels only fit at size |
Most of what wants the room needs no escape. A code block, a folded cell, a numbered listing, a cell’s output, a data table and a Mermaid diagram all take the wider band by themselves, because the reading measure fits 55 columns of code and PEP 8 asks for 79. The band is a ceiling rather than a size: a box is as wide as its content needs, never narrower than the prose and never wider than 100 columns. It centres on the prose unless a margin note has claimed the right side, in which case it right-aligns to the prose instead.
Neither escape is a new block type: .column-margin and .column-page are plain
classes, so they work on a table, a figure, a listing, or a bare div, and the block keeps
its identity (a @fig- cross-reference still resolves through one).
5.8 Reusing a partial
Splices another file’s contents into this one before parsing, as if you had pasted it in place. The directive must be alone on its line, and it is left literal inside a fence or an inline code span (which is why the examples on this page do not fire). The path is resolved relative to the file that holds the directive.
Includes are how you keep one author bio, one license footer, or one shared figure in a single file and pull it into many pages. Because the splice happens before the block model is built, the included blocks are first-class: they carry their own block ids and source positions, and click-to-source still resolves into the partial’s own file and line, not the page that pulled it in. The dev server also watches every transitively included file, so editing a partial re-renders every page that uses it.
A partial is just a .tmd file, but you usually do not want it discovered as its
own page in a site build. A file (or folder) whose name starts with an underscore
is skipped by the page walker, and the idiomatic home for shared partials is an
_includes/ directory near the project root. A nested post then reaches its shared
partial with a relative climb, exactly as the corpus does:
An include that cannot be expanded is left visible and reported, never dropped silently: an unsafe path (absolute, or one that climbs above the project root), an include cycle, and a missing or unreadable file each draw their own located warning instead of failing the build outright.
5.9 Raw HTML passthrough
A {=html} block is emitted verbatim, so you can drop in markup the Markdown
grammar doesn’t cover (custom widgets, embeds, audio players):
The palette is exposed as CSS custom properties you can use the same way:
--tali-fg, --tali-bg, --tali-muted, --tali-border, --tali-code-bg and
--tali-accent.