Statac

Write

Writing

Every page is a Markdown file in content/, ending .md.

Write a page

---
title: Post 1
date: 2026-10-04
---
The first paragraph.

The lines between the two --- lines are the page’s front matter. Below them is the writing.

Front matter is optional. Without title:, the title is the opening # heading.

A file ending .html that opens with front matter is a page written in HTML. Other files in content/ go with the pages: see Addresses.

What Markdown can I use?

Extra How it is written
Tables A row to a line, cells parted by |, a line of dashes under the first
Strikethrough ~~words~~
Task lists - [x] done and - [ ] not done
Definition lists A term, then : what it means on the line below
Footnotes [^1]
Addresses written bare www.example.org
HTML As HTML, published as written, apart from the calls and @url in it

Statac reads CommonMark, with the extras above.

Read the [first post](/posts/post-1/), or [the second][second].

[second]: /posts/post-2/

A link is written [words](address), or [words][label] with the address on a line of its own anywhere on the page. Every link is checked.

An address read from where the page is published, such as photo-1.jpg, ../post-2/ or #notes, leads to the same place wherever the writing is shown: in a list’s excerpt or a feed it is written out from the top of the domain.

A link names an address, never a file in content/. A page is a folder at its address, so from content/essays/essay-1.md, published at /essays/essay-1/, the page beside it is ../essay-2/, not essay-2.md.

[About](@url(/about/))
[About][about]

[about]: @url(/about/)

<a href="@url(/about/)">About</a>

A link written from / starts at the top of the domain. @url(...) leads inside the site, wherever it is published.

Written Its / is the top of On https://example.org/blog/, it leads to
[About](/about/), or href="/about/" in the page’s HTML the domain /about/, outside the site
[About](@url(/about/)) the site /blog/about/
{{ url("/about/") }} in a template the site /blog/about/
![The logo](/img/logo.svg), a picture found in content/ or static/ the site /blog/img/logo.svg
/about/ in permalink, redirect_from or redirect_to the site /blog/about/

The top of the site is its url, path and all. On a site at the top of its domain, the two are one.

@url(...) takes the address a page or file is published at, from the top of the site. On https://example.org/blog/:

