You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a page is shared (e.g. pasted into Discord), the link unfurls into a preview card β title, description, and image. Right now the site emits only a <meta name="description"> and a <title>, so shared links have no image and a weak preview. This ticket adds Open Graph tags so links unfurl nicely.
The behavior we want:
List pages (/projects, /webring, and the / redirect target) β a default site preview image, plus each page's own title and description.
Project detail pages (/projects/[slug]) β the project's cover image and the project's title/description. If a project has no cover (covers are optional), it falls back to the default image.
Base.astro already receives title and description props, so it's the natural place to centralize this: it accepts an optional OG image (and reuses title/description for the OG title/description), builds the absolute URLs, and emits the tags. Individual pages only override what differs (the detail page passes its cover).
Open Graph image URLs must be absolute, which needs site set in astro.config.mjs. That's configured separately β write the code against Astro.site, and if it isn't set yet, leave a clear TODO and fall back to the request origin so dev still works.
Files you'll touch:
src/layouts/Base.astro (accept an OG image prop; emit the tags)
src/pages/projects/[slug].astro (pass the project's cover as the OG image)
Don't touch:
The existing <title> / <meta name="description"> and the favicon block in Base.astro β add alongside them.
The detail page's layout/body β it's worked on separately; your only change there is passing the OG image prop into <Base>.
The content schema and components.
π οΈ Implementation Plan
Default preview image (TODO asset). Reference a default image at public/og-default.png (so it serves from /og-default.png). The asset itself needs to be supplied β leave a TODO comment and ask Jacc for a branded social-preview image (recommended Open Graph size ~1200Γ630). The code should be fully wired so dropping the file in later is the only remaining step.
Extend Base.astro props. Add an optional ogImage prop (a path/URL string). Reuse the existing title and description for the OG title/description so callers don't repeat themselves:
---interfaceProps { title:string; description?:string; ogImage?:string; // path or URL; defaults to the site default below}const { title, description ='Projects and personal sites built by Carleton Computer Science students.', ogImage ='/og-default.png', // TODO: add public/og-default.png (ask Jacc)} =Astro.props;// Absolute URLs. `Astro.site` comes from astro.config.mjs `site` (configured// separately). Until it's set, fall back to the current request origin so dev// works. TODO: once `site` is set, this resolves to the real production URLs.const base =Astro.site??Astro.url;const ogImageUrl =newURL(ogImage, base).href;const ogUrl =newURL(Astro.url.pathname, base).href;---
Emit the OG tags in <head>, alongside the existing description/title:
Project detail page passes its cover. In src/pages/projects/[slug].astro, pass the project's optimized cover path as ogImage on the <Base> call. Because the cover is optional, pass it only when present so it falls back to the default; the title/description already flow through the existing props:
(data.cover.src is the built asset path; Base absolutizes it against Astro.site.)
Verify. Run pnpm build and view-source a list page and a project page: the list page's og:image is β¦/og-default.png; the project page's og:image is its cover (and a cover-less project shows the default). Optionally check the unfurl in a preview debugger. Until site is configured the URLs will point at the dev/preview origin β that's the expected interim behavior noted by the TODO.
π§ Context
When a page is shared (e.g. pasted into Discord), the link unfurls into a preview card β title, description, and image. Right now the site emits only a
<meta name="description">and a<title>, so shared links have no image and a weak preview. This ticket adds Open Graph tags so links unfurl nicely.The behavior we want:
/projects,/webring, and the/redirect target) β a default site preview image, plus each page's own title and description./projects/[slug]) β the project's cover image and the project's title/description. If a project has no cover (covers are optional), it falls back to the default image.Base.astroalready receivestitleanddescriptionprops, so it's the natural place to centralize this: it accepts an optional OG image (and reuses title/description for the OG title/description), builds the absolute URLs, and emits the tags. Individual pages only override what differs (the detail page passes its cover).Open Graph image URLs must be absolute, which needs
siteset inastro.config.mjs. That's configured separately β write the code againstAstro.site, and if it isn't set yet, leave a clearTODOand fall back to the request origin so dev still works.Files you'll touch:
src/layouts/Base.astro(accept an OG image prop; emit the tags)src/pages/projects/[slug].astro(pass the project's cover as the OG image)Don't touch:
<title>/<meta name="description">and the favicon block inBase.astroβ add alongside them.<Base>.π οΈ Implementation Plan
Default preview image (TODO asset). Reference a default image at
public/og-default.png(so it serves from/og-default.png). The asset itself needs to be supplied β leave aTODOcomment and ask Jacc for a branded social-preview image (recommended Open Graph size ~1200Γ630). The code should be fully wired so dropping the file in later is the only remaining step.Extend
Base.astroprops. Add an optionalogImageprop (a path/URL string). Reuse the existingtitleanddescriptionfor the OG title/description so callers don't repeat themselves:Emit the OG tags in
<head>, alongside the existingdescription/title:Project detail page passes its cover. In
src/pages/projects/[slug].astro, pass the project's optimized cover path asogImageon the<Base>call. Because the cover is optional, pass it only when present so it falls back to the default; the title/description already flow through the existing props:(
data.cover.srcis the built asset path;Baseabsolutizes it againstAstro.site.)Verify. Run
pnpm buildand view-source a list page and a project page: the list page'sog:imageisβ¦/og-default.png; the project page'sog:imageis its cover (and a cover-less project shows the default). Optionally check the unfurl in a preview debugger. Untilsiteis configured the URLs will point at the dev/preview origin β that's the expected interim behavior noted by the TODO.β Acceptance Criteria
Base.astroemitsog:type,og:site_name,og:title,og:description,og:url,og:image,og:image:alt, with absolute URLs.TODOto addpublic/og-default.png(the asset comes from Jacc).Astro.sitewith a documented fallback +TODOfor whensiteis configured.<title>,<meta name="description">, and favicon block are intact; the detail page's body/layout is unchanged apart from theogImageprop.pnpm format:check,pnpm check, andpnpm buildall pass.