feat(stats): add clickable section headings (#34095)

This commit is contained in:
Adam 2026-06-26 12:34:44 -05:00 committed by GitHub
parent 7b1fe33ed3
commit 82f47cb312
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 169 additions and 45 deletions

View file

@ -30,6 +30,7 @@ import {
type ModelCatalogCost,
type ModelCatalogEntry,
} from "../model-catalog"
import { SectionHeading } from "../section-heading"
import { runStatsEffect } from "../../stats-runtime"
import { setStatsPageCacheHeaders } from "../stats-cache"
import {
@ -205,7 +206,11 @@ function ModelLoading() {
<a data-slot="model-back-link" href={language.route(import.meta.env.BASE_URL)}>
{i18n.t("footer.modelData")}
</a>
<h1>{i18n.t("model.loadingTitle")}</h1>
<h1>
<a data-slot="heading-link" href="#overview">
{i18n.t("model.loadingTitle")}
</a>
</h1>
<p>{i18n.t("model.loadingDescription")}</p>
</div>
</div>
@ -228,7 +233,11 @@ function ModelNotFound(props: { lab: string; model: string }) {
<a data-slot="model-back-link" href={language.route(import.meta.env.BASE_URL)}>
{i18n.t("footer.modelData")}
</a>
<h1>{props.model || i18n.t("model.fallback")}</h1>
<h1>
<a data-slot="heading-link" href="#overview">
{props.model || i18n.t("model.fallback")}
</a>
</h1>
<p>{i18n.t("model.noMatched", { id: props.lab ? `${props.lab}/${props.model}` : props.model })}</p>
</div>
</div>
@ -260,7 +269,11 @@ function ModelHero(props: { data: StatsModelData | null; catalog: ModelCatalogEn
</a>
<span data-slot="model-id-tag">{modelId()}</span>
</div>
<h1>{props.catalog?.name ?? props.data?.model ?? i18n.t("model.fallback")}</h1>
<h1>
<a data-slot="heading-link" href="#overview">
{props.catalog?.name ?? props.data?.model ?? i18n.t("model.fallback")}
</a>
</h1>
<Show when={props.data} fallback={<p>{i18n.t("model.catalogFallback")}</p>}>
{(data) => (
<p>
@ -352,8 +365,12 @@ function CatalogDatum(props: { label: string; value: string }) {
function ModelOverview(props: { data: StatsModelData | null }) {
const i18n = useI18n()
return (
<section data-section="model-panel">
<SectionTitle title={i18n.t("nav.overview")} description={i18n.t("model.overviewDescription")} />
<section id="model-overview" data-section="model-panel">
<SectionTitle
href="#model-overview"
title={i18n.t("nav.overview")}
description={i18n.t("model.overviewDescription")}
/>
<Show
when={props.data}
fallback={
@ -399,7 +416,7 @@ function ModelUsageSection(props: { data: ModelUsagePoint[] }) {
const i18n = useI18n()
return (
<section id="usage" data-section="model-panel">
<SectionTitle title={i18n.t("nav.usage")} description={i18n.t("model.usageDescription")} />
<SectionTitle href="#usage" title={i18n.t("nav.usage")} description={i18n.t("model.usageDescription")} />
<Show
when={props.data.some((item) => item.tokens > 0)}
fallback={
@ -416,7 +433,7 @@ function ModelUsersSection(props: { data: ModelUsagePoint[] }) {
const i18n = useI18n()
return (
<section id="users" data-section="model-panel">
<SectionTitle title={i18n.t("model.uniqueUsers")} description={i18n.t("model.usersDescription")} />
<SectionTitle href="#users" title={i18n.t("model.uniqueUsers")} description={i18n.t("model.usersDescription")} />
<Show
when={props.data.some((item) => item.users > 0)}
fallback={
@ -550,7 +567,11 @@ function ModelEfficiencySection(props: { data: StatsModelData | null; catalog: M
const i18n = useI18n()
return (
<section id="efficiency" data-section="model-panel">
<SectionTitle title={i18n.t("nav.efficiency")} description={i18n.t("model.efficiencyDescription")} />
<SectionTitle
href="#efficiency"
title={i18n.t("nav.efficiency")}
description={i18n.t("model.efficiencyDescription")}
/>
<Show
when={props.data}
fallback={
@ -623,7 +644,11 @@ function ModelGeoBreakdownSection(props: { data: Record<UsageRange, CountryEntry
setActiveCountry(undefined)
}}
>
<SectionTitle title={i18n.t("nav.geoBreakdown")} description={i18n.t("model.geoDescription")} />
<SectionTitle
href="#geo-breakdown"
title={i18n.t("nav.geoBreakdown")}
description={i18n.t("model.geoDescription")}
/>
<Show
when={data().length > 0}
fallback={<ModelEmptyState title={i18n.t("model.noGeoTitle")} description={i18n.t("model.noGeoDescription")} />}
@ -788,7 +813,7 @@ function ModelPeersSection(props: { data: StatsModelData | null }) {
const i18n = useI18n()
return (
<section id="peers" data-section="model-panel">
<SectionTitle title={i18n.t("nav.peers")} description={i18n.t("model.peersDescription")} />
<SectionTitle href="#peers" title={i18n.t("nav.peers")} description={i18n.t("model.peersDescription")} />
<Show
when={props.data?.peers.length}
fallback={
@ -833,12 +858,8 @@ function PeerRow(props: { peer: ModelPeerEntry; active: boolean }) {
)
}
function SectionTitle(props: { title: string; description: string }) {
return (
<p data-slot="section-title">
<strong>{props.title}.</strong> <span>{props.description}</span>
</p>
)
function SectionTitle(props: { href: string; title: string; description: string }) {
return <SectionHeading href={props.href} title={props.title} description={props.description} />
}
function ModelEmptyState(props: { title: string; description: string; compact?: boolean }) {

View file

@ -20,6 +20,7 @@ import {
type ModelCatalogEntry,
type ModelCatalogLab,
} from "../model-catalog"
import { SectionHeading } from "../section-heading"
import { runStatsEffect } from "../../stats-runtime"
import { setStatsPageCacheHeaders } from "../stats-cache"
import {
@ -148,7 +149,11 @@ function LabLoading() {
<a data-slot="model-back-link" href={language.route(import.meta.env.BASE_URL)}>
{i18n.t("footer.modelData")}
</a>
<h1>{i18n.t("lab.loadingTitle")}</h1>
<h1>
<a data-slot="heading-link" href="#overview">
{i18n.t("lab.loadingTitle")}
</a>
</h1>
<p>{i18n.t("lab.loadingDescription")}</p>
</div>
</div>
@ -166,7 +171,11 @@ function LabNotFound(props: { lab: string }) {
<a data-slot="model-back-link" href={language.route(import.meta.env.BASE_URL)}>
{i18n.t("footer.modelData")}
</a>
<h1>{formatCatalogLabName(props.lab)}</h1>
<h1>
<a data-slot="heading-link" href="#overview">
{formatCatalogLabName(props.lab)}
</a>
</h1>
<p>{i18n.t("lab.notFound")}</p>
</div>
</div>
@ -193,7 +202,11 @@ function LabHero(props: { lab: ModelCatalogLab; stats: StatsLabData | null }) {
</a>
<div data-slot="model-hero-grid">
<div data-slot="model-hero-copy">
<h1>{props.lab.name}</h1>
<h1>
<a data-slot="heading-link" href="#overview">
{props.lab.name}
</a>
</h1>
<div data-slot="model-hero-pattern" aria-hidden="true" />
<p>
{i18n.t("lab.heroPrefix", { count: props.lab.models.length, lab: props.lab.name })}
@ -235,10 +248,11 @@ function LabUsageSection(props: { lab: ModelCatalogLab; data: StatsLabData | nul
return (
<section id="usage" data-section="model-panel">
<p data-slot="section-title">
<strong>{i18n.t("lab.usageTitle", { lab: props.lab.name })}.</strong>{" "}
<span>{i18n.t("lab.usageDescription")}</span>
</p>
<SectionHeading
href="#usage"
title={i18n.t("lab.usageTitle", { lab: props.lab.name })}
description={i18n.t("lab.usageDescription")}
/>
<Show
when={usage().some((item) => item.tokens > 0)}
fallback={<LabEmptyState title={i18n.t("lab.noUsageTitle")} description={i18n.t("lab.noUsageDescription")} />}
@ -336,10 +350,11 @@ function LabModelsSection(props: { lab: ModelCatalogLab; usage: LabUsageModelEnt
const usageBySlug = createMemo(() => new Map(props.usage.map((item) => [item.slug, item])))
return (
<section id="models" data-section="model-panel">
<p data-slot="section-title">
<strong>{i18n.t("lab.modelsTitle", { lab: props.lab.name })}.</strong>{" "}
<span>{i18n.t("lab.recentUsageAndLimits")}</span>
</p>
<SectionHeading
href="#models"
title={i18n.t("lab.modelsTitle", { lab: props.lab.name })}
description={i18n.t("lab.recentUsageAndLimits")}
/>
<div data-component="lab-model-grid">
<For each={props.lab.models}>
{(model) => <LabModelCard model={model} usage={usageBySlug().get(model.slug)} />}

View file

@ -87,6 +87,11 @@
display: none !important;
}
[data-page="stats"] section[id],
[data-page="stats"] [data-component="leaderboard"][id] {
scroll-margin-top: 88px;
}
[data-page="stats"] [data-component="content"] {
color: var(--stats-text);
font-family:
@ -1781,6 +1786,35 @@
font-weight: 400;
}
[data-page="stats"] [data-slot="heading-link"] {
position: relative;
color: inherit;
text-decoration: none;
}
[data-page="stats"] [data-slot="heading-link"]:hover {
text-decoration: none;
}
[data-page="stats"] [data-slot="heading-link"]:focus-visible {
outline: 1px solid var(--stats-accent);
outline-offset: 4px;
}
[data-page="stats"] [data-slot="heading-anchor"] {
position: absolute;
top: -0.08em;
right: 100%;
margin-right: 0.48em;
color: var(--stats-accent);
opacity: 0;
}
[data-page="stats"] [data-slot="heading-link"]:hover [data-slot="heading-anchor"],
[data-page="stats"] [data-slot="heading-link"]:focus-visible [data-slot="heading-anchor"] {
opacity: 1;
}
[data-page="stats"] [data-component="leaderboard"],
[data-page="stats"] [data-slot="leaderboard-featured"],
[data-page="stats"] [data-slot="leaderboard-compact"],

View file

@ -32,6 +32,7 @@ import { useI18n } from "../context/i18n"
import { useLanguage } from "../context/language"
import { localizedUrl } from "../lib/language"
import { findModelCatalogEntry, getModelCatalog, type ModelCatalog } from "./model-catalog"
import { SectionHeading } from "./section-heading"
import { setStatsPageCacheHeaders } from "./stats-cache"
import {
applyThemePreference,
@ -273,7 +274,11 @@ function Hero(props: { updatedAt: string | null }) {
</p>
<div data-slot="hero-canvas">
<div data-slot="hero-pattern" aria-hidden="true" />
<h1>{i18n.t("footer.modelData")}</h1>
<h1>
<a data-slot="heading-link" href="#overview">
{i18n.t("footer.modelData")}
</a>
</h1>
<p data-slot="hero-copy">{i18n.t("home.heroCopy")}</p>
</div>
</section>
@ -296,7 +301,7 @@ function StatsLoading() {
return (
<>
<Hero updatedAt={null} />
<ChartSection title={i18n.t("home.usageTitle")}>
<ChartSection id="top-models" title={i18n.t("home.usageTitle")}>
<EmptyState title={i18n.t("home.loadingTitle")} description={i18n.t("home.loadingDescription")} />
</ChartSection>
</>
@ -314,7 +319,15 @@ function ChartSection(props: {
<section id={props.id} data-section="chart">
<div data-slot="section-header">
<div>
<h2>{props.title}</h2>
<h2>
<Show when={props.id} fallback={props.title}>
{(id) => (
<a data-slot="heading-link" href={`#${id()}`}>
{props.title}
</a>
)}
</Show>
</h2>
{props.description && <p>{props.description}</p>}
</div>
{props.controls}
@ -324,12 +337,8 @@ function ChartSection(props: {
)
}
function SectionTitle(props: { title: string; description: string }) {
return (
<p data-slot="section-title">
<strong>{props.title}.</strong> <span>{props.description}</span>
</p>
)
function SectionTitle(props: { id: string; title: string; description: string }) {
return <SectionHeading href={`#${props.id}`} title={props.title} description={props.description} />
}
function SectionBridge(props: { label: string; href: string }) {
@ -405,9 +414,13 @@ function TopModelsSection(props: { data: StatsHomeData["usage"]; leaderboard: St
return (
<section id="top-models" data-section="top-models">
<h2 data-slot="top-models-title">
<strong>{i18n.t("nav.topModels")}.</strong> <span>{i18n.t("home.topModelsDescription")}</span>
</h2>
<SectionHeading
as="h2"
slot="top-models-title"
href="#top-models"
title={i18n.t("nav.topModels")}
description={i18n.t("home.topModelsDescription")}
/>
<Show
when={data().some((item) => usageTotal(item) > 0)}
fallback={<EmptyState title={i18n.t("home.noUsageTitle")} description={i18n.t("home.noUsageDescription")} />}
@ -802,7 +815,11 @@ function UniqueUsersSection(props: { data: StatsHomeData["users"] }) {
return (
<section id="unique-users" data-section="unique-users">
<SectionBridge label={i18n.t("nav.topModels").toUpperCase()} href="#top-models" />
<SectionTitle title={i18n.t("home.uniqueUsersTitle")} description={i18n.t("home.uniqueUsersDescription")} />
<SectionTitle
id="unique-users"
title={i18n.t("home.uniqueUsersTitle")}
description={i18n.t("home.uniqueUsersDescription")}
/>
<Show
when={data().some((item) => usageTotal(item) > 0)}
fallback={
@ -1073,7 +1090,11 @@ function MarketShareSection(props: { data: StatsHomeData["market"] }) {
}}
>
<SectionBridge label={i18n.t("nav.cacheRatio").toUpperCase()} href="#cache-ratio" />
<SectionTitle title={i18n.t("home.marketShareTitle")} description={i18n.t("home.marketShareDescription")} />
<SectionTitle
id="market-share"
title={i18n.t("home.marketShareTitle")}
description={i18n.t("home.marketShareDescription")}
/>
<Show
when={activeDay()}
fallback={<EmptyState title={i18n.t("home.noMarketTitle")} description={i18n.t("home.noMarketDescription")} />}
@ -1298,7 +1319,7 @@ function GeoBreakdownSection(props: { data: StatsHomeData["country"] }) {
}}
>
<SectionBridge label={i18n.t("nav.marketShare").toUpperCase()} href="#market-share" />
<SectionTitle title={i18n.t("home.geoTitle")} description={i18n.t("home.geoDescription")} />
<SectionTitle id="geo-breakdown" title={i18n.t("home.geoTitle")} description={i18n.t("home.geoDescription")} />
<Show
when={data().length > 0}
fallback={<EmptyState title={i18n.t("home.noGeoTitle")} description={i18n.t("home.noGeoDescription")} />}
@ -1583,7 +1604,11 @@ function TokenCostSection(props: { data: StatsHomeData["tokenCost"]; catalog: Mo
return (
<section id="token-cost" data-section="token-cost">
<SectionBridge label={i18n.t("nav.sessionCost").toUpperCase()} href="#session-cost" />
<SectionTitle title={i18n.t("home.tokenCostTitle")} description={i18n.t("home.tokenCostDescription")} />
<SectionTitle
id="token-cost"
title={i18n.t("home.tokenCostTitle")}
description={i18n.t("home.tokenCostDescription")}
/>
<Show
when={visible().length > 0}
fallback={
@ -1666,7 +1691,11 @@ function CacheRatioSection(props: { data: StatsHomeData["cacheRatio"] }) {
return (
<section id="cache-ratio" data-section="cache-ratio">
<SectionBridge label={i18n.t("nav.tokenCost").toUpperCase()} href="#token-cost" />
<SectionTitle title={i18n.t("home.cacheRatioTitle")} description={i18n.t("home.cacheRatioDescription")} />
<SectionTitle
id="cache-ratio"
title={i18n.t("home.cacheRatioTitle")}
description={i18n.t("home.cacheRatioDescription")}
/>
<Show
when={visible().length > 0}
fallback={<EmptyState title={i18n.t("home.noCacheTitle")} description={i18n.t("home.noCacheDescription")} />}
@ -1792,7 +1821,11 @@ function SessionCostSection(props: { data: StatsHomeData["sessionCost"] }) {
return (
<section id="session-cost" data-section="session-cost">
<SectionBridge label={i18n.t("nav.topModels").toUpperCase()} href="#top-models" />
<SectionTitle title={i18n.t("home.sessionCostTitle")} description={i18n.t("home.sessionCostDescription")} />
<SectionTitle
id="session-cost"
title={i18n.t("home.sessionCostTitle")}
description={i18n.t("home.sessionCostDescription")}
/>
<Show
when={visible().length > 0}
fallback={

View file

@ -0,0 +1,21 @@
export function SectionHeading(props: {
href: string
title: string
description: string
as?: "h2" | "p"
slot?: string
}) {
const content = (
<>
<strong>
<a data-slot="heading-link" href={props.href}>
<span data-slot="heading-anchor" aria-hidden="true">#</span>{props.title}.
</a>
</strong>{" "}
<span>{props.description}</span>
</>
)
if (props.as === "h2") return <h2 data-slot={props.slot ?? "section-title"}>{content}</h2>
return <p data-slot={props.slot ?? "section-title"}>{content}</p>
}