Written Gives
@url(/about/) /blog/about/, as a link’s address, or as text where it stands alone
@url(/about/#team) /blog/about/#team: what follows a ? or a # is kept
@url("/about/") /blog/about/: quotes change nothing
\@url(/about/), or @url in code Text, as typed, without the backslash

Link to a heading

See [how to get there](#getting-there).

## Getting there

Every heading gets an id from its words as typed. Of two headings with the same words, the second’s id ends -1, a third’s -2, and so on.

To choose the id, end the heading with it in braces, and classes if you like:

## Version 1.0 notes {#version-1.0-notes}
## Getting back {#route .wide}

A chosen id is kept as written, and made ids step aside for it. Other braces, as in ## Sets {a, b}, are words.

Addresses written bare

Read more at www.example.org/a, or write to name@example.org.

An address with no link around it becomes one: www.example.org/a leads to http://www.example.org/a, and a mail address to itself after mailto:.

The rule
What is linked An address starting www., http://, https:// or ftp://, and a mail address. After http://, https:// or ftp:// any name will do, as http://localhost:8080.
Where it starts www. at the start of a line, or after a space, *, _, ~ or (. http://, https:// and ftp:// after anything but a letter. A mail address after anything.
Where it ends At the next space or <. Punctuation at its end stays in the sentence.
What it leaves out A closing bracket or quote with no opening one inside the address: (see www.example.org/a). links www.example.org/a.
Where nothing is linked In code, in maths, in a link’s words, in a snippet call, in HTML written as a block of its own, and inside <a>. Between a [ and the next ], only a mail address is linked.
To keep an address as text Write it as code, or put a backslash before one of its full stops: www\.example.org.

Pictures

![A gradient from blue to orange](photo-1.jpg)

The words in the brackets describe the picture for a reader who can’t see it. Keep the file beside the page and name it from there. More: Pictures, and Videos and files.

Callouts

> [!WARNING]
> Back up the site before moving it.

A quotation that opens with one of five markers is a callout. Without a snippet for callouts, Statac builds each as an <aside> holding the writing after the marker:

Marker Built as title in English
[!NOTE] <aside class="callout note"> Note
[!TIP] <aside class="callout tip"> Tip
[!IMPORTANT] <aside class="callout important"> Important
[!WARNING] <aside class="callout warning"> Warning
[!CAUTION] <aside class="callout caution"> Caution

To build callouts your own way, write snippets/markdown/callout.html. It is handed kind, in small letters; title, the kind’s name in the page’s language; and body, the writing inside:

<div class="callout {{ kind }}" role="note">
  <p class="callout-title">{{ title }}</p>
  {{ body }}
</div>

Statac has the names in English alone. Any other language takes them from the callouts line of the site’s language file.

Code blocks

```rust An example
fn main() {}
```

The first word after the opening fence names the language the code is coloured as. The rest is the caption, shown above the code.

After the fence Coloured as Caption
rust rust none
rust An example rust An example
rust "An example" rust An example
"An example" nothing An example
example.rs rs example.rs
src/example.rs Part 1 rs src/example.rs Part 1
Cargo.toml toml Cargo.toml
Makefile or Dockerfile the language it names none
notes-1.txt nothing notes-1.txt
text, txt, plain, plaintext, or no word nothing none
rust { rust none
GNUmakefile, CMakeLists.txt, Gemfile, Rakefile, Cargo.lock, .bashrc or .zshrc its language, known by its whole name the name

A first word written as a file’s name, in letters, digits, ., _, - and /, is the caption, and what follows its last dot names the language.

Language names

Statac colours over two hundred languages, by name or short form, in any capitals. The common ones:

Language Names after the fence
Rust rust, rs
Python python, py
JavaScript javascript, js
Shell shell, bash, sh, zsh
A terminal session, with its prompts console
C and C++ c, c++, cpp
Go go, golang
Diff diff, udiff
Make make, makefile
Assembly nasm, asm
Common Lisp cl, common-lisp
HTML, CSS, JSON, YAML, TOML and SQL html, css, json, yaml, toml, sql

What a block is built as

The language’s word stays as a class on the block’s <code>, such as language-rust, coloured or not. The colours are classes from the code stylesheet. Spaces and line breaks are published as written.

Statac writes the block’s HTML unless the site has a snippets/markdown/code.html: pieces of the writing shows both. For a file beside the writing shown as code, see Videos and files.

Footnotes

The figures are from 2026.[^1]

[^1]: The yearly report.

A marker, [^1], points to a note, [^1]: on a line of its own anywhere on the page. Notes are numbered in the order they are first pointed to, whatever their labels, and shown at the foot of the page with a link back: ↩, or ↪ on a page written right to left.

The number in the text is in the page language’s digits. The notes are an ordered list, whose digits the stylesheet chooses.

A screen reader says “back to the text” for the link back, in English. Any other language takes the words from the back_to_the_text line of the site’s language file.

Maths

The area of a circle is $\pi r^2$.

$$
\int_0^1 x^2 \, dx = \frac{1}{3}
$$

Maths between dollar signs is built into the page as MathML, which browsers show without a script. Two dollar signs either side set the maths on a line of its own.

An opening dollar sign has no space after it, and a closing one has no space before it and no figure after it, so $30 and $60 stays two prices. To show a dollar sign that would open maths, write \$.

The notation is LaTeX’s:

What Such as
Fractions, roots, sub- and superscripts, primes \frac{1}{3}, \sqrt{2}, x_1^2, f'
Greek letters, sums, integrals and limits \pi, \sum, \int_0^1, \lim
Accents, words and other alphabets \hat{x}, \text{...}, \mathbb, \operatorname, \color
Matrices and the environments of amsmath matrix, cases, aligned
Commands of your own \newcommand, inside one expression
Numbered equations \begin{equation}, counted on through the page; \eqref{...} links to one by its \label{...}

Maths works the same in headings, tables, footnotes, callouts and excerpts. A heading’s id and the table of contents take it without its dollar signs: ## The $O(n)$ bound is listed as “The O(n) bound”.

Statac adds only the <math> elements. page.maths is true where the writing holds maths, so a template can load a maths font on those pages alone: see a stylesheet for maths.

Snippet calls

@figure("photo-1.jpg")

Read @book('Post 1') before the rest.

@aside(kind='tip')
Writing read as *Markdown*, lists and all.
@end

@name(...) places the snippet snippets/name.html there, on a line of its own or inside a sentence.

In the call The snippet is handed
'Post 1' or "Post 1" Text
2026, true or false, without quotes A number, or true or false
Any other value without quotes Text
kind='tip' tip, named kind

A snippet that shows body wraps writing: called on a line of its own, it takes the lines below it, up to a line holding only @end, as Markdown. Snippets has what a snippet takes.

Calls inside HTML

<div class="aside">@book('Post 1')</div>

Calls work inside HTML written in a page, but not inside <style>, <script>, <pre>, <code> or an HTML comment.

Show a call as text

Type \@book('Post 1') where the book should go.

A backslash before the @ shows the call as text. In code, a call is text already.

Writing is never read as a template: another tool’s call or mark in braces, such as {{< figure >}}, is published as words.

Where a post’s excerpt ends

The part a list shows.

<!--more-->

The rest, shown only on the post's own page.

A list can show the start of each post, as post.excerpt in its template: the writing before <!--more-->, written on a line of its own between two paragraphs. <!-- more --> is fine too. Another tool’s marker, such as <!--break-->, is an ordinary comment.

Without the line, the excerpt is the first paragraph that holds words, passing over a picture standing alone and the writing a call wraps. On a page written in HTML, it is the first <p>. Footnote markers are left out.

Quotes, dashes and ellipses

You type The page shows
"words" The language’s outer quotation marks: “words” in English
'words' Its inner marks: ‘words’ in English
it's An apostrophe, ’, in every language
-- An en dash
--- An em dash
... An ellipsis, …
Language Its outer marks
English “ and ”
German „ and “
Japanese 「 and 」
French « and ». A language file can ask for a space inside each, and before ;, :, ! or ?.
One with no marks of its own None: quotes stay straight

Languages says how to write a language’s marks. A double quote that closes nothing, such as the inch mark in a 13" screen, stays straight.

Words handed to a snippet call, such as a caption, are set as the writing is. What stays as typed:

Left as typed Such as
Code and maths `a--b`
Anything inside <code>, <pre>, <kbd> or <samp> <kbd>--help</kbd>
HTML written in a page <p class="note">"words"</p>
Addresses, in a link or written bare https://example.org/a--b
A value in a snippet call that names something: one word, an address, a path starting /, or a file’s name @figure("photo 1.jpg")
A character with a backslash before it, or written as HTML writes it \", \-, &quot;
All punctuation, on a site with typography: false in statac.yaml

Where next?

  • Front matter: the title, date, tags and other fields a page can set
  • Pictures: where pictures go, describing them, and a photograph’s copies
  • Snippets: writing a snippet, and what a call hands it
  • Addresses: where each page is published, and a site under a path

Something unclear or wrong? Open an issue on GitHub.