From 3e0e92261e07de1ca4b0891cc6451fe704f1de1d Mon Sep 17 00:00:00 2001
From: "Yaroslav (Spokodev)"
Date: Thu, 9 Jul 2026 13:10:35 +0100
Subject: [PATCH] feat(home): make upstream proof-band logos clickable (expand
PRs in place)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The 12 brand logos on the home page looked clickable but weren't. Each is
now a button that expands that repo's merged-PR card in place — right on
the home page, no navigation and no scroll, so the visitor stays exactly
where they were. Mirrors the /upstream tile's expand.
- New components/upstream/pr-list.tsx: the merged-PR
lifted out of
brand-tile.tsx so the /upstream tiles and the home panel share one
source of truth for PR-row styling.
- New components/home/upstream-proof-marquee.tsx (client): logos become
buttons (aria-expanded/-controls, hover + focus-visible + active accent
highlight); a single panel below the grid expands the open repo's PRs
using the same grid-rows accordion + requestLenisResize as BrandTile.
displayedSlug lags openSlug so content stays mounted through the close.
- upstream-proof-band.tsx (server) now computes prsForRepo + prsCount
labels per brand and feeds the client marquee; counts/marquee unchanged.
typecheck + lint + build clean; 12 clickable buttons render with the
shared panel; /upstream tiles unaffected (pure extraction).
Co-Authored-By: Claude Opus 4.8 (1M context)
---
components/home/upstream-proof-band.tsx | 45 ++++---
components/home/upstream-proof-marquee.tsx | 138 +++++++++++++++++++++
components/upstream/brand-tile.tsx | 39 ++----
components/upstream/pr-list.tsx | 52 ++++++++
4 files changed, 218 insertions(+), 56 deletions(-)
create mode 100644 components/home/upstream-proof-marquee.tsx
create mode 100644 components/upstream/pr-list.tsx
diff --git a/components/home/upstream-proof-band.tsx b/components/home/upstream-proof-band.tsx
index 9dccff6..fcb7170 100644
--- a/components/home/upstream-proof-band.tsx
+++ b/components/home/upstream-proof-band.tsx
@@ -1,8 +1,11 @@
import { getTranslations } from "next-intl/server";
import { ArrowRight } from "lucide-react";
import { Link } from "@/i18n/navigation";
-import { aggregates, findRepo, type UpstreamRepo } from "@/lib/upstream";
-import { BrandIcon } from "@/components/upstream/brand-icon";
+import { aggregates, findRepo, prsForRepo } from "@/lib/upstream";
+import {
+ UpstreamProofMarquee,
+ type MarqueeBrand,
+} from "@/components/home/upstream-proof-marquee";
/**
* Home trust band — promotes the /upstream OSS proof (merged PRs into the
@@ -31,14 +34,21 @@ const MARQUEE: { slug: string; label: string }[] = [
export async function UpstreamProofBand({ locale }: { locale: string }) {
const t = await getTranslations({ locale, namespace: "home.upstream" });
+ const tu = await getTranslations({ locale, namespace: "upstream" });
const agg = aggregates();
const brands = MARQUEE.map((m) => {
const repo = findRepo(m.slug);
- return repo ? { ...m, repo } : null;
- }).filter(
- (b): b is { slug: string; label: string; repo: UpstreamRepo } => b !== null,
- );
+ if (!repo) return null;
+ const prs = prsForRepo(m.slug);
+ return {
+ slug: m.slug,
+ label: m.label,
+ repo,
+ prs,
+ prsCountLabel: tu("tile.prsCount", { count: prs.length }),
+ };
+ }).filter((b): b is MarqueeBrand => b !== null);
return (
@@ -61,24 +71,11 @@ export async function UpstreamProofBand({ locale }: { locale: string }) {
{t("sub", { prs: agg.totalPrs, repos: agg.totalRepos })}
diff --git a/components/upstream/pr-list.tsx b/components/upstream/pr-list.tsx
new file mode 100644
index 0000000..b52ad02
--- /dev/null
+++ b/components/upstream/pr-list.tsx
@@ -0,0 +1,52 @@
+import { ExternalLink } from "lucide-react";
+import type { UpstreamPr } from "@/lib/upstream-schema";
+
+/**
+ * Shared merged-PR list — the expandable body used by both the `/upstream`
+ * brand tiles and the home proof-band panel. One source of truth for the
+ * PR-row styling so the two never drift.
+ */
+export function UpstreamPrList({
+ prs,
+ locale,
+ viewPrLabel,
+}: {
+ prs: readonly UpstreamPr[];
+ locale: string;
+ viewPrLabel: string;
+}) {
+ const df = new Intl.DateTimeFormat(locale, { dateStyle: "medium" });
+
+ return (
+