feat(seo): site meta composable + Open Graph image generation

Adds `useSiteMeta()` composable that wraps useSeoMeta with site defaults
(title template, canonical URL, og/twitter image, og:site_name) and
absolute-URL handling via runtimeConfig.public.appUrl.

Adds /og/events/[slug].png route that renders per-event OG images via
satori + @resvg/resvg-js, cached on disk by slug + updatedAt. Bundles
Brygada 1918 + Commit Mono fonts as server assets, ships a fallback
default.png, and patches @shuding/opentype.js via patch-package.

Converts ~25 pages from useHead to useSiteMeta and adds noindex on
private/auth/admin pages.
This commit is contained in:
Jennie Robinson Faber 2026-05-21 17:50:34 +01:00
parent 877ef1a220
commit 31144617d7
36 changed files with 1182 additions and 53 deletions

View file

@ -39,8 +39,9 @@ if (!policy) {
throw createError({ statusCode: 404, statusMessage: 'Policy not found', fatal: true })
}
useHead({
title: `${policy.title} · Ghost Guild`,
useSiteMeta({
title: policy.title,
description: policy.description,
})
</script>

View file

@ -231,8 +231,10 @@
</template>
<script setup>
useHead({
title: 'Privacy Policy · Ghost Guild',
useSiteMeta({
title: 'Privacy Policy',
description:
'How Ghost Guild handles your data: what we collect, why we collect it, and who has access. No Google Analytics, no advertising pixels, no third-party tracking.',
})
</script>

View file

@ -50,8 +50,10 @@
</template>
<script setup>
useHead({
title: 'Refund Policy · Ghost Guild',
useSiteMeta({
title: 'Refund Policy',
description:
'How Ghost Guild handles refund requests for membership dues and event tickets. Pay-what-you-can, case-by-case, run as a non-profit program of Baby Ghosts.',
})
</script>

View file

@ -250,8 +250,10 @@
</template>
<script setup>
useHead({
title: 'Terms of Service · Ghost Guild',
useSiteMeta({
title: 'Terms of Service',
description:
'Terms of service for ghostguild.org and wiki.ghostguild.org, operated by Baby Ghosts. Covers accounts, membership, acceptable use, and what we expect from each other.',
})
</script>