og-images
Skill9 starsChanged 30 days agoMITView on GitHub
Generate branded per-page Open Graph (social share) cards for a website: a 1200×630 JPEG ≤300 KB for every page, rendered by a committed Python+Pillow script and wired so each page sets its own `image=` (with a default fallback) and the test suite proves the card exists at the right size. Use when building or auditing a site's share previews — "OG image", "og:image", "social share image", "share card", "WhatsApp/Slack/LinkedIn preview", "Twitter card image", "the picture when I share the link", "per-page OG images", "generate share images". For general marketing image creation/optimization use the `image` skill; this one is specifically the build-time, tested, per-page card generator for the suite's Astro starter.
- Installs packages
5 other projects use a version of this.
Adopted by
Most recently taken up by
- Anglesite/anglesite-skillstheir own version, first seen Oct 27, 2026
- Zmoki/zmoki-astro-startertheir own version, first seen Oct 27, 2026
- phucbm/skillstheir own version, first seen Oct 13, 2026
- gwendall/skillstheir own version, first seen Oct 13, 2026
- karero/croftweaverword for word, first seen Oct 13, 2026
Other versions5 projects use a version of this.
Word for word · 1
- karero/croftweaver★ 9100% same
Edited · 0
None found.
Wrote their own version · 4
- Anglesite/anglesite-skills★ 15same name
- Zmoki/zmoki-astro-starter★ 4same name
- phucbm/skills★ 2same name
- gwendall/skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository41 other files it gives its agents.
- skills/new-website/templates/AGENTS.mdAGENTS.md
- skills/new-website/templates/CLAUDE.mdCLAUDE.md
- ai-seoSkill
- astro-i18n-setupSkill
- business-listings-setupSkill
- copywritingSkill
- customer-researchSkill
- double-knuthSkill
- facts-checkSkill
- imageSkill
- independent-reviewSkill
- internal-link-auditSkill
- keystatic-setupSkill
- new-websiteSkill
- outgoing-link-auditSkill
- schema-markupSkill
- search-console-insightsSkill
- search-console-setupSkill
- seo-auditSkill
- seo-repositionSkill
- site-architectureSkill
- website-brand-kitSkill
- website-contact-formSkill
- website-content-guideSkill
- website-design-systemSkill
- website-hero-carouselSkill
- website-launch-checkSkill
- website-motionSkill
- website-new-pageSkill
- website-permissionsSkill
- website-positioning-checkSkill
- website-positioningSkill
- website-qaSkill
- website-reviewSkill
- website-scorecardSkill
- website-seo-geoSkill
- website-storySkill
- website-taglineSkill
- website-team-setupSkill
- website-testimonialsSkill
- wordpress-inspectorSkill
The file
We split this skill into the rules it gives an agent
2 rules. 0 are common, 0 are distinctive, and 2 are very specific to this project.
0
Common
Most of the 4 edited versions we read keep these.
0
Distinctive
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 19
When a URL is shared (WhatsApp, Slack, iMessage, X, LinkedIn, Discord), the og:image is the preview.
- Very specific: line 83
If you hit one: shorten the text to a near-synonym, or insert a plain space at a natural compound boundary (Suchmaschinen Optimierung) — …
“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
OG images — per-page share cards
When a URL is shared (WhatsApp, Slack, iMessage, X, LinkedIn, Discord), the og:image
is the preview. Get it wrong and the link shares with no picture or a badly cropped
one — a silent conversion leak. This skill makes every page render a consistent,
on-brand 1200×630 JPEG ≤300 KB, generated by a committed script and enforced by the
test gate.
The three hard rules (messengers enforce them)
- 1200×630, exactly (1.91:1). Square images get cropped on X/Teams; off-ratio ones
letterbox. The starter's
Base.astroalready emitsog:image:width/height= 1200×630. - ≤300 KB. WhatsApp silently drops previews heavier than ~300 KB — the link then shares with no image at all. Encode JPEG and compress to fit.
- Extension matches the bytes. A PNG renamed
.jpgtrips strict scrapers. Use JPEG for photographic cards, PNG only for flat logo/gradient cards that still come ≤300 KB.
How it's wired in the Astro starter
scripts/generate_og_cards.py— the generator. Renders one card per page you list, plus adefault.jpg. Run it withnpm run og. Idempotent; commit the output.src/layouts/Base.astro—imageprop defaults to/images/og/default.jpg. A page gets its own card by passingimage="/images/og/<slug>.jpg"; a page with noimage=falls back to the default — which only exempt utility pages should do (the test below enforces it).og:image+twitter:imageare derived from that one prop.tests/seo.spec.ts— for every page, reads the referenced card off disk and asserts it exists inpublic/, is ≤300 KB, a real JPEG/PNG, and exactly 1200×630. It also enforces that every page has its own card (not the generic default) and that no two pages share one — except anOWN_CARD_EXEMPTallowlist (home + utility pages, default['/', '/privacy']). Opt-out: as you add content pages, leave them OUT of the allowlist and the guard flags any that forgot a card. A missing/oversized/shared card fails the build — OG cards can't silently rot, and a new page can't silently ship on the generic default.
The starter ships a generated public/images/og/default.jpg so the suite is green from
commit 1; you replace it with your brand by editing the script and re-running npm run og.
Setup for a site (two edits, one command)
- Fill the
BRANDblock inscripts/generate_og_cards.py—BRAND_NAME,SITE_URL_LABEL, the background/accent/text colours (copy the hexes fromsrc/styles/global.css/BRAND.md), and optionally pointLOGOat a transparent raster PNG for a faint emblem watermark (SVG won't load — Pillow needs raster). The card is a dark share card by design: light text on a deep background reads well in every chat app regardless of whether the site theme is light or dark. - List per-page cards in the
PAGESblock — one(slug, title, [subtitle, …])per content page. Then setimage="/images/og/<slug>.jpg"on that page's<Base>. Only exempt utility pages (home, privacy, …) may fall back todefault.jpg; every other content page must be listed here and wired to its own card, orseo.spec.tsfails it. - Run
npm run og, thennpm test. Commit the generated JPEGs.
npm run og -- --check exits non-zero if any card exceeds 300 KB — wire it into CI or the
pre-push hook if you want generation (not just validation) gated.
Long or unbreakable words (German compounds)
fit_title() shrinks the font until every wrapped line actually fits the card's text
area, not just until the line count looks right. Three overflow cases fail the run
loudly (OG card OVERFLOW: ...) instead of drawing past the edge, all unconditional
(not gated behind --check, which only guards file size):
- a single unbroken word wider than the text area at the smallest size (52pt) — long
German compounds (
Suchmaschinenoptimierung,Datenschutz-Grundverordnung) are the classic trigger; - a title still needing more than 3 lines at 52pt (it would collide with the subtitles/footer);
- a subtitle wider than the text area (subtitles render at a fixed 30pt).
If you hit one: shorten the text to a near-synonym, or insert a plain space at a
natural compound boundary (Suchmaschinen Optimierung) — the generator deliberately
does not hyphenate compounds automatically.
Prerequisite
Pillow, a local authoring dependency only (not shipped to the site or its runtime):
pip3 install PillowOn Windows, run authoring/build inside WSL2 — the suite's recommended Windows
environment (SETUP.md), where python3 and the bash helpers all work. Running natively
is fine too: py -m pip install Pillow, and npm run og already falls back python3 → py
-3 → python.
Going further — photo-driven cards
The generator deliberately ships the text-card core (emblem + wordmark + accent bar +
title + subtitles + footer URL) because it works for any site with zero per-page art. When
a content type has a natural image — a speaker headshot, a product shot, an author avatar —
extend build_page() to read that image from frontmatter and composite a circular portrait
- glow beside the text: iterate a content collection (
src/content/<type>/*.md), render one portrait card per entry alongside the static-page cards, and keep the same encode-under-300 KB step. The template ships the text-card core; the portrait variant is a small, additive change.
Don't
- Don't hand-export cards in Figma/Canva per page. They drift and balloon past 300 KB. The script is the single source of truth; re-running it is free.
- Don't ship a square logo as
og:image. It crops badly. Always 1200×630. - Don't point
og:imageat a remote/CDN URL the repo can't verify. Keep cards on-site inpublic/soseo.spec.tscan prove they exist and fit.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

