site: rename "finds" superposts → "bundles" + 2026-05-06 capture run

Collection rename: src/content/finds/ → src/content/bundles/, URLs /finds/<slug>/ → /bundles/<slug>/, internal symbols (FindsPost → Bundle, findListMosaic → bundleMosaic, findsReferencing → bundlesReferencing, Entry.type 'finds' → 'bundle'). Skills build-finds/cluster-finds renamed to build-bundle/cluster-bundles. The `find` collection (individual items) is unchanged.

New content: 4 reviews graduated (finalist, gum-shell, tablepro, valchoose-barf-bags) and 6 bundles for 2026-05-06 (constrain-the-surface, menu-bar-singletons, on-device-by-design, six-american-detours, six-lines-six-origins, things-in-the-terminal). Adds EntryGrid component.
This commit is contained in:
2026-05-06 20:45:22 -04:00
parent 049c6c5377
commit 2def9d685b
23 changed files with 356 additions and 104 deletions
+60 -100
View File
@@ -1,8 +1,8 @@
---
import { getCollection, render } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import SourceBadge from '../../components/SourceBadge.astro';
import EditAction from '../../components/EditAction.astro';
import { sourceSlug } from '../../lib/sources';
import {
resolveExternalLink,
AFFILIATE_REL,
@@ -75,6 +75,9 @@ const breadcrumbs = buildBreadcrumbs([
{ name: review.data.category, url: `/categories/${review.data.category}/` },
{ name: review.data.name, url: `/reviews/${review.id}/` },
]);
const categoryLabel =
review.data.category.charAt(0).toUpperCase() + review.data.category.slice(1);
---
<BaseLayout
@@ -91,49 +94,27 @@ const breadcrumbs = buildBreadcrumbs([
jsonLd={[reviewSchema, breadcrumbs]}
>
<article>
<header class="review-header">
{review.data.source && (
<p class="source-line"><SourceBadge source={review.data.source} /></p>
)}
<p class="meta">
<a href={`/categories/${review.data.category}/`} class="category">
{review.data.category}
</a>
{
external && (
<>
<span class="dot">·</span>
<a
href={external.href}
target="_blank"
rel={external.isAffiliate ? AFFILIATE_REL : 'noopener noreferrer'}
>
{linkLabel} ↗
</a>
{external.isAffiliate && <span class="aff-badge">Affiliate</span>}
</>
)
}
</p>
{external?.isAffiliate && (
<p class="aff-disclosure">{AFFILIATE_DISCLOSURE}</p>
)}
</header>
{fromFindItem && (
<p class="from-find">
← First surfaced on{' '}
<time datetime={fromFindItem.data.date.toISOString()}>
{fmt.format(fromFindItem.data.date)}
</time>{' '}
as <a href={`/find/${fromFindItem.id}/`}>a find</a>
</p>
)}
<div class="body">
<Content />
</div>
{external && (
<p class="external">
<a
href={external.href}
target="_blank"
rel={external.isAffiliate ? AFFILIATE_REL : 'noopener noreferrer'}
>
{linkLabel} ↗
</a>
{external.isAffiliate && <span class="aff-badge">Affiliate</span>}
</p>
)}
{external?.isAffiliate && (
<p class="aff-disclosure">{AFFILIATE_DISCLOSURE}</p>
)}
{import.meta.env.DEV && (
<div class="edit-toolbar">
<EditAction
@@ -145,61 +126,50 @@ const breadcrumbs = buildBreadcrumbs([
</div>
)}
{
review.data.tags.length > 0 && (
<footer class="tags">
{review.data.tags.map((tag) => (
<a href={`/tags/${tag}/`} class="tag">#{tag}</a>
))}
</footer>
)
}
<aside class="colophon" aria-label="Colophon">
<dl>
{review.data.source && (
<>
<dt>Via</dt>
<dd>
<a href={`/sources/#${sourceSlug(review.data.source)}`}>
{review.data.source}
</a>
</dd>
</>
)}
<dt>Filed under</dt>
<dd>
<a href={`/categories/${review.data.category}/`}>{categoryLabel}</a>
</dd>
{fromFindItem && (
<>
<dt>First surfaced</dt>
<dd>
<time datetime={fromFindItem.data.date.toISOString()}>
{fmt.format(fromFindItem.data.date)}
</time>
{' '}as <a href={`/find/${fromFindItem.id}/`}>a find</a>
</dd>
</>
)}
{review.data.tags.length > 0 && (
<>
<dt>Tagged</dt>
<dd class="colophon-tags">
{review.data.tags.map((tag) => (
<a href={`/tags/${tag}/`}>#{tag}</a>
))}
</dd>
</>
)}
</dl>
</aside>
<p class="back"><a href="/">← back</a></p>
</article>
<style>
.source-line { margin: 0 0 0.5rem; }
.from-find {
margin: 0 0 1.5rem;
padding: 0.6rem 0.85rem;
background: var(--rule);
border-radius: 6px;
font-size: 0.85rem;
color: var(--muted);
}
.from-find time { color: var(--muted); }
.review-header {
margin-bottom: 1.5rem;
}
.meta {
margin: 0.5rem 0 0;
color: var(--muted);
font-size: 0.9rem;
}
.meta .dot { margin: 0 0.4rem; }
.meta .category { color: var(--muted); }
.meta .category:hover { color: var(--accent); }
.aff-badge {
display: inline-block;
margin-left: 0.45rem;
padding: 0.12rem 0.45rem;
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
font-size: 0.65rem;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--muted);
background: var(--rule);
border-radius: 999px;
vertical-align: middle;
}
.aff-disclosure {
margin: 0.6rem 0 0;
font-size: 0.78rem;
color: var(--muted);
font-style: italic;
}
.body :global(p) { margin: 0 0 1rem; }
.body :global(h2) { font-size: 1.3rem; margin: 2rem 0 0.75rem; }
.body :global(h3) { font-size: 1.1rem; margin: 1.5rem 0 0.5rem; }
@@ -299,16 +269,6 @@ const breadcrumbs = buildBreadcrumbs([
margin: 0 0 1.75rem;
}
.body :global(blockquote p) { margin: 0; }
.tags {
margin-top: 2.5rem;
padding-top: 1rem;
border-top: 1px solid var(--rule);
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
}
.tag { font-size: 0.85rem; color: var(--muted); }
.tag:hover { color: var(--accent); }
.back { margin-top: 2rem; font-size: 0.9rem; }
.edit-toolbar {
margin: 2rem 0 0;