Templates
A template is a layout. Frontmatter picks one by name, so different posts on the same site can produce different kinds of image from the same config.
| Name | Layout |
|---|---|
banner |
Left-aligned title with optional version, subtitle, corner badge and footer. |
card |
Minimal centred title with an optional subtitle. |
code |
Syntax-highlighted snippet on a rounded panel over the background. |
The code template has enough of its own behaviour to need
a page of its own.
Choosing a template
Section titled “Choosing a template”The template field inside the props block names it:
---meta_img_props: template: card title: About---A post that names no template gets content.defaultTemplate, which is unset by
default. Both the field name and the default are configurable under content;
see Frontmatter.
Naming a template that is not registered fails the build and lists the ones that are, because a name typed into frontmatter is easy to get wrong:
Unknown template "bannner". Available templates: banner, card, code.Writing your own
Section titled “Writing your own”Pass templates in config. The keys are the names frontmatter uses, and what
you supply is merged over the built-ins, so a key of banner replaces the
built-in banner.
A template’s render receives the props, the resolved config and the pixel
dimensions of the image being drawn. It returns the SVG foreground content.
The background and the enclosing <svg> root are added by the renderer, so a
template does not draw them.
import { defineConfig, escapeXml, type Template } from "@kensio/colophon";
const stripe: Template = { name: "stripe", render({ props, config, dimensions }) { const { width, height } = dimensions;
return ` <rect x="0" y="${height - 32}" width="${width}" height="32" fill="${config.colors.brandWarm}" /> <text x="80" y="${height / 2}" font-family="${config.fontFamily}" font-size="${width * 0.08}" font-weight="700" fill="${config.colors.foreground}">${escapeXml(props.title ?? "")}</text> `; },};
export default defineConfig({ colors: { brand: "#2563eb" }, templates: { stripe },});Some notes on writing one:
- Size everything from
dimensions. The same template is asked to draw a 1200x1200 square and a 1200x630 landscape. Fractions of the width travel better than fixed pixel values, since that is what a feed scales the image to. - Escape anything that came from frontmatter.
escapeXmlis exported for this. A title containing&produces invalid SVG otherwise. propsis open. Colophon does not check what a template reads, so you can put whatever fields your layout needs into the props block.rendermay return a promise if it has to load something. Keep it synchronous when it does not need to. The built-incodetemplate is async because it loads syntax grammars on demand.
config here is the resolved config, with defaults applied and per-size
overrides folded in, so config.colors.brandWarm is always a colour and
config.footer is either a string or undefined.
