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
+41 -12
View File
@@ -2,6 +2,12 @@
import BaseLayout from '../layouts/BaseLayout.astro';
import sourcesData from '../../sources.json';
import { getAllFinds } from '../lib/find-items';
import {
buildOgImage,
buildBreadcrumbs,
buildItemList,
absoluteUrl,
} from '../lib/seo';
type Source = {
name: string;
@@ -40,14 +46,43 @@ for (const f of allFinds) {
arr.push(f);
findsBySource.set(f.data.source, arr);
}
const lastUpdated = allFinds.sort(
(a, b) => b.data.date.valueOf() - a.data.date.valueOf()
)[0]?.data.date;
const description = `The ${sources.length} feeds, shops, and forums Unique draws daily picks from.`;
const ogImage = buildOgImage(undefined, 'site', 'Unique sources catalog');
const itemList = buildItemList(
sources.map((s) => ({ url: s.url, name: s.name })),
'Sources used by Unique'
);
const collectionPage: Record<string, unknown> = {
'@context': 'https://schema.org',
'@type': 'CollectionPage',
'@id': absoluteUrl('/sources/#collection'),
name: 'Sources — Unique',
description,
url: absoluteUrl('/sources/'),
inLanguage: 'en-US',
};
const breadcrumbs = buildBreadcrumbs([
{ name: 'Home', url: '/' },
{ name: 'Sources', url: '/sources/' },
]);
---
<BaseLayout title="Sources" wide>
<div class="intro">
<h1>Sources</h1>
<p>Where the daily picks come from. {sources.length} feeds, shops, and forums.</p>
</div>
<BaseLayout
title="Sources"
description={description}
subheader={`Where the daily picks come from. ${sources.length} feeds, shops, and forums.`}
lastUpdated={lastUpdated}
ogImage={ogImage}
jsonLd={[collectionPage, itemList, breadcrumbs]}
>
<ul class="grid">
{
sources.map((source) => {
@@ -93,12 +128,6 @@ for (const f of allFinds) {
<p class="back"><a href="/">← back</a></p>
<style>
.intro {
margin-bottom: 1.5rem;
}
.intro h1 { margin: 0 0 0.25rem; font-size: 1.85rem; }
.intro p { margin: 0; color: var(--muted); }
.grid {
column-count: 3;
column-gap: 1rem;