Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions src/app/benchmarks/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -463,12 +463,15 @@ export default async function BenchmarkPage({
{benchmark.subtitle}
</p>

{/* Wikipedia-style infobox. Floats right on desktop next to the
TL;DR and the intro copy, stacks above on mobile. Table markup
+ microdata make the key/value pairs extractable by LLM
crawlers verbatim (Perplexity, Gemini, ChatGPT-with-web all
hoist this format from Wikipedia pages when composing answers)
while giving readers a two second scan of the headline facts. */}
{/* Compact "At a glance" card. Rendered as a native <details>
element so it collapses to a one line summary on click while
still shipping the full microdata + key/value pairs inside the
collapsed body (LLM crawlers see the content whatever the
visual state). Sits between the subtitle and the TL;DR
grounding trace so a reader has: headline claim (H1) → context
(subtitle) → at-a-glance facts (this card) → canonical
grounding sentence (TL;DR) before entering the leaderboard
body. */}
<BenchInfobox benchmark={benchmark} />

{/* Visible grounding-trace TL;DR. Renders the same canonical line
Expand Down
168 changes: 109 additions & 59 deletions src/components/bench-infobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,24 @@ import { fmtUnit } from "@/lib/format";
import { SITE } from "@/data/site";

/**
* Wikipedia-style infobox for benchmark pages. A compact `<table>` of
* key/value pairs that sits directly under the H1/subtitle and above the
* TL;DR grounding trace.
* Compact "At a glance" card that sits under the H1/subtitle on every
* benchmark page. Wrapped in a native <details> element so it collapses
* to a one-line summary and expands to the full key/value grid on click.
*
* Why this shape:
* - LLM crawlers (Perplexity, Gemini, ChatGPT-with-web) extract Wikipedia
* infoboxes almost verbatim because they are `<table>` markup with
* `<th>`/`<td>` label/value pairs sitting above the fold, before any
* prose. Copying the shape is the single highest-leverage change
* identified in the July 2026 GEO deep-dive.
* - Users get a two-second scan of the key facts (leader, metric, last
* measured, license) without scrolling into the leaderboard body, so
* the UX gain is real and not just an SEO trick.
* - `itemscope` + `itemtype="https://schema.org/Dataset"` layers a
* microdata graph over the DOM in addition to the JSON-LD block, which
* some crawlers prefer.
* - Language models parse the content inside <details> whether it is
* visually expanded or not, so the LLM-grounding pattern (Wikipedia
* infobox-style key/value pairs directly under the H1) is preserved
* in either state.
* - Human readers get a scannable one-line teaser that a click reveals
* into a compact card. The default is expanded so first-time visitors
* see the facts without having to interact.
* - itemscope + itemtype layer a schema.org Dataset microdata graph
* over the DOM in addition to the JSON-LD block so crawlers that
* parse microdata pick up the same facts.
*
* Hidden gracefully when the bench has no defensible leader (draft,
* insufficient, awaiting first run) so the rendered box never publishes
* a fabricated leader row.
* Renders nothing when the bench has no defensible leader (draft,
* insufficient, awaiting) so we never publish a fabricated leader row.
*/
export function BenchInfobox({ benchmark }: { benchmark: Benchmark }) {
const top = leader(benchmark);
Expand All @@ -33,54 +31,103 @@ export function BenchInfobox({ benchmark }: { benchmark: Benchmark }) {
const lastRunIso = benchmark.lastRunAt
? new Date(benchmark.lastRunAt).toISOString()
: null;
const lastRunDisplay = lastRunIso ? lastRunIso.replace("T", " ").slice(0, 19) + " UTC" : "n/a";
const lastRunDisplay = lastRunIso
? lastRunIso.replace("T", " ").slice(0, 19) + " UTC"
: "n/a";

return (
<aside
aria-label={`${benchmark.title} at a glance`}
className="mt-6 max-w-md sm:float-right sm:ml-6 sm:mb-4 w-full sm:w-[22rem]"
className="mt-6 max-w-3xl"
itemScope
itemType="https://schema.org/Dataset"
>
<table
className="w-full text-[12.5px] border-collapse card-soft rounded-lg overflow-hidden"
style={{ borderColor: "var(--color-ink-10, rgba(0,0,0,.08))" }}
<details
open
className="group card-soft rounded-lg border border-ink/10 overflow-hidden [&_summary::-webkit-details-marker]:hidden"
>
<caption className="sr-only" itemProp="name">
{benchmark.title} at a glance
</caption>
<tbody>
<InfoRow label="Benchmark">
{/* Summary. one line teaser with a chevron, always visible.
Click toggles the expanded body. When collapsed the leader
claim stays surfaced in the summary so the "quick answer"
never requires interaction to find. */}
<summary
className="flex items-center justify-between gap-3 px-4 py-2.5 cursor-pointer select-none list-none hover:bg-paper-soft/50 transition-colors"
aria-controls="benchmark-infobox-body"
>
<span className="flex items-center gap-2 min-w-0">
<span
className="text-[10px] font-medium uppercase tracking-[0.14em] text-ink-faint"
style={{ fontFamily: "var(--font-mono, monospace)" }}
>
At a glance
</span>
{top && (
<span className="text-[12px] text-ink-muted truncate">
<span className="text-ink-faint">·</span>{" "}
<span className="text-ink font-medium">{top.name}</span>{" "}
<span className="text-ink-faint">
{fmtUnit(top.value, benchmark.unit)}
</span>
</span>
)}
</span>
<svg
aria-hidden
width="14"
height="14"
viewBox="0 0 20 20"
fill="none"
className="shrink-0 text-ink-faint transition-transform duration-200 group-open:rotate-180"
>
<path
d="M5 7.5L10 12.5L15 7.5"
stroke="currentColor"
strokeWidth="1.6"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</summary>
{/* Body. grid of compact key/value pairs. Two columns on
desktop, single column on mobile. Keeps type + spacing tight
so the box does not compete with the leaderboard body below
for attention. */}
<div
id="benchmark-infobox-body"
className="border-t border-ink/8 px-4 py-3 grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-2 text-[12.5px]"
>
<InfoPair label="Benchmark">
<span itemProp="identifier">#{benchmark.number}</span>
{" · "}
<span className="text-ink-faint">{benchmark.category}</span>
</InfoRow>
<InfoRow label="Metric">
<span className="text-ink-faint"> · {benchmark.category}</span>
</InfoPair>
<InfoPair label="Metric">
<span itemProp="variableMeasured">{benchmark.metric}</span>
{benchmark.unit ? (
<span className="text-ink-faint"> ({benchmark.unit})</span>
) : null}
</InfoRow>
</InfoPair>
{top && (
<InfoRow label="Leader (24h)">
<InfoPair label="Leader (24h)">
<span className="font-medium text-ink">{top.name}</span>
<span className="text-ink-faint">
{" · "}
{fmtUnit(top.value, benchmark.unit)}
{" "}
· {fmtUnit(top.value, benchmark.unit)}
</span>
</InfoRow>
</InfoPair>
)}
<InfoRow label="Providers">
{providerCount} tracked
</InfoRow>
<InfoPair label="Providers">{providerCount} tracked</InfoPair>
{lastRunIso && (
<InfoRow label="Last measured">
<time dateTime={lastRunIso} itemProp="dateModified">
<InfoPair label="Last measured">
<time
dateTime={lastRunIso}
itemProp="dateModified"
className="tabular-nums"
>
{lastRunDisplay}
</time>
</InfoRow>
</InfoPair>
)}
<InfoRow label="License">
<InfoPair label="License">
<a
href="https://creativecommons.org/licenses/by/4.0/"
className="underline decoration-ink/20 hover:decoration-ink"
Expand All @@ -89,8 +136,8 @@ export function BenchInfobox({ benchmark }: { benchmark: Benchmark }) {
>
CC-BY-4.0
</a>
</InfoRow>
<InfoRow label="Dataset DOI">
</InfoPair>
<InfoPair label="Dataset DOI">
<a
href="https://doi.org/10.5281/zenodo.20800312"
className="underline decoration-ink/20 hover:decoration-ink tabular-nums"
Expand All @@ -99,40 +146,43 @@ export function BenchInfobox({ benchmark }: { benchmark: Benchmark }) {
>
10.5281/zenodo.20800312
</a>
</InfoRow>
<InfoRow label="Live JSON">
</InfoPair>
<InfoPair label="Live JSON" className="sm:col-span-2">
<a
href={`${SITE.url}/api/stat/${benchmark.slug}`}
className="underline decoration-ink/20 hover:decoration-ink font-mono text-[11.5px]"
className="underline decoration-ink/20 hover:decoration-ink font-mono text-[11.5px] break-all"
rel="noopener"
itemProp="distribution"
>
/api/stat/{benchmark.slug}
</a>
</InfoRow>
</tbody>
</table>
</InfoPair>
</div>
</details>
</aside>
);
}

function InfoRow({
function InfoPair({
label,
children,
className,
}: {
label: string;
children: React.ReactNode;
className?: string;
}) {
return (
<tr className="border-t border-ink/5 first:border-t-0">
<th
scope="row"
className="w-[9.5rem] px-3 py-2 text-left font-medium uppercase tracking-wide text-[10.5px] text-ink-faint bg-paper-soft/50"
<div
className={`flex items-baseline gap-2 py-1 border-b border-ink/5 last:border-b-0 sm:border-b-0 ${className ?? ""}`}
>
<span
className="shrink-0 text-[10px] font-medium uppercase tracking-[0.14em] text-ink-faint w-[6.5rem]"
style={{ fontFamily: "var(--font-mono, monospace)" }}
>
{label}
</th>
<td className="px-3 py-2 text-ink">{children}</td>
</tr>
</span>
<span className="text-ink min-w-0 flex-1">{children}</span>
</div>
);
}
Loading