The home page, /archive and /about on usingitup.kynth.studio were serving no og:image tag at all, and /og.jpg returned a 404. Every paste of one of those three links into iMessage, Slack, LinkedIn or Discord unfurled as a bare grey box. The audit finding had fired twelve times across five days before anyone sat down with it.
Entry pages were never affected. generateMetadata in src/app/entry/[slug]/page.tsx hands openGraph.images the entry's own wide frame, so an entry link unfurled correctly the whole time. It was the three routes at the top of the site, the ones a reader actually shares when they want to show you the publication rather than one piece of it, that had nothing.
Where the wiring had to go
The obvious fix is openGraph.images in src/app/layout.tsx. That file is shared with the other publications on the same footing and this site does not own it, so editing it here would break their deploys the moment their copies stopped matching. A share card also cannot sensibly be a shared default, because the picture is the one part of it that is irreducibly this publication's.
So the card is not wired in TypeScript at all. ops/og-card.mjs writes src/app/opengraph-image.jpg and src/app/twitter-image.jpg, which are Next file conventions. Next emits og:image with its type, width, height and alt, plus twitter:image and twitter:card=summary_large_image, for every route that does not declare its own. The picture is the wiring. Nothing in the shared file moved.
Both files come from one screenshot buffer, not two renders:
const OUT = [
path.join(SITE, "src/app/opengraph-image.jpg"),
path.join(SITE, "src/app/twitter-image.jpg"),
];Two renders of the same card is two things that can drift, and the failure would be silent. A stale twitter-image.jpg is a correct-looking card one version behind, which nothing downstream would ever flag.
The arithmetic that decided the layout
Using It Up is a publication of first-person entries about finishing what you already own, and its object photography is a library of 55 licensed stock frames, every one of them 900x1125. That is 4:5 portrait, because the frames double as stills for vertical clips.
A share card is 1200x630. Crop a 900x1125 frame to that ratio and you keep a 900x472 band, throw away 58% of the height, and then upscale what is left by 1.33x. Soft, and for the frame I picked it cuts the jar in half.
Inset at 430x526 the same file is a downscale and stays sharp. So the photograph sits flush to the right edge and to the top of the accent strip, at the library's own 4:5, with the headline and dateline in the paper field to its left:
.frame { position: absolute; right: 0; top: 0; width: 430px; height: 526px;
background: url(...) center/cover no-repeat;
border-left: 1px solid #ddd8ca; border-bottom: 1px solid #ddd8ca }The picture choice was made against a contact sheet of all 55 rather than by filename. Most of the library is either high-key and pale or shot macro, and both die at roughly the 300px an unfurl actually renders at. Pale goes to a white rectangle, macro goes to texture. The one that survives at that size is a single unmistakable object at high contrast, a hand refilling a clip-top jar of dried hibiscus at a bulk counter.
What it cost
The strip at the foot is not the accent the site uses. The brand spec settles the on-frame treatment as a full-width bar in the accent carrying dark ink, and dark ink on the paper accent #8A4F32 measures 2.6:1. On the lighter video accent #D08A5E it measures 5.9:1, so the card wears the video accent and is fractionally off the palette of the page it links to. Readable beat consistent.
The inset costs more than that. The photograph occupies 430 of 1200 pixels of width, so most of the card is empty paper. A full-bleed crop would look richer in a feed and be visibly soft, and the geometry is not negotiable once the library is 4:5.
The headline, ink and italic em were measured on the live page rather than read out of the comment in src/copy.ts, which describes an accent the page does not render. The card sets the wordmark from the same woff2 the site serves and reads the mark geometry out of src/components/Mark.tsx, so neither can drift from what the page shows.
If you are building a share card out of pictures you already own, do the crop arithmetic before you design the layout. The source library's aspect ratio decides whether the picture can be full-bleed, and if 1200x630 does not fit inside it, insetting is the only version that stays sharp.
Using It Up is live at usingitup.kynth.studio.
One shipped product, taken apart, once a month. What it does, what it cost to build, what the pipeline behind it looks like, and what the numbers did — read off the repository and the live site, not written from memory. Join the list.
← All writing