| name | og |
| description | Share-link previews and app identity for apps on *.grok.me: the og:image card in the root head, the SVG favicon, and PWA icons for installable apps. Use when scaffolding, renaming, or restyling the app — and for share / unfurl / OG / Twitter card questions. A custom 1200×630 card from the app's own art is the default — games of every kind (DOM board/word games included), whimsical apps, creative tools, and brand-forward pages; only plain utilities keep the placeholder. By default run the whole brand-asset pass in a parallel background task (the `task` tool) while the app builds. Triggers on "share", "rename", "app name", "OG", "Open Graph", "twitter card", "unfurl", "og:image", "og:type", "link preview", "social card", "thumbnail", "preview image", "favicon", "app icon", "PWA", "manifest", "installable", "home screen", "SEO", "meta description".
|
| metadata | {"short-description":"Brand assets: og.jpg card, SVG favicon, PWA icons — parallel task by default"} |
| user-invocable | false |
Share cards, favicon, and app icons
A deployed app (https://{name}.grok.me) unfurls with a 1200×630 card, and
every app (preview included) shows a favicon in the browser tab. Both are
wired in the root head() — AGENTS.md § "First scaffold" is the source of
truth for src/routes/__root.tsx. Extend __root.tsx; never replace it
wholesale (auth SSR, redesign, another skill's excerpt). Dropping og:image
or the favicon link ships a broken card / blank tab icon, and npm run dev /
build will not catch it.
og:type for games (X game-card presentation)
When the app is a game of any kind (Canvas/WebGL and DOM board/word/
puzzle/quiz games), set this meta tag in the root head() always — it
does not need VITE_PUBLIC_HOSTNAME and must not be gated on ogImage:
{ property: "og:type", content: "x:game" },
Why this exists: X (Twitter) uses og:type="x:game" when unfurling
*.grok.me links to present the card as a game rather than a generic
website. This is a product contract with X's card pipeline — keep the tag,
and do not "correct" it to website during refactors. Non-games should omit
og:type or use website (scraper default).
Why og:type content x:game (and not the alternatives)
This will live forever in every shipped game; pick the stable channel:
| Option | Verdict |
|---|
<meta property="og:type" content="x:game"> | Chosen. Stays on the standard OG content-type property; value is a namespaced type (x:game) so it cannot be confused with a future global OGP game type or bare website. X's card pipeline keys off this exact value to present the unfurl as a game. Do not shorten to bare game. |
og:type="game" (no x:) | Rejected. Looks like a global OGP type that does not exist on ogp.me; the product contract is the namespaced x:game string. |
twitter:card | Wrong layer for the game signal. Layout only (summary_large_image). It lives in the first-scaffold __root.tsx and PWA chrome (AGENTS.md) so every app gets it — do not treat this skill as the place that adds it. Never use it as the game type. |
Separate x:type / x:card meta properties | Do not invent. Extra properties double surface area (agents forget one of two tags). Namespacing inside og:type's content (x:game) is enough. |
Example root meta slice for a game (with a custom card once published):
meta: [
{ charSet: "utf-8" },
{ name: "viewport", content: "width=device-width, initial-scale=1" },
{ title: APP_NAME },
{ property: "og:type", content: "x:game" },
...(ogImage
? [
{ property: "og:image", content: ogImage },
{ property: "og:image:width", content: "1200" },
{ property: "og:image:height", content: "630" },
]
: []),
],
browser-smoke.mjs / brand-check.mjs emit a BRAND WARNING when a
canvas app is missing og:type="x:game" — treat that as not done, same as a
missing custom card.
Parallelize the brand-asset pass (default)
The identity bundle — public/og.jpg, public/favicon.svg, PWA icons when
installable — needs only the app's name, theme, and palette, and writes only
public/ plus a few head() / manifest lines. Start it as a background
task with the task tool early in the build (AGENTS.md § "Parallel work"),
right after the name and palette are settled: give the task this skill and
sole ownership of public/ brand assets, keep building the app, and
integrate its head() edits at the end (wait_tasks before the final
verify). Image generation is pure waiting — a build that runs the card pass
inline at the end serializes minutes of model latency behind an already
finished app.
Pass the task this skill plus the 16:9 canvas contract below, so the card it
generates crops to 1200×630 without clipping the title.
A default, not a hard rule: stay sequential when the user is art-directing
the card, when the app art the card should reuse doesn't exist yet, or when
the task tool isn't available.
Which card an app gets
Default: generate a custom card. Any app with a face of its own gets one
built from its own art (below):
- Games — every kind and rendering tech. Canvas and WebGL, but equally
DOM-rendered board, card, word, puzzle, and quiz games. A tic-tac-toe grid
made of divs is still a game and still ships a custom card.
- Whimsical, playful, and toy apps — generators, virtual pets, screensavers.
- Creative tools — drawing, music, photo, design, anything whose output is
visual or expressive.
- Content- and brand-forward apps — landing pages, portfolios,
storefronts, anything presenting an identity to visitors.
When in doubt, make the custom card — a themed card is the better unfurl
for anything a user would show someone.
-
Plain utility apps only (converters, invoice/CRUD trackers, internal
dashboards, minimal notes/admin tools — apps whose face is the data, not a
theme) → keep the default og.grok.me card:
https://og.grok.me/v1/card.png?host={VITE_PUBLIC_HOSTNAME}&title={APP_NAME}
plus og:image:width / og:image:height of 1200 / 630. When the user
renames the app, update APP_NAME — it is both the document title and the
painted card title.
Either way: live preview has no VITE_PUBLIC_HOSTNAME, so emit no
og:image at all (text-only unfurl is expected). On publish the platform
injects the hostname — do not write a .env for it. Card pixels update on
the next deploy (the URL is baked into HTML at build time).
Custom card: generate public/og.jpg
For prompt-craft, composition, and blind read-back verification, follow the
imagine and game-asset-core skills — this skill owns the card-specific
contract only (size, lockup, wiring).
-
Set the canvas with aspect_ratio: "16:9". The call looks like
{ "prompt": "…", "aspect_ratio": "16:9" } — ratio words in the prompt do
not set the canvas. At 2mp, 16:9 renders 1792×1008, so the normalize
below cover-crops to 1200×630 trimming only ~3% vertically, which a
centered title survives. A narrower canvas is what kills titles: from 3:2
the same crop takes ~21% vertically, straight through the lockup.
Paths:
- Default — one call with
aspect_ratio: "16:9": imagine_image
with the art + baked title. Check the output dimensions via
imagine_view_media + Pillow; if the ratio missed, reframe or use
the API path.
- Reframe if needed: pass the prior
asset_id into imagine_image
with aspect_ratio: "16:9" and a prompt like "extend the scenery
left and right into a wider frame; keep the title lettering and
central subject exactly as they are".
- Optional — true 2:1 via the xAI Images API:
POST https://api.x.ai/v1/images/generations with "aspect_ratio": "2:1"
and response_format: "b64_json" using the injected XAI_API_KEY
(see the xai-api skill). 2mp 2:1 is 1984×992; normalize then trims
only ~2.4% per side and nothing vertical.
Build the prompt from the app's theme, palette, and characters. If the
app already has a key generated asset (hero sprite, title scene), pass
its asset_id into imagine_image so the card matches in-game art —
same 16:9 + check-the-output rule applies.
Last resort only (no imagine_image and no xAI Images API): stay
on whatever canvas you have and keep the entire title block inside the
middle half of the frame height, with the crop-clipping check in
step 6 as the gate.
-
Bake the title in like a game cover. Store-page covers (Stardew Valley,
Cuphead) lead with a short stylized logo-type title. Put the exact app name
in quotes in the prompt; 1–3 strong words; optional short tagline under the
title in smaller lettering (exact words in quotes).
- Stack multi-word titles into a two-line lockup ("SKY" over "STRIKE").
- Center the block both ways with generous margins — avoid "upper third"
/ percentage placement (models hug the edge).
If neither imagine_image nor the xAI Images API is available,
fall back to the og.grok.me card — never ship a missing or broken
og:image URL.
Regenerate the card when the app's visual identity materially changes (theme
overhaul, new hero art) — and on rename: the title is baked into the pixels,
so update APP_NAME and regenerate the card with the new name. (A titleless
fallback card survives a rename without regeneration.)
Favicon: hand-author public/favicon.svg
Every app gets one, and it works in live preview immediately (no host needed).
-
Write the SVG by hand — never imagine_image. It must stay crisp at 16px:
one bold glyph or shape, flat fills from the app's design tokens, a square
viewBox, a handful of elements at most. For whimsical apps an emoji-text
SVG is a fine quick win:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<rect width="100" height="100" rx="20" fill="#1a1b26"/>
<text x="50" y="50" font-size="62" text-anchor="middle"
dominant-baseline="central">🥕</text>
</svg>
-
Wire it in the root head():
links: [
{ rel: "icon", type: "image/svg+xml", href: "/favicon.svg" },
{ rel: "stylesheet", href: appCss },
],
-
Verify it renders non-blank and legible small (browser tab in a preview
screenshot, or read back a rasterized copy). Update it when the app's theme
or name changes meaning — it is part of the app's identity, not a set-and-forget.
PWA icons: only for installable apps
When the app ships a web manifest (the user asked for installable / PWA /
home-screen behavior — do not invent a manifest just to have icons), add
raster icons derived from the favicon artwork so the identity stays
consistent:
public/icon-192.png and public/icon-512.png — the favicon's glyph on
its tile, rasterized at size. Playwright (baked into the sandbox) can
screenshot the served /favicon.svg at a 192/512 viewport; or redraw the
same mark as a flat PNG. Keep it bold and flat — no photographic detail.
- A maskable variant (
"purpose": "maskable") needs the glyph inside the
center ~80% safe zone so launcher shapes don't clip it.
- Wire the manifest
icons array plus theme_color / background_color
from the app's design tokens, and read the 192 back to confirm it stays
legible.
Not supported
No /api/og route, no runtime image renderer, no per-route cards, no runtime
og:* mutation. The card is one static site-wide image chosen at build time.
If you add robots.txt, never blanket Disallow: / — crawlers must fetch /
to read the meta tags.