Track NotesProject Build

Yorkshire Motorsport Now Writes Its Own Social Posts

Published on 8 September 2026 by Mark

A while back I wrote about building Yorkshire Motorsport, the consolidated trackday and spectator-access calendar I run for the circuits in and around Yorkshire. One job it didn't do back then was talk about itself. Every week I'd open Canva, build a "what's on this weekend" graphic by hand, write the caption, and post it to Facebook and Instagram. It took twenty minutes I didn't always have, and the weeks I skipped it were the weeks the page went quiet.

So I built that into the site. Yorkshire Motorsport now generates its own social posts, caption and branded image included, from exactly the same event data that drives the calendar. The part I want to be clear about up front: nothing posts on its own. Every generated post lands in a review queue and needs me to approve it before it can go anywhere.

What It Actually Produces

Two formats, both of which I used to make by hand. A weekly weekend digest, one card listing every circuit with something on over the coming weekend, and single-event spotlights for the races and trackdays worth calling out on their own. A daily job checks for spotlight candidates, an upcoming event that's just been confirmed as open to spectators, or a headline race meeting a week out. The weekend digest runs once, on a Thursday, after the newsletter goes out so the two never disagree.

Each approved post also goes out as a 24-hour story on both platforms, a portrait crop of the same card. Stories are on by default and I can untick either one in the queue before publishing. One quirk of Meta's API worth stating: a story posted this way is image-only, so it carries no caption and no link back to the site, it's just the card. That's fine here, because the card is built to stand on its own.

A generated Yorkshire Motorsport weekend digest card: the site logo, a trackday photo, and a list of the circuits with events that weekend

The Caption Is Built From the Data, Not a Template You Can See

The easy version of this is a fill-in-the-blanks string, and it reads like one. The captions here are assembled from small pure functions instead: an opener line that rotates by week number rather than at random, so re-running the job in the same week gives the same post; series detection that picks out a BTCC or British GT round from the event title and leads with it; hashtags scoped to only the circuits and series actually on that week. The one line that describes spectator access is never paraphrased, it comes verbatim from the single function the rest of the site uses for that, so the post can't say "spectators welcome" when the event page says "check with the circuit".

The other thing that took a pass to get right was making it not sound like a computer wrote it. I sat the generated captions next to the ones I'd written by hand and the tells were obvious: em dashes everywhere, blank lines splitting the text into neat sections, dates with the year on them. Real posts don't look like that. The generator now matches the house style, and there's a test that fails if an em dash ever creeps back in.

The Branded Image, and a Bug Worth Knowing About

Each post gets a 1080 × 1080 card for the feed and a 1080 × 1920 portrait version of it for the story: a trackday photo, a dark gradient, the logo, and the event details. The spotlight card colour-codes the spectator-access line, green for confirmed, red for no, amber for unconfirmed, so the single most useful fact on it reads at a glance.

A generated Yorkshire Motorsport spotlight card for a single event, with the spectator-access status shown in green

Early versions of this card looked cheap, and it took me a while to work out why. The whole thing was being rendered in one pass with Next's image renderer, photo included. That renderer is built for turning HTML and CSS into an image, and it's excellent at text and shapes, but it resamples an embedded photo with a low-quality filter and no sharpening. So a crisp 2400-pixel-wide source came out visibly soft at 1080, while the logo and text on top of it stayed sharp. Sharp text on a mushy photo is exactly what "cheap" looks like.

The fix was to stop asking one tool to do both jobs. The text, logo and gradient layer is now rendered on a transparent background, and sharp does the photo properly: a high-quality resize to 1080 square, cropped with the same bias the site's page heroes use, then the text layer composited on top and the whole thing encoded as a high-quality JPEG. The square and the portrait frame both come out of one photo fetch and one crop, each encoded exactly once, an earlier version quietly re-compressed the same photo several times over on its way out and you could see it. JPEG specifically, because Instagram's publishing API only accepts JPEG, and left to its own devices Meta would re-compress the image at whatever quality it felt like.

Nothing Goes Out Without Me Approving It

This is the part I care most about. Generation is automatic; publishing is not. Every post is created as a pending row in an admin queue with an editable caption and the rendered image. I approve it, tweak the wording if it needs it, and then a separate "publish" button actually sends it to Facebook and Instagram through Meta's API, the feed posts and, if I've left them ticked, the stories. Each of those is its own step with its own record, so if one succeeds and another fails, hitting publish again only retries the parts that didn't land, it can't double-post the ones that already worked.

The known long-term risk with this kind of integration is a Meta access token quietly expiring. A monthly job re-exchanges it for a fresh one well inside its roughly 60-day window, so under normal conditions it never lapses. What I deliberately haven't done is let that job paper over failures: if the refresh can't complete, usually because access was revoked outright, it emails me straight away instead of retrying quietly, and a failed publish does the same. When something actually breaks I'd rather it be loud than silently handled.

Why This Is on a Web Developer's Blog

Because it's the same problem a lot of businesses have. Keeping a Facebook page or a Google Business Profile current is a small job that never stops, and it's the first thing to slip when the week gets busy. The answer isn't to hand it to something that posts unsupervised, it's to take the effort out of the routine parts so a person can spend thirty seconds approving a post instead of twenty minutes building one. Managing exactly that, for clients, is part of what Austin Tech Solutions does.

As with the rest of the project, this was built to the standard I'd apply to a client site: the caption logic is covered by an automated test suite, the publish step fails safe, and the admin queue sits behind the same authentication as everything else in the back end. There's a fuller technical write-up on the Yorkshire Motorsport case study, or you can see the posts it produces on yorkshiremotorsport.co.uk.

Austin Tech Solutions

Rather just have this handled for you?

I build and run websites for businesses that would rather not think about any of this.