Files
Compass/web/components/SEO.tsx
MartinBraquet de99d2decc Add social preview card rendering and deployment
- Introduced `OgCard` React component as the default Open Graph preview card for social sharing (1200x630).
- Added `BrandFonts` module to load web fonts for accurate rendering.
- Integrated `OgCard` into the media creator pipeline as a `<Still>` with a new `still:og` script.
- Updated upload script to version the output and ensure compatibility with caching policies.
- Included new card meta tags across web pages, defaulting to this design for links without a custom preview.
- Extended theme and README to document font usage, card details, and deployment workflow.
2026-07-24 18:34:18 +02:00

81 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {DEPLOYED_WEB_URL, endTitle} from 'common/envs/constants'
import {removeUndefinedProps} from 'common/util/object'
import {buildOgUrl} from 'common/util/og'
import Head from 'next/head'
export function SEO<P extends Record<string, string | undefined>>(props: {
title: string
description: string
url?: string
ogProps?: {props: P; endpoint: string}
image?: string
/** Only needed for an image that is not a 1200×630 PNG card — see the note on the tags below. */
imageType?: string
imageWidth?: string
imageHeight?: string
imageAlt?: string
}) {
const {title, description, url, image, ogProps} = props
const {
// The /api/og/* routes answer with PNG (that is what @vercel/og's ImageResponse emits) at the
// 1.91:1 card size, and that is the only image any caller passes today.
imageType = 'image/png',
imageWidth = '1200',
imageHeight = '630',
imageAlt,
} = props
const imageUrl =
image ?? (ogProps && buildOgUrl(removeUndefinedProps(ogProps.props) as any, ogProps.endpoint))
const absUrl = DEPLOYED_WEB_URL + url
const fullTitle = `${title} | ${endTitle}`
return (
<Head>
<title>{fullTitle}</title>
<meta name="description" content={description} key="description" />
{url && <link rel="canonical" href={absUrl} key="canonical" />}
{url && (
<meta
name="apple-itunes-app"
content={'app-id=6444136749, app-argument=' + absUrl}
key="apple-itunes-app"
/>
)}
{/*OG tags (WhatsApp, Facebook, etc.). Everything here overrides the site-wide defaults in
_app.tsx via the shared `key`s.*/}
<meta property="og:title" content={fullTitle} key="og-title" />
<meta property="og:description" content={description} key="og-description" />
{url && <meta property="og:url" content={absUrl} key="og-url" />}
{imageUrl && (
<>
<meta property="og:image" content={imageUrl} key="og-image" />
<meta property="og:image:secure_url" content={imageUrl} key="og-image-secure-url" />
{/*Every image reaching this component is a 1200×630 card, so the dimensions are safe to
state — and stating them is what makes WhatsApp reliably pick the large layout. The
type has to be restated as well: _app.tsx's default says JPEG (its card is a .jpg),
and without this the two would disagree on pages that override the image.*/}
<meta property="og:image:type" content={imageType} key="og-image-type" />
<meta property="og:image:width" content={imageWidth} key="og-image-width" />
<meta property="og:image:height" content={imageHeight} key="og-image-height" />
<meta property="og:image:alt" content={imageAlt ?? title} key="og-image-alt" />
</>
)}
{/*Twitter/X tags — separate!*/}
<meta name="twitter:title" content={fullTitle} key="twitter-title" />
<meta name="twitter:description" content={description} key="twitter-description" />
{imageUrl && (
<>
<meta name="twitter:card" content="summary_large_image" key="twitter-card" />
<meta name="twitter:image" content={imageUrl} key="twitter-image" />
</>
)}
</Head>
)
}