TP232 min read

social cards drawn at build time

every note on this site gets its own open graph image, rendered from the same board tokens as the page, with no server and no browser in the loop.

a shared link is the first viewport for most visitors. it should look like the site.

paste a link to any note here and you get a card: soldermask green, a copper designator, the title in b612, a title block in the corner. none of them is a screenshot. all of them are drawn at build time from the same tokens the page uses.

two constraints. no serverless function generating images on request. no headless browser during the build. both work, and both are a second system to keep alive for a site that has to survive months of neglect.

the pipeline is three pieces:

  1. satori takes a tree of elements with flexbox styles and produces an svg.
  2. resvg turns the svg into a png.
  3. astro calls both. a file at src/pages/og/notes/[...slug].png.ts declares one static path per note and returns a png from its GET handler. the build writes it into dist/, and the note page points og:image at it.

a few hundred milliseconds per card. no browser anywhere.

the card always uses the dark board tokens; social cards can’t follow the visitor’s theme, and the board is the layer worth showing. designator and date on one baseline at the top. title in b612, stepping down in size at 30, 50, and 75 characters, clamped to three lines. description in atkinson hyperlegible, two lines. bottom right, the same two-cell title block as the site footer: TITLE nomadic.al, REF TP3.

satori needs raw font bytes. the site already self-hosts b612, b612 mono, and atkinson hyperlegible through @fontsource, so the build reads the woff files out of node_modules. one copy of the fonts. the card uses exactly the faces the page does.

two lessons. cards are unforgiving at 1200 by 630: the full name wrapped in the title block, and the fix was a shorter label. and satori supports a subset of css: line-clamp works, text-wrap: balance doesn’t, and anything that isn’t display: flex has to say so. read the supported list before you design the card.

an afternoon of work. every note gets its card for free.

All notes
nomadic.al