Docs for v0.0.17.v0.0.18 is the current release.

Web app manifest

A manifest.webmanifest is what a browser reads when a visitor installs the site to a home screen: what to call it, what to launch, what to paint before the first page renders. The block’s presence writes it, and manifest #false stops it.

site "Baudelaire"

generate {
  manifest {
    short "Baudelaire"
    description "A Typst-native static site generator"
    display "standalone"
    theme "#101014"
    background "#ffffff"
    icons {
      "/icons/app-192.png" size=192
      "/icons/app-512.png" size=512
      "/icons/app-512-maskable.png" size=512 purpose="maskable"
    }
  }
}

Every page then carries <link rel="manifest">, plus a theme-color meta tag when you set theme. Without the link a browser never looks for the file.

Keys

Key Type Default Does
name string the site title The installed app’s name.
short string What a launcher shows when the full name doesn’t fit.
description string One line, shown by an install prompt.
display standalone | fullscreen | minimal | browser standalone How the app is presented. minimal writes minimal-ui.
theme string CSS colour of the browser UI, and of every page’s theme-color.
background string CSS colour painted before the first render.
start string the language’s root Where launching the app lands. Localized per language.
scope string the language’s root The URLs the app covers. Localized per language.
icons block One line per icon, named by the path it is served from.

What the build already knows it fills in: the name from site, start and scope from where that language’s site begins, an icon’s media type from its extension. An authored start "/home/" is localized like the default it replaces, so the French app launches into /fr/home/ and stays inside the French scope. Under a base path (url "https://example.com/docs") every URL it writes carries the prefix, because a browser resolves none of them against the page the manifest was linked from.

Icons

icons {
  "/icons/app-192.png" size=192
  "/icons/app.svg"
  "/icons/badge.png" size=96 purpose="monochrome"
}

The node name is the path, exactly as the browser will request it: put the files in static/ and they’re served verbatim. size is the square edge in pixels, and leaving it out means the image scales to any size, which is the honest answer for an SVG.

purpose is any (the default, and then left unwritten), maskable for an image safe to crop to the platform’s icon shape, or monochrome for a single-colour glyph the platform recolours.

WARN

A manifest with no icons is written and linked, and nothing will ever offer to install it. The build says so (baudelaire::manifest::icons). Ship at least a 192 and a 512.

Languages

One manifest per language, beside that language’s feeds and search index: /manifest.webmanifest, /fr/manifest.webmanifest. Each is named in its own language (site under languages { fr { .. } }), declares its own lang and dir, and starts and scopes at its own root, so installing from /fr/ gives a French app that stays in the French site. Every page links the manifest of the language it is written in.

What it does not do

The manifest makes a site installable. It does not make it work offline: that needs a service worker, which baudelaire does not generate. Write one into static/ and register it from your own script if you want one.