site: ui/ux refresh, seo pass, find/lists indexes, 25 new finds

- newspaper-style masthead (stats | "Unique" | About/Blog) on every page
  with per-page italic subheader + last-updated date on listing pages;
  sitewide 42rem reading width
- pinterest home (hero + masonry, "load more"); old list view at /index3/
- new pages: /about, /blog, /legal, /find/, /finds/, custom 404
- footer collection links now clickable; "founded" stat replaces "updated"
  in the masthead
- centralised SEO component with per-page json-ld (Review, BlogPosting,
  Product, Article, CollectionPage, ItemList, BreadcrumbList, etc.);
  @astrojs/sitemap + @astrojs/rss; robots.txt; promoted finds canonical
  to their review
- memoize getSiteStats() so the masthead/footer counts don't multiply
  per-page build cost
- 25 new daily-finds captures
This commit is contained in:
2026-05-03 16:58:23 -04:00
parent c184f3f50a
commit c8095d2766
54 changed files with 2337 additions and 95 deletions
+101
View File
@@ -0,0 +1,101 @@
---
export interface OgImage {
url: string;
width: number;
height: number;
alt: string;
type?: string;
}
interface Props {
title: string;
description: string;
canonicalPath?: string;
canonicalOverride?: string;
ogType?: 'website' | 'article';
ogImage?: OgImage;
noindex?: boolean;
publishedTime?: Date;
modifiedTime?: Date;
articleTags?: string[];
jsonLd?: Record<string, unknown> | Record<string, unknown>[];
}
const {
title,
description,
canonicalPath,
canonicalOverride,
ogType = 'website',
ogImage,
noindex = false,
publishedTime,
modifiedTime,
articleTags = [],
jsonLd,
} = Astro.props;
const SITE_NAME = 'Unique';
const titleTag = title === SITE_NAME ? SITE_NAME : `${title} — ${SITE_NAME}`;
const canonicalUrl =
canonicalOverride ??
new URL(canonicalPath ?? Astro.url.pathname, Astro.site).href;
const jsonLdBlocks = jsonLd
? Array.isArray(jsonLd)
? jsonLd
: [jsonLd]
: [];
---
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{titleTag}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonicalUrl} />
{noindex && <meta name="robots" content="noindex,nofollow" />}
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<meta name="theme-color" content="#fafaf7" media="(prefers-color-scheme: light)" />
<meta name="theme-color" content="#161613" media="(prefers-color-scheme: dark)" />
<link rel="alternate" type="application/rss+xml" title={`${SITE_NAME} — RSS`} href="/rss.xml" />
<meta property="og:type" content={ogType} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonicalUrl} />
<meta property="og:site_name" content={SITE_NAME} />
<meta property="og:locale" content="en_US" />
{ogImage && (
<>
<meta property="og:image" content={ogImage.url} />
<meta property="og:image:width" content={String(ogImage.width)} />
<meta property="og:image:height" content={String(ogImage.height)} />
<meta property="og:image:alt" content={ogImage.alt} />
{ogImage.type && <meta property="og:image:type" content={ogImage.type} />}
</>
)}
{ogType === 'article' && publishedTime && (
<meta property="article:published_time" content={publishedTime.toISOString()} />
)}
{ogType === 'article' && modifiedTime && (
<meta property="article:modified_time" content={modifiedTime.toISOString()} />
)}
{ogType === 'article' &&
articleTags.map((t) => <meta property="article:tag" content={t} />)}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
{ogImage && (
<>
<meta name="twitter:image" content={ogImage.url} />
<meta name="twitter:image:alt" content={ogImage.alt} />
</>
)}
{jsonLdBlocks.map((block) => (
<script type="application/ld+json" is:inline set:html={JSON.stringify(block)} />
))}