Link roundup posts from Shelf cards and keep long link lists short

Shelf game cards show the post about the game, as Library rows already
do. A make's chip prefers its own build log and says Blog for other
posts. Post chips and Connected pages count the links past the first
few, so a roundup linking a hundred items stays readable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
This commit is contained in:
Claude
2026-10-18 17:08:43 +00:00
parent b3cf6bd02e
commit 57721efbcd
10 changed files with 89 additions and 20 deletions
@@ -6,8 +6,12 @@ import SidePanel from "./SidePanel.astro";
interface Props {
items: Connected[];
/** A roundup links many pages; past this many the panel counts the rest. */
max?: number;
}
const { items } = Astro.props;
const { items: all, max = 8 } = Astro.props;
const items = all.length > max + 1 ? all.slice(0, max) : all;
const more = all.length - items.length;
---
{
items.length > 0 && (
@@ -26,11 +30,17 @@ const { items } = Astro.props;
</li>
))}
</ul>
{more > 0 && <p class="more">And {more} more, each linked in the post.</p>}
</SidePanel>
)
}
<style>
.more {
margin: 10px 0 0;
font-size: 13px;
color: var(--gk-muted);
}
.cp {
margin: 0;
padding: 0;
@@ -5,7 +5,6 @@
* blog's seal art stands in.
*/
import Button from "@gk/ui/components/Button.astro";
import Chip from "@gk/ui/components/Chip.astro";
import GiltCorners from "@gk/ui/components/GiltCorners.astro";
import Seal from "@gk/ui/components/Seal.astro";
import Star from "@gk/ui/components/Star.astro";
@@ -16,6 +15,7 @@ import TickScale from "@gk/ui/components/TickScale.astro";
import { filamentBanner } from "@gk/ui/components/seals.ts";
import { dateLine, nowStatuses, pageLinks, type Post } from "../../lib/blog.ts";
import { mediaSrc, type SiteIndex } from "../../lib/site.ts";
import PageChips from "./PageChips.astro";
interface Props {
post: Post;
@@ -62,7 +62,7 @@ const now = nowStatuses(s, post);
<h2 id="lead-title" class="title"><a href={href}>{post.title}</a></h2>
<span class="underline" aria-hidden="true"></span>
{post.data.summary && <p class="summary">{post.data.summary}</p>}
{links.length > 0 && <span class="chips">{links.map((l) => <Chip href={l.href}>{l.label}</Chip>)}</span>}
{links.length > 0 && <span class="chips"><PageChips links={links} max={4} /></span>}
<Button href={href} arrow class="go">{post.data.postType === "build log" ? "Read the build log" : "Read the post"}</Button>
</div>
<GiltCorners />
@@ -0,0 +1,26 @@
---
/**
* A post's page chips. A roundup can link a hundred pages, so past the first
* few the rest are counted; the post itself links every one.
*/
import Chip from "@gk/ui/components/Chip.astro";
import type { PageLink } from "../../lib/blog.ts";
interface Props {
links: PageLink[];
max?: number;
}
const { links, max = 5 } = Astro.props;
const shown = links.length > max + 1 ? links.slice(0, max) : links;
const more = links.length - shown.length;
---
{shown.map((l) => <Chip href={l.href}>{l.label}</Chip>)}
{more > 0 && <span class="more">and {more} more</span>}
<style>
.more {
font-size: 12px;
color: var(--gk-muted);
white-space: nowrap;
}
</style>
@@ -16,6 +16,7 @@ import { connectedPages, neighbours, nowStatuses, pageLinks, readMinutes, relate
import { formatDate, mediaSrc, type SiteIndex } from "../../lib/site.ts";
import ConnectedPages from "./ConnectedPages.astro";
import CrumbHero from "./CrumbHero.astro";
import PageChips from "./PageChips.astro";
import PostList from "./PostList.astro";
import SidePanel from "./SidePanel.astro";
import StarHeading from "./StarHeading.astro";
@@ -67,7 +68,7 @@ const making = d.topic === "Making";
<span>{readMinutes(body)} min read</span>
{(links.length > 0 || now.length > 0) && <span aria-hidden="true">·</span>}
{now.map((st) => <StatusPill>Now · {st.data.kind}</StatusPill>)}
{links.map((l) => <Chip href={l.href}>{l.label}</Chip>)}
<PageChips links={links} max={8} />
</div>
</CrumbHero>
</Fragment>
@@ -3,11 +3,11 @@
* One post as a fixed-height row: picture, topic and date, title, a
* two-line summary, Now chips for linked notes and page-link chips.
*/
import Chip from "@gk/ui/components/Chip.astro";
import Star from "@gk/ui/components/Star.astro";
import StatusPill from "@gk/ui/components/StatusPill.astro";
import { dateLine, nowStatuses, pageLinks, type Post } from "../../lib/blog.ts";
import { mediaSrc, type SiteIndex } from "../../lib/site.ts";
import PageChips from "./PageChips.astro";
import PostThumb from "./PostThumb.astro";
interface Props {
@@ -37,7 +37,7 @@ const now = nowStatuses(s, post);
<H class="title"><a href={href}>{post.title}</a></H>
{post.data.summary && <p class="summary">{post.data.summary}</p>}
<span class="chips">
{links.length ? links.map((l) => <Chip href={l.href}>{l.label}</Chip>) : <span class="none">Not tied to a page</span>}
{links.length ? <PageChips links={links} max={3} /> : <span class="none">Not tied to a page</span>}
</span>
</div>
</li>
@@ -25,7 +25,9 @@ const status = site.statusFor(game);
// Players and play time sync from BoardGameGeek; nothing shows until they have.
const facts = [gamePlayers(game), gamePlayTime(game)].filter(Boolean);
const seats = playersOf(game);
const hasPost = site.backlinks(game, { type: "post" }).length > 0;
const post = site.backlinks(game, { type: "post" })[0];
const postHref = post && site.href(post);
const hasPost = !!post;
---
<li
id={game.slug}
@@ -61,9 +63,19 @@ const hasPost = site.backlinks(game, { type: "post" }).length > 0;
</div>
)
}
<a class="gk-text gk-quiet bgg" href={`https://boardgamegeek.com/boardgame/${d.bggId}`} rel="external noopener">
BoardGameGeek<span class="gk-visually-hidden">: {game.title}</span>
</a>
<span class="links">
{
post && postHref && (
<a href={postHref} class="blog">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h16v14H4zM8 9h8M8 13h5" /></svg>
Blog: {post.title}
</a>
)
}
<a class="gk-text gk-quiet bgg" href={`https://boardgamegeek.com/boardgame/${d.bggId}`} rel="external noopener">
BoardGameGeek<span class="gk-visually-hidden">: {game.title}</span>
</a>
</span>
</div>
</Card>
</li>
@@ -134,10 +146,24 @@ const hasPost = site.backlinks(game, { type: "post" }).length > 0;
font-size: 12px;
color: var(--gk-muted);
}
.bgg {
align-self: flex-start;
.links {
margin-top: auto;
padding-top: 4px;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
}
.blog {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
font-weight: 600;
color: var(--gk-teal-bright);
}
.bgg {
align-self: flex-start;
font-size: 12px;
}
</style>