Images and Media

Braised has no image pipeline. Images are files you place in static/, referenced from Markdown with standard image syntax. There is no resizing, no format conversion, and no content-hashing — the file you commit is the file that is served.

This page covers the authoring side. For the mechanics of the copy paths themselves, see Static assets.

The short version

Put the file in static/:

static/
  img/
    arch.png

Reference it with the braised:static/ namespace:

![Build pipeline architecture](braised:static/img/arch.png)

The static/ directory is copied to the output root, so static/img/arch.png is served at /img/arch.png. Braised checks the file exists — a typo is a build error, not a broken image in production — and applies base_url if the site is deployed under a sub-path.

Ways to reference an image

Form Example Output If the file is missing
Namespace braised:static/img/arch.png /img/arch.png Error
Root-absolute path /img/arch.png /img/arch.png Warning
Relative path arch.png arch.png Warning
External URL https://cdn.example.com/arch.png unchanged not checked

With base_url: /docs, the first two forms output /docs/img/arch.png. Relative paths and external URLs are never changed.

Use braised:static/. It follows the same contract as braised:ref/: an explicit reference is a promise the target exists, so a broken one fails the build. A bare path might be served by something other than Braised — a CDN rewrite, a reverse proxy, a post-build step — so a broken one is only a warning, the same as a broken page link.

braised:static/ covers files from both the site's static/ and the active theme's static/, exactly as they are merged in the output. The path must name a file and be clean — braised:static/../x.png and braised:static/img//a.png are errors.

External URLs are how you point at images hosted elsewhere — S3, a CDN, an image service. Braised passes them through untouched and does not check them.

Why not relative paths

A page's URL is not where its source file lives. A page at docs/content/images.md is served from /content/images/, so a relative image path resolves against that directory:

![diagram](arch.png)        ← resolves to /content/images/arch.png
![diagram](../img/arch.png) ← resolves to /content/img/arch.png

Neither exists unless you deliberately mirror your page URL structure inside static/, and moving the page silently breaks every image on it. Braised warns when a relative image does not resolve to a static file.

Images co-located with your Markdown are not copied to the output. The content walk only picks up .md files, so dropping arch.png next to page.md in docs/ does nothing. The file must live under static/.

Linking to files

braised:static/ works in links too, for downloads and other non-page files:

[Download the PDF](braised:static/files/guide.pdf)

braised:ref/ does the reverse job — it points at pages. Using braised:ref/ as an image source is a build error.

Alt text and titles

The alt text is the first argument; an optional quoted title becomes the title attribute, which browsers surface as a tooltip:

![Build pipeline architecture](braised:static/img/arch.png "Figure 1: the seven build stages")
<img src="/img/arch.png" alt="Build pipeline architecture" title="Figure 1: the seven build stages" />

Write real alt text. It is the only accessible description of the image, and it is what the RAG chunker sees — an image whose alt text is diagram contributes nothing to retrieval.

Linked images

Wrap the image in a link to make it clickable. The link destination is rewritten normally, so .md paths and braised:ref/ both work:

[![Build pipeline architecture](braised:static/img/arch.png)](braised:ref/architecture)

Captions and sizing

Markdown image syntax produces a bare <img> — there is no caption, width, or alignment syntax. For any of those, use the image block:

:::{.image src="braised:static/img/arch.png" alt="Build pipeline architecture" width="600px"}
Figure 1: the seven build stages. See [Architecture](braised:ref/architecture).
:::
<figure class="image" data-align="center" style="--image-width: 600px;">
  <img src="/img/arch.png" alt="Build pipeline architecture" loading="lazy" decoding="async">
  <figcaption>Figure 1: the seven build stages. See <a href="/developer/architecture/">Architecture</a>.</figcaption>
</figure>

src resolves exactly like a Markdown image: braised:static/ is checked and base_url is applied. alt is required — omitting it is a build error, and alt="" marks a decorative image. Width and height take CSS lengths with units, and the image never overflows the content column.

Raw HTML is still possible with a rawhtml block, for markup the image block does not cover (a <picture> element with multiple sources, say):

:::{.rawhtml}
<picture>
  <source srcset="/img/arch.avif" type="image/avif">
  <img src="/img/arch.png" alt="Build pipeline architecture">
</picture>
:::

HTML inside rawhtml is passed through verbatim. Braised does not resolve braised:static/, apply base_url, or check that the file exists for src attributes written there. Under a base_url deployment you must write the full prefixed path yourself.

Writing an <img> tag directly in a page, outside rawhtml, does not work: Braised runs Goldmark without the unsafe renderer option, so raw HTML is dropped and replaced with <!-- raw HTML omitted -->.

To frame an image as a visually prominent element, the spotlight block expands it beyond the normal content column:

:::{.spotlight}
:::{.image src="braised:static/img/arch.png" alt="Build pipeline architecture"}
:::
:::{/spotlight}

Styling

The built-in theme keeps every image within its container (max-width: 100%; height: auto), at zero specificity so any rule of your own wins. The image block's spacing and caption color are set through --image-gap and --image-caption; see CSS and Assets for where site CSS lives.

The dev server

braised serve watches static/. Adding, changing, or removing a file there triggers a full rebuild, so a new image is served — and stops being reported as missing — as soon as you save it.

The dev server ignores base_url and always serves the site at localhost/, so images resolve locally without the deployment prefix.