commit 0ebdb1a6b3247eb856d297eb7b4effc702ae2f22
parent d8c9f227f216e98be545f100f549192a764798cb
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Fri, 25 Sep 2026 20:08:42 -0400
hub, homepage: Title Case headings, no subtitle under Official Instances, no description on the official cards
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
4 files changed, 53 insertions(+), 70 deletions(-)
diff --git a/export/app/components/hub/AddArchive.tsx b/export/app/components/hub/AddArchive.tsx
@@ -1,7 +1,7 @@
"use client";
// The add-an-archive control: paste an archive URL, it validates against the
-// federation contract, and on success the archive joins "Archives you added".
+// federation contract, and on success the archive joins "Archives You Added".
// Written as direction — validating → success, or a plain reason it couldn't
// be read. Self-contained: owns its input + status and commits to the shared
// registry. The button is the family's CTA — ink on ground, not a coloured
diff --git a/export/app/components/hub/ArchiveShelf.tsx b/export/app/components/hub/ArchiveShelf.tsx
@@ -3,8 +3,9 @@
// The hub's front: what it is, the official instances it searches, the
// archives a visitor added, and the form to add another — in the homepage's
// register (homepage/app/page.tsx): short declarative sentences, no eyebrow
-// over a band, no subtitle under a heading, and each heading after the H1
-// names what is under it.
+// over a band, no subtitle under a heading (nor under a card's title: the
+// site's description is not shown), Title Case headings, and each heading
+// after the H1 names what is under it.
//
// Two kinds of number, deliberately kept apart. An official card shows the
// homepage's own figures for that archive, composed at hub build time
@@ -67,7 +68,6 @@ function ArchiveCard({
}) {
const accent = site.accent || figures?.accent || fallbackAccent;
const href = site.siteUrl || site.origin;
- const description = figures?.siteDescription;
const channels =
figures === undefined ? site.channelCount : figures?.channels;
const anyFigure =
@@ -86,38 +86,31 @@ function ArchiveCard({
className="absolute inset-y-0 left-0 w-[3px]"
style={{ background: accent }}
/>
- <div className="flex flex-col gap-1.5">
- <div className="flex items-start justify-between gap-2">
- <h3 className="font-display text-lg font-semibold text-foreground">
- <a
- href={href}
- target="_blank"
- rel="noopener noreferrer"
- className="underline decoration-transparent underline-offset-4 transition-colors hover:decoration-border-strong"
- >
- {site.siteTitle}
- <span aria-hidden="true" className="ml-1.5 text-sm text-faint">
- ↗
- </span>
- </a>
- </h3>
- {onRemove && (
- <button
- type="button"
- onClick={() => onRemove(site.origin)}
- aria-label={`Remove ${site.siteTitle}`}
- // Reveal-on-hover is not an affordance on a touch screen: a device
- // with no hover shows it always. `hover:` never gates a function.
- className="-mr-1 mt-0.5 rounded p-0.5 text-muted-foreground/60 opacity-0 transition-opacity pointer-coarse:opacity-100 hover:text-destructive focus-visible:opacity-100 group-hover:opacity-100"
- >
- <X className="size-3.5" aria-hidden="true" />
- </button>
- )}
- </div>
- {description && (
- <p className="text-sm leading-relaxed text-muted-foreground">
- {description}
- </p>
+ <div className="flex items-start justify-between gap-2">
+ <h3 className="font-display text-lg font-semibold text-foreground">
+ <a
+ href={href}
+ target="_blank"
+ rel="noopener noreferrer"
+ className="underline decoration-transparent underline-offset-4 transition-colors hover:decoration-border-strong"
+ >
+ {site.siteTitle}
+ <span aria-hidden="true" className="ml-1.5 text-sm text-faint">
+ ↗
+ </span>
+ </a>
+ </h3>
+ {onRemove && (
+ <button
+ type="button"
+ onClick={() => onRemove(site.origin)}
+ aria-label={`Remove ${site.siteTitle}`}
+ // Reveal-on-hover is not an affordance on a touch screen: a device
+ // with no hover shows it always. `hover:` never gates a function.
+ className="-mr-1 mt-0.5 rounded p-0.5 text-muted-foreground/60 opacity-0 transition-opacity pointer-coarse:opacity-100 hover:text-destructive focus-visible:opacity-100 group-hover:opacity-100"
+ >
+ <X className="size-3.5" aria-hidden="true" />
+ </button>
)}
</div>
<div className="mt-auto flex flex-col gap-3">
@@ -177,12 +170,9 @@ export default function ArchiveShelf() {
{official.length > 0 ? (
<div>
- <h2 className="font-display text-2xl font-semibold text-foreground">
- Official instances
+ <h2 className="mb-6 font-display text-2xl font-semibold text-foreground">
+ Official Instances
</h2>
- <p className="mt-2 mb-6 max-w-xl leading-relaxed text-muted-foreground">
- The archives I run. Anyone can run their own.
- </p>
<ul className={GRID}>
{official.map((site, i) => {
const figures = hubSummarySiteFor(summary, site);
@@ -209,7 +199,7 @@ export default function ArchiveShelf() {
{added.length > 0 && (
<>
<h2 className="font-display text-2xl font-semibold text-foreground">
- Archives you added
+ Archives You Added
</h2>
<ul className={`${GRID} mt-6 mb-6`}>
{added.map((site, i) => (
diff --git a/homepage/app/components/ArchiveCards.tsx b/homepage/app/components/ArchiveCards.tsx
@@ -2,7 +2,9 @@ import type { HomepageSummarySite } from "yt-dlp-transcript-common/lib/homepageS
import { seriesColor } from "yt-dlp-transcript-common/lib/homepageChart";
// The official instances: one card per public archive, its title linking out,
-// its own numbers underneath. The stripe is seriesColor(i), the same index the
+// its own numbers underneath. No description line under the title — the
+// page's copy rule is no subtitles unless needed, and the name plus the
+// numbers say what the archive is. The stripe is seriesColor(i), the same index the
// growth chart colours that instance's layer with, so a card is its own legend.
//
// A site's `accent` is deliberately NOT used here any more: the chart cannot
@@ -38,24 +40,17 @@ export function ArchiveCards({ sites }: { sites: HomepageSummarySite[] }) {
className="absolute left-0 top-0 h-full w-[3px]"
style={{ backgroundColor: seriesColor(i) }}
/>
- <div className="flex flex-col gap-1.5">
- <h3 className="font-display text-lg font-semibold text-[var(--foreground)]">
- <a
- href={site.siteUrl}
- target="_blank"
- rel="noopener noreferrer"
- className="underline decoration-transparent underline-offset-4 transition-colors hover:decoration-[var(--border-strong)]"
- >
- {site.siteTitle}
- <span aria-hidden="true" className="ml-1.5 text-sm text-[var(--faint)]">↗</span>
- </a>
- </h3>
- {site.siteDescription && (
- <p className="text-sm leading-relaxed text-[var(--muted-foreground)]">
- {site.siteDescription}
- </p>
- )}
- </div>
+ <h3 className="font-display text-lg font-semibold text-[var(--foreground)]">
+ <a
+ href={site.siteUrl}
+ target="_blank"
+ rel="noopener noreferrer"
+ className="underline decoration-transparent underline-offset-4 transition-colors hover:decoration-[var(--border-strong)]"
+ >
+ {site.siteTitle}
+ <span aria-hidden="true" className="ml-1.5 text-sm text-[var(--faint)]">↗</span>
+ </a>
+ </h3>
<dl className="mt-auto grid grid-cols-4 gap-3">
<Figure value={site.channels} unit="channels" />
<Figure value={site.recordings} unit="recordings" />
diff --git a/homepage/app/page.tsx b/homepage/app/page.tsx
@@ -13,7 +13,8 @@ import { currentHomepage } from "./lib/homepage";
// loadSummary(). A build with no data renders the words and the links only.
//
// Copy rules, kept deliberately: short declarative sentences, no eyebrow over
-// every band, no subtitles under the headings. The page has two headings after
+// every band, no subtitles under the headings or the cards, headings in Title
+// Case (the hub shelf matches). The page has two headings after
// the H1 and both name what is under them.
const CONTAINER = "w-full max-w-6xl mx-auto px-5 sm:px-6";
@@ -106,28 +107,25 @@ export default function Home() {
</section>
)}
- {/* ── Official instances ───────────────────────────────────────────── */}
+ {/* ── Official Instances ───────────────────────────────────────────── */}
{sites.length > 0 && (
<section className="border-t border-[var(--border)]">
<div className={`${CONTAINER} py-14 sm:py-20`}>
- <h2 className="font-display text-2xl font-semibold text-[var(--foreground)]">
- Official instances
+ <h2 className="mb-8 font-display text-2xl font-semibold text-[var(--foreground)]">
+ Official Instances
</h2>
- <p className="mt-2 mb-8 max-w-xl text-[var(--muted-foreground)] leading-relaxed">
- The archives I run. Anyone can run their own.
- </p>
<ArchiveCards sites={sites} />
</div>
</section>
)}
- {/* ── What it does ─────────────────────────────────────────────────── */}
+ {/* ── What It Does ─────────────────────────────────────────────────── */}
{/* Three short paragraphs, not three titled panels: without titles,
three one-sentence items read as prose, not as a feature grid. */}
<section className="border-t border-[var(--border)]">
<div className={`${CONTAINER} py-14 sm:py-20`}>
<h2 className="font-display text-2xl font-semibold text-[var(--foreground)]">
- What it does
+ What It Does
</h2>
<div className="mt-6 flex max-w-2xl flex-col gap-4 text-[1.0625rem] leading-relaxed text-[var(--muted-foreground)]">
<p>