Tag pages are now generated from the union of entry and find tags — 1,179 tags that existed only on finds (most tag links on find pages) previously 404'd. Tagged finds render in a compact secondary "Finds" list below the entry grid, keeping the editorial tier on top. Find-only tag pages with fewer than 3 finds are noindexed until they fill out. Tag feeds stay entries-only; the RSS pill and autodiscovery link only render where a feed exists. Category pages unchanged (finds have no category). Claude-Session: https://claude.ai/code/session_01WZaczDJjL3xZ3u5spsN5AL
151 lines
3.9 KiB
Plaintext
151 lines
3.9 KiB
Plaintext
---
|
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
|
import { getFeedCategories, getFeedTags } from '../lib/feed';
|
|
import { buildOgImage, buildBreadcrumbs, absoluteUrl } from '../lib/seo';
|
|
|
|
const categories = await getFeedCategories();
|
|
const tags = await getFeedTags();
|
|
|
|
const description =
|
|
'Subscribe to Unique: RSS and JSON feeds for everything, plus per-category and per-tag feeds.';
|
|
|
|
const ogImage = buildOgImage(undefined, 'site', 'Feeds on Unique');
|
|
|
|
const collectionPage = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'CollectionPage',
|
|
'@id': absoluteUrl('/feeds/#collection'),
|
|
name: 'Feeds — Unique',
|
|
description,
|
|
url: absoluteUrl('/feeds/'),
|
|
inLanguage: 'en-US',
|
|
};
|
|
|
|
const breadcrumbs = buildBreadcrumbs([
|
|
{ name: 'Home', url: '/' },
|
|
{ name: 'Feeds', url: '/feeds/' },
|
|
]);
|
|
---
|
|
|
|
<BaseLayout
|
|
title="Feeds"
|
|
description={description}
|
|
subheader="Take Unique with you."
|
|
ogImage={ogImage}
|
|
jsonLd={[collectionPage, breadcrumbs]}
|
|
>
|
|
<h1>Feeds</h1>
|
|
<p>
|
|
Everything published here — reviews, bundles, posts — is available as a
|
|
feed. Paste any of these into your feed reader.
|
|
</p>
|
|
|
|
<h2>Everything</h2>
|
|
<ul class="feed-list">
|
|
<li>
|
|
<a href="/rss.xml">/rss.xml</a> — all entries, RSS 2.0
|
|
</li>
|
|
<li>
|
|
<a href="/feed.json">/feed.json</a> — all entries,
|
|
<a href="https://jsonfeed.org/">JSON Feed 1.1</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<h2>By category</h2>
|
|
<p class="hint">Each category page also advertises its own feed for autodiscovery.</p>
|
|
<ul class="feed-list scopes">
|
|
{categories.map(({ name, count }) => (
|
|
<li>
|
|
<a href={`/categories/${name}/`}>{name}</a>
|
|
<span class="count">({count})</span>
|
|
<a class="rss" href={`/categories/${name}/rss.xml`}>RSS</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<h2>By tag</h2>
|
|
<ul class="feed-list scopes">
|
|
{tags.map(({ name, count }) => (
|
|
<li>
|
|
<a href={`/tags/${name}/`}>#{name}</a>
|
|
<span class="count">({count})</span>
|
|
<a class="rss" href={`/tags/${name}/rss.xml`}>RSS</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<h2>For robots</h2>
|
|
<p>Reading the site programmatically? You may also want:</p>
|
|
<ul class="feed-list">
|
|
<li>
|
|
<a href="/search.json">/search.json</a> — the full index of everything on
|
|
the site as JSON, including individual finds (which don't appear in the
|
|
feeds — they surface through bundles)
|
|
</li>
|
|
<li>
|
|
<a href="/llms.txt">/llms.txt</a> — a plain-text map of the site for
|
|
language models and other tools
|
|
</li>
|
|
<li>
|
|
<a href="/sitemap-index.xml">/sitemap-index.xml</a> — every page, for crawlers
|
|
</li>
|
|
<li>
|
|
Every page embeds schema.org JSON-LD (<code>Review</code>,
|
|
<code>Product</code>, <code>Article</code>, <code>CollectionPage</code>…)
|
|
</li>
|
|
</ul>
|
|
|
|
<style>
|
|
h2 {
|
|
margin-top: 2.25rem;
|
|
font-size: 1.15rem;
|
|
}
|
|
.hint {
|
|
margin: 0.25rem 0 0.75rem;
|
|
font-size: 0.85rem;
|
|
color: var(--muted);
|
|
font-style: italic;
|
|
}
|
|
.feed-list {
|
|
margin: 0.5rem 0 0;
|
|
padding-left: 1.25rem;
|
|
}
|
|
.feed-list li {
|
|
margin: 0.3rem 0;
|
|
}
|
|
.feed-list.scopes {
|
|
list-style: none;
|
|
padding: 0;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.35rem 1.4rem;
|
|
}
|
|
.feed-list.scopes li {
|
|
margin: 0;
|
|
white-space: nowrap;
|
|
}
|
|
.count {
|
|
margin-left: 0.25rem;
|
|
font-size: 0.8rem;
|
|
color: var(--muted);
|
|
}
|
|
.rss {
|
|
margin-left: 0.45rem;
|
|
padding: 0.05rem 0.4rem;
|
|
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
font-size: 0.66rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
background: var(--rule);
|
|
color: var(--muted);
|
|
border-radius: 999px;
|
|
vertical-align: middle;
|
|
}
|
|
.rss:hover {
|
|
color: var(--accent);
|
|
text-decoration: none;
|
|
}
|
|
</style>
|
|
</BaseLayout>
|