Improve SEO and social card wiring

Add SearchAction and optional Product JSON-LD, per-page OG images/alts, and document the og build and cache headers.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
michilis
2026-08-21 02:17:35 +02:00
co-authored by Cursor
parent 6f17b572b1
commit 1c5df18e81
10 changed files with 120 additions and 60 deletions
@@ -34,6 +34,7 @@ import { ogImageFor } from '../../../lib/og';
import { fedimintSubject } from '../../../lib/review-subject';
import { breadcrumbNode, mintServiceNode } from '../../../lib/schema';
import { clampWords, cleanMintDescription, isIndexableMint, nameCollisions, seoName } from '../../../lib/seo';
import { PRODUCT_JSONLD } from '../../../lib/site';
import { bannerHtml } from '../../../lib/mint-state';
import { localModuleName, localNetworkName, moduleRowsHtml, otherModules } from '../../../lib/fedimint-ui';
import { iconGradient, initials, transitionName, truncateMiddle } from '../../../lib/format';
@@ -197,6 +198,7 @@ const schema = [
canonical,
name: titleName,
description,
product: PRODUCT_JSONLD,
/*
* The invite code stands in for the service's address, because it is the address:
* it is the string a wallet is given to reach this federation. A federation with no
@@ -217,11 +219,13 @@ const schema = [
---
<Base
title={t('fedimint.title', { name: titleName })}
title={title}
description={description}
current="fedimints"
noindex={!indexable}
schema={schema}
image={ogImage ?? undefined}
imageAlt={imageAlt}
>
<div class="page">
<nav class="crumbs" aria-label={t('fedimint.breadcrumb')}>
+7 -1
View File
@@ -15,6 +15,7 @@ import { ogImageFor } from '../../../lib/og';
import { mintSubject } from '../../../lib/review-subject';
import { breadcrumbNode, mintServiceNode } from '../../../lib/schema';
import { clampWords, cleanMintDescription, isIndexableMint, nameCollisions, seoName } from '../../../lib/seo';
import { PRODUCT_JSONLD } from '../../../lib/site';
import { bannerHtml, limitsHtml, localNutName, nutRowsHtml } from '../../../lib/mint-state';
import {
displayDomain, iconGradient, initials, shortNpub, transitionName, truncateMiddle,
@@ -185,9 +186,13 @@ const title =
* the plain status, and the limits are only quoted for a mint that can actually take
* a deposit.
*/
// A mint with no name of its own (titleName fell back to the domain) gets the
// domain-only sentence, not "Community reviews for {domain} ({domain})".
const lead =
cleanMintDescription(mint.description) ??
t('seo.desc.fallback', { name: titleName === domain ? name : titleName, domain });
(titleName === domain
? t('seo.desc.fallbackDomain', { domain })
: t('seo.desc.fallback', { name: titleName, domain }));
const ratingPart = hasRating
? t('seo.desc.rated', { rating: f.decimal(mint.rating_avg!), n: mint.review_count })
@@ -251,6 +256,7 @@ const schema = [
name: titleName,
description,
mintUrl: mint.url,
product: PRODUCT_JSONLD,
icon: icon ? new URL(icon, Astro.site).href : null,
/*
* Only when reviews actually carried ratings. `ratedCount` is what produced the