Docs · Content & SEO

OG Images

How social share cards are generated with bin/rails og:images, and how to add your own.

The social share card that shows up when someone pastes a link into Slack, X, or iMessage is a generated PNG, not a hand-drawn image. bin/rails og:images builds one per coding-agent landing page, and bin/rails og:images:docs builds one per docs category and page, including the one this guide is talking about right now.

How it works

Each task renders an ERB template to a local HTML file, using the site's own design tokens copied inline (Propshaft-bundled CSS isn't sized for a 1200x630 card, so the template carries its own copy of the relevant :root variables), then screenshots that file with headless Chrome at 2x device-scale-factor, landing on a 2400x1260 PNG. A browser renders the card rather than an SVG rasterizer, because the card is the Terminal design system at a larger size, and the only renderer that draws oklch() colors and loads IBM Plex the way the live site does is the engine the live site runs in.

This needs Chrome and network access locally (Selenium Manager fetches a matching driver the first time, and the card pulls IBM Plex from Google Fonts, the same stylesheet the layout itself loads). Both are build-time only: the generated PNGs are committed to the repository, and nothing at runtime or in CI depends on Selenium being available.

Generating cards

bin/rails og:images                    # every coding-agent page
bin/rails 'og:images[claude]'          # just one, while tuning the layout

bin/rails og:images:docs               # every docs category and page
bin/rails 'og:images:docs[mobile-apps]' # just one category

Docs cards land in public/og/docs/, named <category>--<slug>.png for an article and <category>.png for a category's own index page. Both tasks share the same underlying renderer, so adding a new docs page and running og:images:docs is the entire workflow; there's no design tool involved.

Why a missing card fails loudly

If a page's card hasn't been generated, the site doesn't show a broken image; ApplicationHelper falls back to the site-wide default card (/og.png) automatically, so nothing looks obviously wrong in a browser. That silent fallback is exactly why a spec exists that checks every real page and category has its own generated file, and fails the build if one is missing. See spec/requests/docs_og_cards_spec.rb. Run the relevant og:images task, commit the new PNGs, and the spec passes again.

Adding your own custom card

If a specific page wants a hand-supplied image instead of a generated one, blog posts support this today through the image front-matter field, which overrides the generated fallback entirely. Docs pages follow the same image field for the rare page that needs it, though most docs pages are better served by the generated card, since it stays visually consistent with every other page in the category automatically.

Next

You've now covered the whole content and SEO layer. See how the mobile apps reuse these same web views: Mobile Overview.