Static site generator
Write Typst. Ship a website.
Pages are .typ files and layouts are Typst functions. One binary does the rest: clean URLs, feeds, taxonomies, search, an asset pipeline, and a build cache.
curl -fsSL https://baudelaire.cstef.dev/install.sh -o install.sh
less install.sh # read it
sh install.sh
A page is a program
content/sourdough.typ
== Sourdough
#let recipe = (Flour: 300, Water: 210, Salt: 6)
#let total = recipe.values().sum()
#total g of dough, #recipe.len() ingredients.
#table(
columns: 3,
table.header([Ingredient], [Grams], [Share]),
..recipe.pairs().map(((name, g)) => (
[#name], [#g], [#calc.round(100 * g / total)%],
)).flatten(),
)
what it renders
Sourdough
516 g of dough, 3 ingredients.
| Ingredient | Grams | Share |
|---|---|---|
| Flour | 300 | 58% |
| Water | 210 | 41% |
| Salt | 6 | 1% |
content/releases.typ
== Shipped
#let rows = csv("/data/releases.csv")
#for (tag, what) in rows [
/ #raw(tag): #what
]
what it renders
Shipped
v0.0.7- Social cards and virtual Typst modules
v0.0.8- Nested directories in the permalink
v0.0.9- One theme per kind of site
v0.0.10- TypeScript assets and editor tooling
v0.0.11- A theme from anywhere
content/hello.typ
== Hello
Ordinary prose, a #link("https://typst.app")[link],
*bold*, and `code`.
+ Lists, numbered or not
+ Math, set as Typst sets it: $a^2 + b^2 = c^2$
+ Terms, tables, figures, footnotes
what it renders
Hello
Ordinary prose, a link, bold, and code.
- Lists, numbered or not
- Math, set as Typst sets it:
- Terms, tables, figures, footnotes
The right pane is not a screenshot. It is the left pane, evaluated while this page compiled, by the same Typst that built every other page here. That is the whole argument: Markdown has no variables, no functions, no math, and no way to load a CSV and build a table from it, so the moment you want one you start stacking a template engine and plugins on top. What that trade costs, next to Hugo, Zola, Eleventy and Astro, is on compared.
One block, every artifact
config.kdl
generate {sitemap #truefeed { formats "rss" "atom" }search { ui }cards { template "card.typ" }pdf #true}
public/
Turn one on and the config writes itself, along with what it puts in dist/. Each artifact is built from the pages you already wrote, none of it is on until the config says so, and each has its own page under Build.
Start here
- QuickstartScaffold, build, and serve in three commands.
- TemplatesA layout is a function taking a page and its body.
- Config referenceEvery key the build reads, in one table.
- SearchA flat index, and a palette to read it.
- TaxonomiesGroup pages by tag, with generated index pages.
- Feeds & sitemapRSS, Atom, and sitemap.xml from your dated pages.
Latest writing
These docs are a baudelaire site: 57 pages, one config file, no plugins, and the sidebar on every other page built from the same content tree the build uses for prev/next links. Its source is in the repository, under docs/.