From fd07d198fd522a8dccafb4856678730122aa532a Mon Sep 17 00:00:00 2001 From: Liang Juhao Date: Fri, 15 May 2026 14:57:16 +0800 Subject: [PATCH 01/21] refactor(leaderboard): scaffold v2 UI with module split and Home page Splits the legacy 2300-line index.html into ES modules (assets/css/* + assets/js/*) and introduces a hash router so each view lives in its own file. This commit only realises the Home page (suite overview grid + top-3 podiums + recent submissions). Rankings, Compare, Chip detail and Suites views are scaffolded with informative placeholders so the router never errors; full implementations land in follow-ups (commit 2: rankings + compare, 3: chip detail, 4: polish). Notable bits: - assets/css/{base,layout,components,home}.css design-token layer. - assets/js/data.js single source of truth for SUITE_META, primary- metric direction/scale/unit rules, and row indexing. - formatPrimary(value, suiteId) centralises display rules so suite quirks (e.g. Suite E ratios 0-1 rendered as %) only live in data.js. - assets/js/router.js minimal hash router with compare-basket holder. - leaderboard/generate.py exposes window.LEADERBOARD_DATA so ES modules can read the generator output (a classic-script `const` is not visible to modules). - Old style.css removed (unused since the current dark-theme UI inlined styles into index.html). Co-authored-by: Cursor --- leaderboard/generate.py | 3 + leaderboard/site/assets/css/base.css | 95 + leaderboard/site/assets/css/components.css | 205 ++ leaderboard/site/assets/css/home.css | 141 + leaderboard/site/assets/css/layout.css | 148 + leaderboard/site/assets/js/data.js | 216 ++ leaderboard/site/assets/js/main.js | 54 + leaderboard/site/assets/js/router.js | 119 + leaderboard/site/assets/js/utils.js | 136 + .../site/assets/js/views/chip-detail.js | 58 + leaderboard/site/assets/js/views/compare.js | 14 + leaderboard/site/assets/js/views/home.js | 152 ++ leaderboard/site/assets/js/views/rankings.js | 18 + leaderboard/site/assets/js/views/suites.js | 49 + leaderboard/site/index.html | 2423 +---------------- leaderboard/site/style.css | 92 - 16 files changed, 1466 insertions(+), 2457 deletions(-) create mode 100644 leaderboard/site/assets/css/base.css create mode 100644 leaderboard/site/assets/css/components.css create mode 100644 leaderboard/site/assets/css/home.css create mode 100644 leaderboard/site/assets/css/layout.css create mode 100644 leaderboard/site/assets/js/data.js create mode 100644 leaderboard/site/assets/js/main.js create mode 100644 leaderboard/site/assets/js/router.js create mode 100644 leaderboard/site/assets/js/utils.js create mode 100644 leaderboard/site/assets/js/views/chip-detail.js create mode 100644 leaderboard/site/assets/js/views/compare.js create mode 100644 leaderboard/site/assets/js/views/home.js create mode 100644 leaderboard/site/assets/js/views/rankings.js create mode 100644 leaderboard/site/assets/js/views/suites.js delete mode 100644 leaderboard/site/style.css diff --git a/leaderboard/generate.py b/leaderboard/generate.py index 65bfbdd0..8c66687e 100644 --- a/leaderboard/generate.py +++ b/leaderboard/generate.py @@ -1084,7 +1084,10 @@ def main(): out_path = SITE_DIR / "leaderboard.js" with open(out_path, "w") as f: f.write("// Auto-generated by leaderboard/generate.py. Do not edit manually.\n") + # window.LEADERBOARD_DATA so ES modules (assets/js/data.js) can read it. + # Also exposed as bare LEADERBOARD_DATA for any legacy classic-script consumers. f.write(f"const LEADERBOARD_DATA = {json.dumps(rows, indent=2)};\n") + f.write("window.LEADERBOARD_DATA = LEADERBOARD_DATA;\n") print(f"Leaderboard data written to {out_path} ({len(rows)} rows).") generate_api(results, SITE_DIR) diff --git a/leaderboard/site/assets/css/base.css b/leaderboard/site/assets/css/base.css new file mode 100644 index 00000000..2812f477 --- /dev/null +++ b/leaderboard/site/assets/css/base.css @@ -0,0 +1,95 @@ +/* base.css — design tokens, reset, typography */ + +:root { + /* Surfaces */ + --bg: #0d1117; + --bg-elev: #161b22; + --bg-elev-2: #1c2128; + --border: #30363d; + --border-soft: #21262d; + + /* Text */ + --fg: #e6edf3; + --fg-strong: #f0f6fc; + --fg-muted: #8b949e; + --fg-faint: #6e7681; + + /* Accents */ + --accent: #58a6ff; + --accent-2: #1f6feb; + --accent-soft: rgba(88, 166, 255, 0.12); + + --good: #3fb950; + --warn: #d29922; + --bad: #f85149; + --gold: #e3b341; + --silver: #adb5bd; + --bronze: #cd7f32; + + /* Vendor palette (used for chips/badges) */ + --v-nvidia: #76b900; + --v-amd: #ed1c24; + --v-apple: #a1a1aa; + --v-google: #4285f4; + --v-huawei: #ff0000; + --v-moore: #c084fc; + --v-intel: #0071c5; + + /* Radii / shadows */ + --r-sm: 4px; + --r-md: 6px; + --r-lg: 10px; + --shadow-sm: 0 1px 2px rgba(0,0,0,0.5); + --shadow-md: 0 4px 16px rgba(0,0,0,0.4); + + /* Layout */ + --max-w: 1400px; + --gap: 1rem; + --gap-lg: 1.5rem; + + /* Type */ + --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; +} + +*, *::before, *::after { box-sizing: border-box; } + +html, body { margin: 0; padding: 0; } + +body { + font-family: var(--font-sans); + background: var(--bg); + color: var(--fg); + min-height: 100vh; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +a { color: var(--accent); text-decoration: none; } +a:hover { text-decoration: underline; } + +button { + font-family: inherit; + cursor: pointer; +} + +::selection { background: var(--accent-soft); color: var(--fg-strong); } + +/* Scrollbar (webkit only) */ +::-webkit-scrollbar { width: 10px; height: 10px; } +::-webkit-scrollbar-track { background: var(--bg); } +::-webkit-scrollbar-thumb { background: var(--border); border-radius: 5px; } +::-webkit-scrollbar-thumb:hover { background: var(--fg-faint); } + +/* Utility */ +.muted { color: var(--fg-muted); } +.faint { color: var(--fg-faint); } +.mono { font-family: var(--font-mono); } +.text-xs { font-size: 0.75rem; } +.text-sm { font-size: 0.85rem; } +.text-lg { font-size: 1.1rem; } +.text-xl { font-size: 1.35rem; } +.text-2xl { font-size: 1.8rem; } +.bold { font-weight: 600; } +.no-wrap { white-space: nowrap; } +.hide-mobile { /* set in layout.css responsive block */ } diff --git a/leaderboard/site/assets/css/components.css b/leaderboard/site/assets/css/components.css new file mode 100644 index 00000000..cbd6855d --- /dev/null +++ b/leaderboard/site/assets/css/components.css @@ -0,0 +1,205 @@ +/* components.css — buttons, cards, badges, tags, bars */ + +/* ── Cards ────────────────────────────────────────────────── */ +.card { + background: var(--bg-elev); + border: 1px solid var(--border-soft); + border-radius: var(--r-lg); + padding: 1.1rem 1.2rem; + transition: border-color 120ms ease, transform 120ms ease; +} +.card.is-link { + cursor: pointer; + display: block; + color: inherit; +} +.card.is-link:hover { + border-color: var(--accent); + text-decoration: none; +} + +/* ── Buttons ──────────────────────────────────────────────── */ +.btn { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.45rem 0.9rem; + border-radius: var(--r-md); + font-size: 0.85rem; + font-weight: 500; + background: var(--bg-elev); + color: var(--fg); + border: 1px solid var(--border); + cursor: pointer; + transition: background 120ms ease, border-color 120ms ease; +} +.btn:hover { background: var(--bg-elev-2); border-color: var(--accent); text-decoration: none; } +.btn.primary { + background: var(--accent-2); + border-color: var(--accent-2); + color: white; +} +.btn.primary:hover { background: #2876f9; border-color: #2876f9; } +.btn.ghost { + background: transparent; + border-color: transparent; + color: var(--fg-muted); +} +.btn.ghost:hover { color: var(--fg); background: var(--bg-elev); border-color: var(--border-soft); } +.btn.small { padding: 0.3rem 0.6rem; font-size: 0.78rem; } + +/* ── Badges & tags ────────────────────────────────────────── */ +.badge { + display: inline-flex; + align-items: center; + gap: 0.3rem; + padding: 0.15rem 0.55rem; + font-size: 0.72rem; + font-weight: 600; + border-radius: 999px; + background: var(--bg-elev-2); + color: var(--fg-muted); + border: 1px solid var(--border-soft); + white-space: nowrap; +} +.badge.tier-verified { color: var(--good); border-color: rgba(63,185,80,0.35); background: rgba(63,185,80,0.08); } +.badge.tier-community { color: var(--accent); border-color: rgba(88,166,255,0.35); background: var(--accent-soft); } +.badge.flagged { color: var(--bad); border-color: rgba(248,81,73,0.4); background: rgba(248,81,73,0.08); } + +/* Vendor pill */ +.vendor-pill { + display: inline-flex; + align-items: center; + gap: 0.3rem; + padding: 0.1rem 0.5rem; + font-size: 0.7rem; + font-weight: 600; + border-radius: var(--r-sm); + background: var(--bg-elev-2); + color: var(--fg-muted); +} +.vendor-pill::before { + content: ""; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--vendor-color, var(--fg-faint)); +} +.vendor-pill[data-vendor="NVIDIA"] { --vendor-color: var(--v-nvidia); } +.vendor-pill[data-vendor="AMD"] { --vendor-color: var(--v-amd); } +.vendor-pill[data-vendor="Apple"] { --vendor-color: var(--v-apple); } +.vendor-pill[data-vendor="Google"] { --vendor-color: var(--v-google); } +.vendor-pill[data-vendor="Huawei"] { --vendor-color: var(--v-huawei); } +.vendor-pill[data-vendor="Moore Threads"] { --vendor-color: var(--v-moore); } +.vendor-pill[data-vendor="Intel"] { --vendor-color: var(--v-intel); } + +/* Suite chip — for top nav of rankings etc. */ +.suite-chip { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.32rem 0.65rem; + background: var(--bg-elev); + border: 1px solid var(--border-soft); + border-radius: 999px; + color: var(--fg-muted); + font-size: 0.8rem; + font-weight: 500; + cursor: pointer; +} +.suite-chip:hover { color: var(--fg); border-color: var(--accent); text-decoration: none; } +.suite-chip.active { + background: var(--accent-soft); + border-color: var(--accent); + color: var(--fg-strong); +} +.suite-chip .suite-letter { + width: 18px; height: 18px; + border-radius: 50%; + background: var(--bg-elev-2); + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 0.68rem; + font-weight: 700; + color: var(--fg); +} +.suite-chip.active .suite-letter { background: var(--accent-2); color: white; } + +/* ── Relative bar ─────────────────────────────────────────── */ +.rel-bar { + position: relative; + height: 6px; + background: var(--bg-elev-2); + border-radius: 3px; + overflow: hidden; +} +.rel-bar > .fill { + position: absolute; + inset: 0 auto 0 0; + background: linear-gradient(90deg, var(--accent-2), var(--accent)); + border-radius: 3px; +} + +/* Compact KPI tile */ +.kpi { + display: flex; + flex-direction: column; + gap: 0.15rem; +} +.kpi .kpi-value { + font-family: var(--font-mono); + font-size: 1.4rem; + font-weight: 700; + color: var(--fg-strong); + letter-spacing: -0.02em; +} +.kpi .kpi-label { + font-size: 0.75rem; + color: var(--fg-muted); + text-transform: uppercase; + letter-spacing: 0.06em; +} + +/* Hero strip (used by home + chip detail) */ +.hero { + padding: 1.5rem 0 0.75rem; +} +.hero h1 { + margin: 0 0 0.4rem; + font-size: 1.8rem; + font-weight: 700; + color: var(--fg-strong); + letter-spacing: -0.01em; +} +.hero .tagline { + color: var(--fg-muted); + margin: 0 0 1rem; + font-size: 0.98rem; +} + +.hero-stats { + display: flex; + gap: 2rem; + flex-wrap: wrap; + margin-bottom: 0.75rem; +} + +@media (max-width: 640px) { + .hero h1 { font-size: 1.45rem; } + .hero-stats { gap: 1.25rem; } + .kpi .kpi-value { font-size: 1.2rem; } +} + +/* Rank medals */ +.rank { + font-family: var(--font-mono); + font-weight: 700; + font-size: 0.85rem; + color: var(--fg-muted); + min-width: 1.8em; + display: inline-block; +} +.rank.gold { color: var(--gold); } +.rank.silver { color: var(--silver); } +.rank.bronze { color: var(--bronze); } diff --git a/leaderboard/site/assets/css/home.css b/leaderboard/site/assets/css/home.css new file mode 100644 index 00000000..1f4efa3c --- /dev/null +++ b/leaderboard/site/assets/css/home.css @@ -0,0 +1,141 @@ +/* home.css — Home page (suite overview grid + recent additions) */ + +.suite-card { + display: flex; + flex-direction: column; + gap: 0.85rem; + padding: 1.1rem 1.2rem 1rem; + text-decoration: none; + color: inherit; +} + +.suite-card-header { + display: flex; + align-items: flex-start; + gap: 0.65rem; +} +.suite-card-header .suite-letter-big { + flex: 0 0 auto; + width: 32px; height: 32px; + border-radius: 8px; + background: var(--accent-soft); + color: var(--accent); + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 0.95rem; + font-weight: 800; + letter-spacing: 0.02em; +} +.suite-card-header .suite-title { + font-size: 1rem; + font-weight: 600; + color: var(--fg-strong); + line-height: 1.25; +} +.suite-card-header .suite-tagline { + font-size: 0.78rem; + color: var(--fg-muted); + margin-top: 0.1rem; +} + +.suite-card-meta { + display: flex; + gap: 0.85rem; + flex-wrap: wrap; + font-size: 0.72rem; + color: var(--fg-muted); +} +.suite-card-meta .meta-item { + display: inline-flex; + align-items: center; + gap: 0.3rem; +} + +.podium { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: 0.5rem; +} +.podium-row { + display: grid; + grid-template-columns: 1.4rem 1fr auto; + gap: 0.6rem; + align-items: center; + font-size: 0.85rem; +} +.podium-row .podium-name { + color: var(--fg); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.podium-row .podium-value { + font-family: var(--font-mono); + font-size: 0.78rem; + color: var(--fg-muted); + white-space: nowrap; +} +.podium-bars { + display: flex; + flex-direction: column; + gap: 0.35rem; +} +.podium-row .rel-bar { height: 4px; } + +.suite-card-cta { + margin-top: 0.25rem; + font-size: 0.82rem; + color: var(--accent); + font-weight: 500; + display: inline-flex; + align-items: center; + gap: 0.25rem; +} +.suite-card.is-link:hover .suite-card-cta { color: var(--fg-strong); } + +/* Empty suite (no submissions yet) */ +.suite-card.empty .podium { + color: var(--fg-faint); + font-style: italic; + font-size: 0.82rem; +} + +/* Recent additions */ +.recent-list { + display: flex; + flex-direction: column; + gap: 0.45rem; +} +.recent-row { + display: grid; + grid-template-columns: minmax(140px, 1.6fr) 60px 80px 1fr auto; + gap: 0.85rem; + align-items: center; + padding: 0.55rem 0.85rem; + background: var(--bg-elev); + border: 1px solid var(--border-soft); + border-radius: var(--r-md); + font-size: 0.85rem; + color: var(--fg); + text-decoration: none; +} +.recent-row:hover { + border-color: var(--accent); + text-decoration: none; +} +.recent-row .recent-suite { font-family: var(--font-mono); font-size: 0.78rem; color: var(--fg-muted); } +.recent-row .recent-metric { font-family: var(--font-mono); font-weight: 600; color: var(--fg-strong); } +.recent-row .recent-date { color: var(--fg-muted); font-size: 0.78rem; } + +@media (max-width: 700px) { + .recent-row { + grid-template-columns: 1fr; + gap: 0.25rem; + } + .recent-row .recent-date, + .recent-row .recent-metric { font-size: 0.78rem; } +} diff --git a/leaderboard/site/assets/css/layout.css b/leaderboard/site/assets/css/layout.css new file mode 100644 index 00000000..31335167 --- /dev/null +++ b/leaderboard/site/assets/css/layout.css @@ -0,0 +1,148 @@ +/* layout.css — top nav, page shell, footer, responsive */ + +.topnav { + position: sticky; + top: 0; + z-index: 50; + display: flex; + align-items: center; + gap: 1.25rem; + padding: 0.75rem 1.5rem; + background: rgba(13, 17, 23, 0.92); + backdrop-filter: saturate(140%) blur(6px); + -webkit-backdrop-filter: saturate(140%) blur(6px); + border-bottom: 1px solid var(--border-soft); +} + +.topnav .brand { + display: flex; + align-items: center; + gap: 0.55rem; + font-weight: 700; + font-size: 1.05rem; + color: var(--fg-strong); +} +.topnav .brand:hover { text-decoration: none; } +.topnav .brand svg { display: block; } + +.topnav .nav-links { + display: flex; + gap: 0.25rem; + align-items: center; +} + +.topnav .nav-link { + padding: 0.45rem 0.85rem; + border-radius: var(--r-md); + font-size: 0.9rem; + color: var(--fg-muted); + font-weight: 500; +} +.topnav .nav-link:hover { background: var(--bg-elev); color: var(--fg); text-decoration: none; } +.topnav .nav-link.active { background: var(--accent-soft); color: var(--fg-strong); } + +.topnav .nav-right { + margin-left: auto; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.topnav .compare-pill { + display: none; + align-items: center; + gap: 0.4rem; + padding: 0.3rem 0.65rem; + background: var(--accent-2); + color: white; + border-radius: 999px; + font-size: 0.78rem; + font-weight: 600; +} +.topnav .compare-pill.show { display: inline-flex; } +.topnav .compare-pill .count { + background: rgba(255,255,255,0.25); + border-radius: 999px; + padding: 0 0.4rem; + font-size: 0.72rem; +} + +.topnav .gh-link { + color: var(--fg-muted); + font-size: 0.85rem; + padding: 0.35rem; +} +.topnav .gh-link:hover { color: var(--fg); text-decoration: none; } + +main { + max-width: var(--max-w); + margin: 0 auto; + padding: 1.5rem; +} + +footer { + border-top: 1px solid var(--border-soft); + padding: 2rem 1.5rem 3rem; + text-align: center; + color: var(--fg-muted); + font-size: 0.82rem; +} +footer a { color: var(--fg-muted); border-bottom: 1px solid transparent; } +footer a:hover { color: var(--fg); border-bottom-color: var(--fg); text-decoration: none; } + +/* Page section spacing */ +.section { + margin-bottom: 2.25rem; +} +.section-header { + display: flex; + align-items: baseline; + justify-content: space-between; + margin-bottom: 1rem; + gap: 1rem; + flex-wrap: wrap; +} +.section-header h2 { + margin: 0; + font-size: 1.15rem; + font-weight: 600; + color: var(--fg-strong); +} +.section-header .section-sub { + color: var(--fg-muted); + font-size: 0.85rem; +} + +/* Loading + empty states */ +.state { + padding: 3rem 1rem; + text-align: center; + color: var(--fg-muted); + font-size: 0.95rem; +} +.state .state-icon { + font-size: 1.6rem; + color: var(--fg-faint); + display: block; + margin-bottom: 0.5rem; +} + +/* Generic grid helpers */ +.grid { + display: grid; + gap: 1rem; +} +.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } +.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } +.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } + +@media (max-width: 980px) { + .grid-3, .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } +} +@media (max-width: 640px) { + .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } + .topnav { padding: 0.6rem 1rem; gap: 0.5rem; flex-wrap: wrap; } + .topnav .nav-link { padding: 0.35rem 0.55rem; font-size: 0.82rem; } + main { padding: 1rem; } + .hide-mobile { display: none !important; } +} diff --git a/leaderboard/site/assets/js/data.js b/leaderboard/site/assets/js/data.js new file mode 100644 index 00000000..056234e4 --- /dev/null +++ b/leaderboard/site/assets/js/data.js @@ -0,0 +1,216 @@ +// data.js — single source of truth for leaderboard rows. +// +// The legacy generator emits leaderboard.js which sets `window.LEADERBOARD_DATA` +// as a side-effect when included via - - - detailSection('Description', [ - impl.description ? `
- ${impl.description} -
` : null, - impl.notes ? `
- Notes - ${impl.notes} -
` : null, - ]), + + - detailSection('Source', [ - `
- Runner folder - - runners/${impl.id} ↗ - -
`, - `
- runner.py - - View source ↗ - -
`, - ]), - ].join(''); - } - + - \ No newline at end of file + diff --git a/leaderboard/site/style.css b/leaderboard/site/style.css deleted file mode 100644 index bad295be..00000000 --- a/leaderboard/site/style.css +++ /dev/null @@ -1,92 +0,0 @@ -*, *::before, *::after { box-sizing: border-box; } - -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - margin: 0; - background: #f8f9fa; - color: #212529; -} - -header { - background: #1a1a2e; - color: #fff; - padding: 2rem; - text-align: center; -} - -header h1 { margin: 0 0 0.5rem; font-size: 2rem; } -header p { margin: 0 0 1rem; opacity: 0.8; } - -.btn { - display: inline-block; - margin: 0 0.4rem; - padding: 0.5rem 1.2rem; - background: #e94560; - color: #fff; - border-radius: 4px; - text-decoration: none; - font-size: 0.9rem; -} -.btn:hover { background: #c73652; } - -main { max-width: 1400px; margin: 0 auto; padding: 1.5rem; } - -#filters { - display: flex; - gap: 1.5rem; - flex-wrap: wrap; - margin-bottom: 1rem; - background: #fff; - padding: 1rem 1.5rem; - border-radius: 6px; - box-shadow: 0 1px 3px rgba(0,0,0,.1); -} - -#filters label { font-size: 0.9rem; } -#filters select { - margin-left: 0.4rem; - padding: 0.3rem 0.5rem; - border: 1px solid #ced4da; - border-radius: 4px; -} - -.table-wrap { overflow-x: auto; } - -table { - width: 100%; - border-collapse: collapse; - background: #fff; - border-radius: 6px; - overflow: hidden; - box-shadow: 0 1px 3px rgba(0,0,0,.1); - font-size: 0.88rem; -} - -th { - background: #1a1a2e; - color: #fff; - padding: 0.7rem 0.8rem; - text-align: left; - white-space: nowrap; -} - -td { - padding: 0.6rem 0.8rem; - border-bottom: 1px solid #e9ecef; -} - -tr:last-child td { border-bottom: none; } -tr:hover td { background: #f1f3f5; } - -.tier-verified { color: #2f9e44; font-weight: 600; } -.tier-community { color: #1971c2; } - -.acc-valid { color: #2f9e44; } -.acc-invalid { color: #e03131; } - -footer { - text-align: center; - padding: 2rem; - color: #6c757d; - font-size: 0.85rem; -} From 17f4eaf785691b4579ab9bce572062d096e93a77 Mon Sep 17 00:00:00 2001 From: Liang Juhao Date: Fri, 15 May 2026 15:30:51 +0800 Subject: [PATCH 02/21] chore: ignore local .handoff-*.md notes Cross-session handoff drafts are session-local; they shouldn't land in the repo. Match by glob so any feature handoff stays untracked. Co-authored-by: Cursor --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 2ea1c8c9..953116da 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,6 @@ mini_result/ *_backup/ backup/ /tmp/ + +# ── Local-only handoff notes (across-session continuity) ──────────────────── +.handoff-*.md From 453f6a27a2a7f29f7b32606022bb2a2b38b4bd95 Mon Sep 17 00:00:00 2001 From: Liang Juhao Date: Fri, 15 May 2026 15:31:21 +0800 Subject: [PATCH 03/21] feat(leaderboard): redesign Home in OpenCompass-style cards + theme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per first-round Home feedback ("乱", "字小", "bar 喧宾夺主", "97.0 怪"), rebuild the Home page around small standalone leaderboards instead of a podium+bar visual. UI changes - Suite card now has a filled accent header (letter + title + metric tag) followed by tagline, top-5 row list, and a centered "View full ranking →" CTA. Replaces the 3-entry podium with relative bars. - New .lb-row primitive: rank circle (gold/silver/bronze for #1–#3, neutral for #4–#5), chip name, vendor color dot + framework, and a right-aligned mono score with muted unit. - Recent submissions reuse .lb-row; suite letter takes the rank slot and a small "Suite X" pill plus date sit in the sub line. - Vendor identity is now visible at a glance via the colored dot. Theming - Auto light/dark via prefers-color-scheme; no manual toggle, no separate stylesheet. Tokens redeclared in @media block in base.css. - Badges, primary button, compare pill now use color-mix on the current accent/good/bad tokens so they adapt to both themes. Layout polish - Hero: bigger h1 (2.1rem), more breathing tagline, KPI value bumped to 1.7rem with uppercased label kerning. - Main padding loosened to 2rem 1.75rem 3rem; section margin to 3rem so 7-card grid no longer feels cramped. - --max-w narrowed 1400 → 1200 for more comfortable line lengths. Misc - fmtNum integer fast-path: 97 / 32 / 4 render without ".0". - formatPrimary unit is split from the number in views so the unit can render muted next to the value. Co-authored-by: Cursor --- leaderboard/site/assets/css/base.css | 50 +++- leaderboard/site/assets/css/components.css | 67 +++-- leaderboard/site/assets/css/home.css | 292 +++++++++++++++------ leaderboard/site/assets/css/layout.css | 30 ++- leaderboard/site/assets/js/utils.js | 5 + leaderboard/site/assets/js/views/home.js | 179 +++++++------ 6 files changed, 422 insertions(+), 201 deletions(-) diff --git a/leaderboard/site/assets/css/base.css b/leaderboard/site/assets/css/base.css index 2812f477..e0f483f3 100644 --- a/leaderboard/site/assets/css/base.css +++ b/leaderboard/site/assets/css/base.css @@ -1,23 +1,23 @@ /* base.css — design tokens, reset, typography */ :root { - /* Surfaces */ + /* ── Dark theme (default) ─────────────────────────────── */ --bg: #0d1117; --bg-elev: #161b22; --bg-elev-2: #1c2128; --border: #30363d; --border-soft: #21262d; - /* Text */ --fg: #e6edf3; --fg-strong: #f0f6fc; --fg-muted: #8b949e; --fg-faint: #6e7681; - /* Accents */ --accent: #58a6ff; --accent-2: #1f6feb; + --accent-3: #2f4dc0; /* deeper, for card headers */ --accent-soft: rgba(88, 166, 255, 0.12); + --on-accent: #ffffff; /* text color over accent fills */ --good: #3fb950; --warn: #d29922; @@ -26,7 +26,7 @@ --silver: #adb5bd; --bronze: #cd7f32; - /* Vendor palette (used for chips/badges) */ + /* Vendor palette */ --v-nvidia: #76b900; --v-amd: #ed1c24; --v-apple: #a1a1aa; @@ -35,15 +35,16 @@ --v-moore: #c084fc; --v-intel: #0071c5; - /* Radii / shadows */ + /* Card shadow */ + --shadow-card: 0 1px 2px rgba(0,0,0,0.4); + + /* Radii */ --r-sm: 4px; --r-md: 6px; - --r-lg: 10px; - --shadow-sm: 0 1px 2px rgba(0,0,0,0.5); - --shadow-md: 0 4px 16px rgba(0,0,0,0.4); + --r-lg: 12px; /* Layout */ - --max-w: 1400px; + --max-w: 1200px; --gap: 1rem; --gap-lg: 1.5rem; @@ -52,6 +53,37 @@ --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; } +/* ── Light theme (auto, follows OS) ───────────────────── */ +@media (prefers-color-scheme: light) { + :root { + --bg: #f7f8fb; + --bg-elev: #ffffff; + --bg-elev-2: #f1f3f6; + --border: #d8dde5; + --border-soft: #e8ecf2; + + --fg: #1f2937; + --fg-strong: #0f172a; + --fg-muted: #5b6577; + --fg-faint: #94a3b8; + + --accent: #1d4ed8; + --accent-2: #2563eb; + --accent-3: #1e40af; + --accent-soft: rgba(37, 99, 235, 0.10); + --on-accent: #ffffff; + + --good: #15803d; + --warn: #b45309; + --bad: #b91c1c; + --gold: #b8860b; + --silver: #6b7280; + --bronze: #b45a30; + + --shadow-card: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.04); + } +} + *, *::before, *::after { box-sizing: border-box; } html, body { margin: 0; padding: 0; } diff --git a/leaderboard/site/assets/css/components.css b/leaderboard/site/assets/css/components.css index cbd6855d..fbdc4ba1 100644 --- a/leaderboard/site/assets/css/components.css +++ b/leaderboard/site/assets/css/components.css @@ -37,9 +37,12 @@ .btn.primary { background: var(--accent-2); border-color: var(--accent-2); - color: white; + color: var(--on-accent); +} +.btn.primary:hover { + background: color-mix(in srgb, var(--accent-2) 88%, var(--fg-strong)); + border-color: color-mix(in srgb, var(--accent-2) 88%, var(--fg-strong)); } -.btn.primary:hover { background: #2876f9; border-color: #2876f9; } .btn.ghost { background: transparent; border-color: transparent; @@ -62,9 +65,21 @@ border: 1px solid var(--border-soft); white-space: nowrap; } -.badge.tier-verified { color: var(--good); border-color: rgba(63,185,80,0.35); background: rgba(63,185,80,0.08); } -.badge.tier-community { color: var(--accent); border-color: rgba(88,166,255,0.35); background: var(--accent-soft); } -.badge.flagged { color: var(--bad); border-color: rgba(248,81,73,0.4); background: rgba(248,81,73,0.08); } +.badge.tier-verified { + color: var(--good); + border-color: color-mix(in srgb, var(--good) 40%, transparent); + background: color-mix(in srgb, var(--good) 10%, transparent); +} +.badge.tier-community { + color: var(--accent); + border-color: color-mix(in srgb, var(--accent) 35%, transparent); + background: var(--accent-soft); +} +.badge.flagged { + color: var(--bad); + border-color: color-mix(in srgb, var(--bad) 40%, transparent); + background: color-mix(in srgb, var(--bad) 10%, transparent); +} /* Vendor pill */ .vendor-pill { @@ -145,50 +160,62 @@ .kpi { display: flex; flex-direction: column; - gap: 0.15rem; + gap: 0.2rem; } .kpi .kpi-value { font-family: var(--font-mono); - font-size: 1.4rem; + font-size: 1.7rem; font-weight: 700; color: var(--fg-strong); letter-spacing: -0.02em; + line-height: 1.05; } .kpi .kpi-label { - font-size: 0.75rem; + font-size: 0.78rem; color: var(--fg-muted); text-transform: uppercase; - letter-spacing: 0.06em; + letter-spacing: 0.08em; } /* Hero strip (used by home + chip detail) */ .hero { - padding: 1.5rem 0 0.75rem; + padding: 1rem 0 2rem; } .hero h1 { - margin: 0 0 0.4rem; - font-size: 1.8rem; + margin: 0 0 0.6rem; + font-size: 2.1rem; font-weight: 700; color: var(--fg-strong); - letter-spacing: -0.01em; + letter-spacing: -0.015em; + line-height: 1.15; + max-width: 44ch; } .hero .tagline { color: var(--fg-muted); - margin: 0 0 1rem; - font-size: 0.98rem; + margin: 0 0 1.4rem; + font-size: 1.05rem; + line-height: 1.5; + max-width: 64ch; } .hero-stats { display: flex; - gap: 2rem; + gap: 2.5rem; + flex-wrap: wrap; + margin-bottom: 1.4rem; +} +.hero-cta { + display: flex; + gap: 0.6rem; flex-wrap: wrap; - margin-bottom: 0.75rem; } @media (max-width: 640px) { - .hero h1 { font-size: 1.45rem; } - .hero-stats { gap: 1.25rem; } - .kpi .kpi-value { font-size: 1.2rem; } + .hero { padding: 0.5rem 0 1.5rem; } + .hero h1 { font-size: 1.55rem; } + .hero .tagline { font-size: 0.95rem; } + .hero-stats { gap: 1.5rem; } + .kpi .kpi-value { font-size: 1.4rem; } } /* Rank medals */ diff --git a/leaderboard/site/assets/css/home.css b/leaderboard/site/assets/css/home.css index 1f4efa3c..657efb1f 100644 --- a/leaderboard/site/assets/css/home.css +++ b/leaderboard/site/assets/css/home.css @@ -1,141 +1,265 @@ /* home.css — Home page (suite overview grid + recent additions) */ +/* ── Suite card ──────────────────────────────────────────── + OpenCompass-style: + ┌─ filled accent header ─────────────┐ + │ A · Single-chip throughput tok/s │ + └────────────────────────────────────┘ + · row · row · row · row · row + centered CTA +*/ + .suite-card { + /* override .card padding so header fills edge-to-edge */ + padding: 0; + overflow: hidden; display: flex; flex-direction: column; - gap: 0.85rem; - padding: 1.1rem 1.2rem 1rem; text-decoration: none; color: inherit; + box-shadow: var(--shadow-card); +} +.suite-card.is-link:hover { + text-decoration: none; + transform: translateY(-1px); } -.suite-card-header { +.suite-card-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + padding: 0.85rem 1.1rem; + background: var(--accent-3); + color: var(--on-accent); +} +.suite-card-head .suite-head-left { display: flex; - align-items: flex-start; - gap: 0.65rem; + align-items: center; + gap: 0.7rem; + min-width: 0; } -.suite-card-header .suite-letter-big { +.suite-card-head .suite-letter { flex: 0 0 auto; - width: 32px; height: 32px; - border-radius: 8px; - background: var(--accent-soft); - color: var(--accent); + width: 26px; + height: 26px; + border-radius: 6px; + background: rgba(255, 255, 255, 0.18); + color: var(--on-accent); + font-weight: 800; + font-size: 0.85rem; display: inline-flex; align-items: center; justify-content: center; - font-size: 0.95rem; - font-weight: 800; letter-spacing: 0.02em; } -.suite-card-header .suite-title { +.suite-card-head .suite-title { font-size: 1rem; font-weight: 600; - color: var(--fg-strong); - line-height: 1.25; + color: var(--on-accent); + letter-spacing: -0.005em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } -.suite-card-header .suite-tagline { - font-size: 0.78rem; +.suite-card-head .suite-metric-tag { + flex: 0 0 auto; + font-family: var(--font-mono); + font-size: 0.72rem; + font-weight: 600; + padding: 0.18rem 0.55rem; + border-radius: 999px; + background: rgba(255, 255, 255, 0.2); + color: var(--on-accent); + letter-spacing: 0.02em; + white-space: nowrap; +} + +.suite-card-tag { + padding: 0.65rem 1.1rem 0.1rem; + font-size: 0.82rem; color: var(--fg-muted); - margin-top: 0.1rem; + line-height: 1.4; } -.suite-card-meta { +.suite-card-body { + padding: 0.5rem 0.55rem 0.4rem; display: flex; - gap: 0.85rem; - flex-wrap: wrap; - font-size: 0.72rem; - color: var(--fg-muted); + flex-direction: column; +} + +.suite-card-foot { + padding: 0.5rem 1.1rem 1rem; + text-align: center; +} +.suite-card-foot a, +.suite-card-foot .cta { + font-size: 0.82rem; + color: var(--accent); + font-weight: 500; + text-decoration: none; +} +.suite-card.is-link:hover .suite-card-foot .cta { color: var(--fg-strong); } + +/* Empty suite (no submissions yet) */ +.suite-card.empty .suite-card-body { + padding: 1.25rem 1.1rem 1rem; + color: var(--fg-faint); + font-style: italic; + font-size: 0.85rem; + text-align: center; +} + +/* ── Leaderboard row (used in suite cards + recent list) ── */ +.lb-row { + position: relative; + display: grid; + grid-template-columns: 28px minmax(0, 1fr) auto; + align-items: center; + gap: 0.7rem; + padding: 0.5rem 0.6rem; + border-radius: var(--r-md); + text-decoration: none; + color: inherit; + transition: background 100ms ease; +} +.lb-row:hover { + background: var(--bg-elev-2); + text-decoration: none; +} +.lb-row + .lb-row::before { + content: ""; + position: absolute; + top: 0; + left: 0.6rem; + right: 0.6rem; + height: 1px; + background: var(--border-soft); + pointer-events: none; } -.suite-card-meta .meta-item { +.lb-row:hover::before, +.lb-row:hover + .lb-row::before { background: transparent; } + +.lb-row-rank { + width: 24px; + height: 24px; + border-radius: 50%; display: inline-flex; align-items: center; - gap: 0.3rem; + justify-content: center; + font-family: var(--font-mono); + font-size: 0.72rem; + font-weight: 700; + background: var(--bg-elev-2); + color: var(--fg-muted); + border: 1px solid var(--border-soft); } +.lb-row-rank.gold { background: var(--gold); color: #1a1206; border-color: transparent; } +.lb-row-rank.silver { background: var(--silver); color: #15171c; border-color: transparent; } +.lb-row-rank.bronze { background: var(--bronze); color: #1e0e02; border-color: transparent; } -.podium { - list-style: none; - padding: 0; - margin: 0; +.lb-row-main { + min-width: 0; display: flex; flex-direction: column; - gap: 0.5rem; + gap: 0.1rem; } -.podium-row { - display: grid; - grid-template-columns: 1.4rem 1fr auto; - gap: 0.6rem; - align-items: center; - font-size: 0.85rem; -} -.podium-row .podium-name { - color: var(--fg); +.lb-row-name { + font-size: 0.95rem; + font-weight: 600; + color: var(--fg-strong); + line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.podium-row .podium-value { - font-family: var(--font-mono); - font-size: 0.78rem; +.lb-row-sub { + display: inline-flex; + align-items: center; + gap: 0.4rem; + font-size: 0.74rem; color: var(--fg-muted); - white-space: nowrap; + line-height: 1.2; +} +.lb-row-sub .vendor-dot { + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--vendor-color, var(--fg-faint)); + flex: 0 0 auto; } -.podium-bars { +.lb-row-sub .vendor-dot[data-vendor="NVIDIA"] { --vendor-color: var(--v-nvidia); } +.lb-row-sub .vendor-dot[data-vendor="AMD"] { --vendor-color: var(--v-amd); } +.lb-row-sub .vendor-dot[data-vendor="Apple"] { --vendor-color: var(--v-apple); } +.lb-row-sub .vendor-dot[data-vendor="Google"] { --vendor-color: var(--v-google); } +.lb-row-sub .vendor-dot[data-vendor="Huawei"] { --vendor-color: var(--v-huawei); } +.lb-row-sub .vendor-dot[data-vendor="Moore Threads"] { --vendor-color: var(--v-moore); } +.lb-row-sub .vendor-dot[data-vendor="Intel"] { --vendor-color: var(--v-intel); } + +.lb-row-sub .sub-sep { color: var(--fg-faint); } + +.lb-row-score { + text-align: right; display: flex; flex-direction: column; - gap: 0.35rem; + align-items: flex-end; + line-height: 1.1; } -.podium-row .rel-bar { height: 4px; } - -.suite-card-cta { - margin-top: 0.25rem; - font-size: 0.82rem; - color: var(--accent); - font-weight: 500; - display: inline-flex; - align-items: center; - gap: 0.25rem; +.lb-row-score .score-val { + font-family: var(--font-mono); + font-size: 1.05rem; + font-weight: 700; + color: var(--fg-strong); + letter-spacing: -0.01em; +} +.lb-row-score .score-unit { + font-size: 0.68rem; + color: var(--fg-muted); + text-transform: lowercase; + letter-spacing: 0.02em; } -.suite-card.is-link:hover .suite-card-cta { color: var(--fg-strong); } -/* Empty suite (no submissions yet) */ -.suite-card.empty .podium { +/* Empty placeholder row inside a suite with no entries */ +.lb-row.placeholder { color: var(--fg-faint); font-style: italic; - font-size: 0.82rem; + justify-content: center; + grid-template-columns: auto; } -/* Recent additions */ +/* ── Recent submissions list ─────────────────────────────── */ .recent-list { display: flex; flex-direction: column; - gap: 0.45rem; -} -.recent-row { - display: grid; - grid-template-columns: minmax(140px, 1.6fr) 60px 80px 1fr auto; - gap: 0.85rem; - align-items: center; - padding: 0.55rem 0.85rem; background: var(--bg-elev); border: 1px solid var(--border-soft); - border-radius: var(--r-md); - font-size: 0.85rem; - color: var(--fg); - text-decoration: none; + border-radius: var(--r-lg); + padding: 0.4rem 0.5rem; + box-shadow: var(--shadow-card); } -.recent-row:hover { - border-color: var(--accent); - text-decoration: none; +.recent-list .lb-row .lb-row-sub .suite-tag { + display: inline-flex; + align-items: center; + gap: 0.25rem; + font-family: var(--font-mono); + font-size: 0.7rem; + padding: 0.05rem 0.4rem; + background: var(--bg-elev-2); + color: var(--fg-muted); + border-radius: 999px; +} +.recent-list .lb-row .lb-row-sub .date { + color: var(--fg-faint); + font-size: 0.72rem; } -.recent-row .recent-suite { font-family: var(--font-mono); font-size: 0.78rem; color: var(--fg-muted); } -.recent-row .recent-metric { font-family: var(--font-mono); font-weight: 600; color: var(--fg-strong); } -.recent-row .recent-date { color: var(--fg-muted); font-size: 0.78rem; } +/* ── Responsive ──────────────────────────────────────────── */ @media (max-width: 700px) { - .recent-row { - grid-template-columns: 1fr; - gap: 0.25rem; - } - .recent-row .recent-date, - .recent-row .recent-metric { font-size: 0.78rem; } + .suite-card-head { padding: 0.7rem 0.85rem; } + .suite-card-tag { padding: 0.5rem 0.85rem 0; font-size: 0.78rem; } + .suite-card-body { padding: 0.35rem 0.4rem 0.3rem; } + .suite-card-foot { padding: 0.5rem 0.85rem 0.85rem; } + .lb-row { padding: 0.4rem 0.5rem; gap: 0.55rem; } + .lb-row-name { font-size: 0.9rem; } + .lb-row-score .score-val { font-size: 0.95rem; } } diff --git a/leaderboard/site/assets/css/layout.css b/leaderboard/site/assets/css/layout.css index 31335167..e5f0ecf3 100644 --- a/leaderboard/site/assets/css/layout.css +++ b/leaderboard/site/assets/css/layout.css @@ -7,12 +7,17 @@ display: flex; align-items: center; gap: 1.25rem; - padding: 0.75rem 1.5rem; - background: rgba(13, 17, 23, 0.92); - backdrop-filter: saturate(140%) blur(6px); - -webkit-backdrop-filter: saturate(140%) blur(6px); + padding: 0.85rem 1.75rem; + background: var(--bg); border-bottom: 1px solid var(--border-soft); } +@supports (backdrop-filter: blur(6px)) { + .topnav { + background: color-mix(in srgb, var(--bg) 86%, transparent); + backdrop-filter: saturate(160%) blur(8px); + -webkit-backdrop-filter: saturate(160%) blur(8px); + } +} .topnav .brand { display: flex; @@ -54,14 +59,14 @@ gap: 0.4rem; padding: 0.3rem 0.65rem; background: var(--accent-2); - color: white; + color: var(--on-accent); border-radius: 999px; font-size: 0.78rem; font-weight: 600; } .topnav .compare-pill.show { display: inline-flex; } .topnav .compare-pill .count { - background: rgba(255,255,255,0.25); + background: rgba(255, 255, 255, 0.25); border-radius: 999px; padding: 0 0.4rem; font-size: 0.72rem; @@ -77,7 +82,7 @@ main { max-width: var(--max-w); margin: 0 auto; - padding: 1.5rem; + padding: 2rem 1.75rem 3rem; } footer { @@ -92,25 +97,26 @@ footer a:hover { color: var(--fg); border-bottom-color: var(--fg); text-decorati /* Page section spacing */ .section { - margin-bottom: 2.25rem; + margin-bottom: 3rem; } .section-header { display: flex; align-items: baseline; justify-content: space-between; - margin-bottom: 1rem; + margin-bottom: 1.25rem; gap: 1rem; flex-wrap: wrap; } .section-header h2 { margin: 0; - font-size: 1.15rem; + font-size: 1.25rem; font-weight: 600; color: var(--fg-strong); + letter-spacing: -0.01em; } .section-header .section-sub { color: var(--fg-muted); - font-size: 0.85rem; + font-size: 0.9rem; } /* Loading + empty states */ @@ -130,7 +136,7 @@ footer a:hover { color: var(--fg); border-bottom-color: var(--fg); text-decorati /* Generic grid helpers */ .grid { display: grid; - gap: 1rem; + gap: 1.25rem; } .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } diff --git a/leaderboard/site/assets/js/utils.js b/leaderboard/site/assets/js/utils.js index b18a2aaf..cf4185f8 100644 --- a/leaderboard/site/assets/js/utils.js +++ b/leaderboard/site/assets/js/utils.js @@ -12,6 +12,7 @@ export function esc(s) { } // Format big numbers compactly: 12453 -> "12,453"; 1234567 -> "1.23M" +// Integers (e.g. counts) render with no decimals automatically. export function fmtNum(v, opts = {}) { if (v === null || v === undefined || Number.isNaN(v)) return "—"; const n = Number(v); @@ -21,6 +22,10 @@ export function fmtNum(v, opts = {}) { if (Math.abs(n) >= 1e6) return (n / 1e6).toFixed(2) + "M"; if (Math.abs(n) >= 1e3) return (n / 1e3).toFixed(2) + "K"; } + // Integer fast-path: counts like 97 / 32 / 4 must not render as "97.0". + if (decimals === undefined && Number.isInteger(n)) { + return n.toLocaleString(); + } const d = decimals === undefined ? (Math.abs(n) >= 100 ? 0 : Math.abs(n) >= 10 ? 1 : 2) : decimals; diff --git a/leaderboard/site/assets/js/views/home.js b/leaderboard/site/assets/js/views/home.js index 7c41fe55..ed9216e2 100644 --- a/leaderboard/site/assets/js/views/home.js +++ b/leaderboard/site/assets/js/views/home.js @@ -1,4 +1,16 @@ // views/home.js — Home page (multi-suite overview). +// +// Layout (per OpenCompass / user feedback): +// • Hero: title + tagline + KPI strip + CTAs +// • Suite grid: 7 cards, each a small standalone leaderboard (top 5) +// ┌─ accent header: A · Title [tok/s] ─┐ +// │ tagline │ +// │ ① NVIDIA H200 ×1 5,731 tok/s │ +// │ ② AMD MI300X ×1 5,128 tok/s │ +// │ … │ +// │ View full ranking → │ +// └──────────────────────────────────────────────┘ +// • Recent submissions: same row style in one list card import { SUITE_ORDER, SUITE_META, @@ -23,7 +35,7 @@ export function render({ el }) {
${fmtNum(s.suites)}suites
${fmtNum(s.verified)}verified
-
+
Browse rankings → Compare chips What are the suites? @@ -60,93 +72,108 @@ export function render({ el }) { function renderSuiteCard(suiteId) { const meta = SUITE_META[suiteId]; - const all = rowsForSuite(suiteId); - const podiumRows = bestPerChipForSuite(suiteId).slice(0, 3); - const direction = meta.primary.direction; - const submissions = all.length; - const chipsCovered = new Set(all.map((r) => r._chip_slug)).size; - - const article = document.createElement("a"); - article.href = buildHash("/rankings", { suite: suiteId }); - article.className = "card is-link suite-card" + (podiumRows.length === 0 ? " empty" : ""); - - if (podiumRows.length === 0) { - article.innerHTML = ` -
-
${meta.letter}
-
-
${esc(meta.title)}
-
${esc(meta.tagline)}
-
-
-
- No submissions yet + const top = bestPerChipForSuite(suiteId).slice(0, 5); + const empty = top.length === 0; + + const card = document.createElement("article"); + card.className = "card suite-card" + (empty ? " empty" : ""); + + const rankingsHref = buildHash("/rankings", { suite: suiteId }); + + const header = ` +
+
+ ${esc(meta.letter)} + ${esc(meta.title)}
-
Awaiting first submission.
- View suite → + ${esc(meta.primary.label)} +
+
${esc(meta.tagline)}
+ `; + + if (empty) { + card.innerHTML = ` + ${header} +
Awaiting first submission.
+ `; - return article; + return card; } - const best = podiumRows[0]; - const bestVal = best[meta.primary.key]; - // Range for relative bars: pick min/max across podium. - const vals = podiumRows.map((r) => r[meta.primary.key]); - const max = Math.max(...vals); - const min = Math.min(...vals); - - const podiumHtml = podiumRows.map((r, i) => { - const v = r[meta.primary.key]; - const fillPct = direction === "asc" - // For "lower is better" (e.g. latency), best gets full bar. - ? (max === min ? 100 : ((max - v) / (max - min)) * 100) - : (max === 0 ? 0 : (v / max) * 100); - const medal = ["gold", "silver", "bronze"][i] || ""; - return ` -
  • -
    - #${i + 1} - ${esc(r._chip_label)} - ${esc(formatPrimary(v, suiteId))} -
    -
    -
  • - `; - }).join(""); - - article.innerHTML = ` -
    -
    ${meta.letter}
    -
    -
    ${esc(meta.title)}
    -
    ${esc(meta.tagline)}
    -
    -
    -
    - ${submissions} ${submissions === 1 ? "submission" : "submissions"} - ${chipsCovered} ${chipsCovered === 1 ? "chip" : "chips"} - primary: ${esc(meta.primary.label)} -
    -
      ${podiumHtml}
    - View full ranking → + const body = top.map((r, i) => renderLbRow(r, suiteId, i + 1)).join(""); + card.innerHTML = ` + ${header} +
    ${body}
    + + `; + return card; +} + +// Single ranked row used by suite cards. Anchor → chip detail page. +function renderLbRow(row, suiteId, rank) { + const meta = SUITE_META[suiteId]; + const value = row[meta.primary.key]; + const display = formatPrimary(value, suiteId); + // Split formatted value from trailing unit so we can render unit muted. + const { num, unit } = splitNumUnit(display); + const medal = rank === 1 ? "gold" : rank === 2 ? "silver" : rank === 3 ? "bronze" : ""; + return ` + + ${rank} + + ${esc(row._chip_label)} + + + ${esc(row.vendor)} + · + ${esc(row.framework)}${row.precision ? " · " + esc(row.precision) : ""} + + + + ${esc(num)} + ${unit ? `${esc(unit)}` : ""} + + `; - // Suppress underline on hover for the whole card. - article.style.color = "inherit"; - return article; } function renderRecentRow(row) { const meta = SUITE_META[row.suite]; + const metricVal = meta ? row[meta.primary.key] : row.primary_metric; + const display = formatPrimary(metricVal, row.suite); + const { num, unit } = splitNumUnit(display); + const suiteLabel = row.suite.replace("suite_", "Suite "); + const a = document.createElement("a"); + a.className = "lb-row"; a.href = chipHref(row); - a.className = "recent-row"; - const metricVal = meta ? row[meta.primary.key] : row.primary_metric; a.innerHTML = ` - ${esc(row._chip_label)} - ${esc(row.suite.replace("suite_", "Suite "))} - ${esc(formatPrimary(metricVal, row.suite))} - ${esc(row.framework)} ${esc(row.framework_version || "")} · ${esc(row.precision)} - ${fmtDate(row.date)} + + + ${esc(row._chip_label)} + + + ${esc(row.vendor)} + · + ${esc(suiteLabel)} + · + ${esc(fmtDate(row.date))} + + + + ${esc(num)} + ${unit ? `${esc(unit)}` : ""} + `; return a; } + +// "5,731 tok/s" → { num: "5,731", unit: "tok/s" } +// "94.5 %" → { num: "94.5", unit: "%" } +// "—" → { num: "—", unit: "" } +function splitNumUnit(s) { + if (!s) return { num: "—", unit: "" }; + const idx = s.search(/\s[A-Za-z%]/); + if (idx === -1) return { num: s, unit: "" }; + return { num: s.slice(0, idx), unit: s.slice(idx + 1) }; +} From fd72df0436ef1b34b6b58bae6d9efe1806055390 Mon Sep 17 00:00:00 2001 From: Liang Juhao Date: Fri, 15 May 2026 16:11:26 +0800 Subject: [PATCH 04/21] =?UTF-8?q?style(leaderboard):=20editorial=20palette?= =?UTF-8?q?=20=E2=80=94=20per-suite=20color,=20serif=20h1,=20warm=20parchm?= =?UTF-8?q?ent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Feedback was "颜色和布局有点单调". Premium UI in 2026 isn't about more chrome — it's restraint plus craft (Vercel, Linear, Anthropic). Reworked the visual system in that direction. Palette - Drop pure black / pure white. Dark uses an off-black (#0b0c10), light uses a warm parchment (#faf8f3) borrowed from editorial sites. - Each suite (A–G) now has its own perceptually-distinct category color (cobalt, violet, teal, terracotta, emerald, rose, indigo). Surfaced as the suite card's header bar, the letter chip, the #1 row tint, the CTA color, and the small suite-tag pill on recent rows. - Color reads consistently across the page — same color tells the same story everywhere. Typography - Pair editorial serif (ui-serif → Iowan / Charter / Georgia) with the existing sans body. Serif now drives the hero h1 (with italic accent on the lede), section h2s, KPI numerals, and the score-val numbers. - Hero h1 bumped to 2.7rem with -0.02em tracking; eyebrow micro-cap label sits above for editorial pacing. Layout polish - Hero: subtle multi-color radial-gradient backdrop (3 stops at low alpha) clipped to a softly-rounded billboard. Adds depth without noise. - KPI strip: thin vertical dividers between values, bracketed by hair- line top/bottom rules. - Section headers: editorial eyebrow ("01 · Workloads", "02 · Latest activity") + serif h2, separated from the body by a faint rule. - Suite card #1 row visually featured: faint color tint background + left accent bar + slightly larger score. - Soft card lift on hover (translate + bigger shadow + suite-colored border). - Theme-color meta now follows prefers-color-scheme so the iOS chrome matches. /suites placeholder updated to the same visual language so it doesn't look stranded. Co-authored-by: Cursor --- leaderboard/site/assets/css/base.css | 170 ++++++++++------ leaderboard/site/assets/css/components.css | 94 ++++++--- leaderboard/site/assets/css/home.css | 215 +++++++++++++++------ leaderboard/site/assets/css/layout.css | 26 ++- leaderboard/site/assets/js/views/home.js | 43 +++-- leaderboard/site/assets/js/views/suites.js | 40 ++-- leaderboard/site/index.html | 3 +- 7 files changed, 411 insertions(+), 180 deletions(-) diff --git a/leaderboard/site/assets/css/base.css b/leaderboard/site/assets/css/base.css index e0f483f3..f4d85df9 100644 --- a/leaderboard/site/assets/css/base.css +++ b/leaderboard/site/assets/css/base.css @@ -1,73 +1,99 @@ -/* base.css — design tokens, reset, typography */ +/* base.css — design tokens, reset, typography + * + * Palette philosophy (after "feels monotone" feedback): + * • Avoid pure black / pure white — dark uses off-black charcoal, + * light uses warm parchment (#faf8f3, Anthropic-ish). + * • Single accent (blue) for links / primary CTA stays — but each + * suite gets its own per-category color, surfaced on the card head, + * letter chip, and the featured #1 row. + * • Type pairs serif headline + system sans body for editorial feel. + */ :root { /* ── Dark theme (default) ─────────────────────────────── */ - --bg: #0d1117; - --bg-elev: #161b22; - --bg-elev-2: #1c2128; - --border: #30363d; - --border-soft: #21262d; - - --fg: #e6edf3; - --fg-strong: #f0f6fc; - --fg-muted: #8b949e; - --fg-faint: #6e7681; - - --accent: #58a6ff; - --accent-2: #1f6feb; - --accent-3: #2f4dc0; /* deeper, for card headers */ - --accent-soft: rgba(88, 166, 255, 0.12); - --on-accent: #ffffff; /* text color over accent fills */ - - --good: #3fb950; - --warn: #d29922; - --bad: #f85149; - --gold: #e3b341; - --silver: #adb5bd; - --bronze: #cd7f32; - - /* Vendor palette */ + --bg: #0b0c10; + --bg-elev: #14161c; + --bg-elev-2: #1c1f27; + --bg-tinted: #181a22; /* slightly cooler tint for hero */ + --border: #2a2d36; + --border-soft: #20232b; + + --fg: #d4d4d8; + --fg-strong: #f4f4f5; + --fg-muted: #a1a1aa; + --fg-faint: #71717a; + + --accent: #93c5fd; /* link color — readable on bg */ + --accent-2: #3b82f6; /* primary button / strong link */ + --accent-3: #1e3a8a; /* deep blue — fallback header */ + --accent-soft: rgba(59, 130, 246, 0.14); + --on-accent: #ffffff; + + --good: #34d399; + --warn: #fbbf24; + --bad: #fb7185; + + --gold: #f5b50a; + --silver: #c2c2c8; + --bronze: #d4844a; + + /* Per-suite category colors (chosen perceptually distinct, deep + enough that --on-accent white reads cleanly on top). */ + --suite-A: #4f6bed; /* cobalt — Single-chip throughput */ + --suite-B: #7c4ddb; /* violet — Multi-chip throughput */ + --suite-C: #14b8a6; /* teal — Quantization efficiency */ + --suite-D: #d97757; /* terracotta — Interactive latency */ + --suite-E: #16a34a; /* emerald — Scaling efficiency */ + --suite-F: #db2777; /* rose — Edge / low-power */ + --suite-G: #6366f1; /* indigo — Sustained load */ + + /* Vendor palette (kept) */ --v-nvidia: #76b900; --v-amd: #ed1c24; --v-apple: #a1a1aa; --v-google: #4285f4; - --v-huawei: #ff0000; + --v-huawei: #ff4d4d; --v-moore: #c084fc; --v-intel: #0071c5; - /* Card shadow */ - --shadow-card: 0 1px 2px rgba(0,0,0,0.4); + /* Card shadow (soft on dark) */ + --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.5); + --shadow-lift: 0 8px 24px -8px rgba(0, 0, 0, 0.55); /* Radii */ --r-sm: 4px; - --r-md: 6px; - --r-lg: 12px; + --r-md: 8px; + --r-lg: 14px; /* Layout */ - --max-w: 1200px; - --gap: 1rem; + --max-w: 1200px; + --gap: 1rem; --gap-lg: 1.5rem; /* Type */ - --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; + --font-sans: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif; + --font-serif: ui-serif, "Iowan Old Style", "Source Serif Pro", "Charter", Georgia, serif; + --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; + + color-scheme: dark; } /* ── Light theme (auto, follows OS) ───────────────────── */ @media (prefers-color-scheme: light) { :root { - --bg: #f7f8fb; + --bg: #faf8f3; /* warm parchment, not pure white */ --bg-elev: #ffffff; - --bg-elev-2: #f1f3f6; - --border: #d8dde5; - --border-soft: #e8ecf2; + --bg-elev-2: #f3efe6; + --bg-tinted: #f5f1e8; + --border: #e6e1d4; + --border-soft: #efeadd; - --fg: #1f2937; - --fg-strong: #0f172a; - --fg-muted: #5b6577; - --fg-faint: #94a3b8; + --fg: #44403c; + --fg-strong: #1c1917; + --fg-muted: #78716c; + --fg-faint: #a8a29e; - --accent: #1d4ed8; + --accent: #2563eb; --accent-2: #2563eb; --accent-3: #1e40af; --accent-soft: rgba(37, 99, 235, 0.10); @@ -76,11 +102,25 @@ --good: #15803d; --warn: #b45309; --bad: #b91c1c; + --gold: #b8860b; --silver: #6b7280; --bronze: #b45a30; - --shadow-card: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.04); + /* Suite colors — slight darken for white bg so white text on the + filled header keeps WCAG AA. */ + --suite-A: #3b5bdb; + --suite-B: #6d28d9; + --suite-C: #0f766e; + --suite-D: #c2410c; + --suite-E: #15803d; + --suite-F: #be185d; + --suite-G: #4338ca; + + --shadow-card: 0 1px 2px rgba(15, 23, 42, 0.05), 0 1px 3px rgba(15, 23, 42, 0.04); + --shadow-lift: 0 8px 24px -10px rgba(15, 23, 42, 0.14); + + color-scheme: light; } } @@ -114,14 +154,34 @@ button { ::-webkit-scrollbar-thumb:hover { background: var(--fg-faint); } /* Utility */ -.muted { color: var(--fg-muted); } -.faint { color: var(--fg-faint); } -.mono { font-family: var(--font-mono); } -.text-xs { font-size: 0.75rem; } -.text-sm { font-size: 0.85rem; } -.text-lg { font-size: 1.1rem; } -.text-xl { font-size: 1.35rem; } -.text-2xl { font-size: 1.8rem; } -.bold { font-weight: 600; } -.no-wrap { white-space: nowrap; } +.muted { color: var(--fg-muted); } +.faint { color: var(--fg-faint); } +.mono { font-family: var(--font-mono); } +.serif { font-family: var(--font-serif); } +.text-xs { font-size: 0.75rem; } +.text-sm { font-size: 0.85rem; } +.text-lg { font-size: 1.1rem; } +.text-xl { font-size: 1.35rem; } +.text-2xl { font-size: 1.8rem; } +.bold { font-weight: 600; } +.no-wrap { white-space: nowrap; } .hide-mobile { /* set in layout.css responsive block */ } + +/* ── Editorial eyebrow ──────────────────────────────────── */ +.eyebrow { + display: inline-flex; + align-items: center; + gap: 0.5rem; + font-size: 0.7rem; + font-weight: 600; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--fg-muted); +} +.eyebrow::before { + content: ""; + width: 1.5rem; + height: 1px; + background: currentColor; + opacity: 0.5; +} diff --git a/leaderboard/site/assets/css/components.css b/leaderboard/site/assets/css/components.css index fbdc4ba1..d3b820ba 100644 --- a/leaderboard/site/assets/css/components.css +++ b/leaderboard/site/assets/css/components.css @@ -156,53 +156,95 @@ border-radius: 3px; } -/* Compact KPI tile */ +/* Compact KPI tile (with subtle vertical divider) */ .kpi { display: flex; flex-direction: column; - gap: 0.2rem; + gap: 0.25rem; + padding-right: 2rem; + position: relative; +} +.kpi + .kpi::before { + content: ""; + position: absolute; + left: -1rem; + top: 0.4rem; + bottom: 0.4rem; + width: 1px; + background: var(--border-soft); } .kpi .kpi-value { - font-family: var(--font-mono); - font-size: 1.7rem; - font-weight: 700; + font-family: var(--font-serif); + font-size: 2rem; + font-weight: 500; color: var(--fg-strong); letter-spacing: -0.02em; - line-height: 1.05; + line-height: 1; } .kpi .kpi-label { - font-size: 0.78rem; + font-size: 0.72rem; color: var(--fg-muted); text-transform: uppercase; - letter-spacing: 0.08em; + letter-spacing: 0.12em; + font-weight: 500; } -/* Hero strip (used by home + chip detail) */ +/* Hero — editorial layout with subtle radial spotlight backdrop */ .hero { - padding: 1rem 0 2rem; + position: relative; + padding: 2.5rem 0 2.75rem; + margin-bottom: 1rem; + isolation: isolate; + overflow: hidden; + border-radius: var(--r-lg); +} +.hero::before { + content: ""; + position: absolute; + inset: -1rem -3rem; + z-index: -1; + background: + radial-gradient(45% 70% at 15% 25%, color-mix(in srgb, var(--suite-A) 18%, transparent), transparent 70%), + radial-gradient(40% 60% at 88% 12%, color-mix(in srgb, var(--suite-D) 14%, transparent), transparent 65%), + radial-gradient(50% 80% at 70% 90%, color-mix(in srgb, var(--suite-B) 10%, transparent), transparent 70%); + filter: blur(8px); + pointer-events: none; + opacity: 1; +} +.hero .hero-eyebrow { + margin-bottom: 1.1rem; } .hero h1 { - margin: 0 0 0.6rem; - font-size: 2.1rem; - font-weight: 700; + margin: 0 0 0.85rem; + font-family: var(--font-serif); + font-size: 2.7rem; + font-weight: 500; color: var(--fg-strong); - letter-spacing: -0.015em; - line-height: 1.15; - max-width: 44ch; + letter-spacing: -0.02em; + line-height: 1.1; + max-width: 22ch; +} +.hero h1 em { + font-style: italic; + color: var(--fg-strong); + font-weight: 500; } .hero .tagline { color: var(--fg-muted); - margin: 0 0 1.4rem; + margin: 0 0 1.75rem; font-size: 1.05rem; - line-height: 1.5; - max-width: 64ch; + line-height: 1.6; + max-width: 58ch; } .hero-stats { display: flex; - gap: 2.5rem; + gap: 2rem; flex-wrap: wrap; - margin-bottom: 1.4rem; + margin-bottom: 1.75rem; + padding: 1.25rem 0; + border-top: 1px solid var(--border-soft); + border-bottom: 1px solid var(--border-soft); } .hero-cta { display: flex; @@ -211,11 +253,13 @@ } @media (max-width: 640px) { - .hero { padding: 0.5rem 0 1.5rem; } - .hero h1 { font-size: 1.55rem; } + .hero { padding: 1.5rem 0 1.75rem; } + .hero h1 { font-size: 1.85rem; } .hero .tagline { font-size: 0.95rem; } - .hero-stats { gap: 1.5rem; } - .kpi .kpi-value { font-size: 1.4rem; } + .hero-stats { gap: 1.25rem; padding: 0.9rem 0; } + .kpi { padding-right: 1.25rem; } + .kpi + .kpi::before { left: -0.6rem; } + .kpi .kpi-value { font-size: 1.55rem; } } /* Rank medals */ diff --git a/leaderboard/site/assets/css/home.css b/leaderboard/site/assets/css/home.css index 657efb1f..9164e045 100644 --- a/leaderboard/site/assets/css/home.css +++ b/leaderboard/site/assets/css/home.css @@ -1,16 +1,31 @@ -/* home.css — Home page (suite overview grid + recent additions) */ +/* home.css — Home page (suite overview grid + recent additions) + * + * Each suite card declares its `data-suite="A".."G"` which scopes + * --suite-color (full saturation, used for header bar + accents) + * --suite-tint (~10% alpha, used for #1 row + letter chip bg) + * so the rest of the file can reference them theme-agnostically. + */ + +/* ── Per-suite color tokens via data attribute ──────────── */ +.suite-card[data-suite="A"] { --suite-color: var(--suite-A); } +.suite-card[data-suite="B"] { --suite-color: var(--suite-B); } +.suite-card[data-suite="C"] { --suite-color: var(--suite-C); } +.suite-card[data-suite="D"] { --suite-color: var(--suite-D); } +.suite-card[data-suite="E"] { --suite-color: var(--suite-E); } +.suite-card[data-suite="F"] { --suite-color: var(--suite-F); } +.suite-card[data-suite="G"] { --suite-color: var(--suite-G); } + +/* Default fallback color (also used by .lb-row outside a card) */ +.suite-card, +.lb-row { + --suite-color: var(--accent-2); +} /* ── Suite card ──────────────────────────────────────────── - OpenCompass-style: - ┌─ filled accent header ─────────────┐ - │ A · Single-chip throughput tok/s │ - └────────────────────────────────────┘ - · row · row · row · row · row - centered CTA -*/ + Filled accent header (per-suite color) → tagline → top-5 row + list (with #1 visually featured) → centered "View full →" CTA. */ .suite-card { - /* override .card padding so header fills edge-to-edge */ padding: 0; overflow: hidden; display: flex; @@ -18,10 +33,12 @@ text-decoration: none; color: inherit; box-shadow: var(--shadow-card); + transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease; } -.suite-card.is-link:hover { - text-decoration: none; - transform: translateY(-1px); +.suite-card:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-lift); + border-color: color-mix(in srgb, var(--suite-color) 35%, var(--border-soft)); } .suite-card-head { @@ -29,32 +46,44 @@ align-items: center; justify-content: space-between; gap: 0.75rem; - padding: 0.85rem 1.1rem; - background: var(--accent-3); + padding: 0.9rem 1.1rem; + background: var(--suite-color); color: var(--on-accent); + position: relative; + overflow: hidden; +} +/* Subtle highlight sheen on the colored bar */ +.suite-card-head::after { + content: ""; + position: absolute; + inset: 0; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.08), transparent 60%); + pointer-events: none; } .suite-card-head .suite-head-left { display: flex; align-items: center; gap: 0.7rem; min-width: 0; + position: relative; + z-index: 1; } .suite-card-head .suite-letter { flex: 0 0 auto; - width: 26px; - height: 26px; + width: 28px; + height: 28px; border-radius: 6px; - background: rgba(255, 255, 255, 0.18); + background: rgba(255, 255, 255, 0.2); color: var(--on-accent); - font-weight: 800; - font-size: 0.85rem; + font-weight: 700; + font-size: 0.9rem; + font-family: var(--font-serif); display: inline-flex; align-items: center; justify-content: center; - letter-spacing: 0.02em; } .suite-card-head .suite-title { - font-size: 1rem; + font-size: 1.02rem; font-weight: 600; color: var(--on-accent); letter-spacing: -0.005em; @@ -65,45 +94,52 @@ .suite-card-head .suite-metric-tag { flex: 0 0 auto; font-family: var(--font-mono); - font-size: 0.72rem; + font-size: 0.7rem; font-weight: 600; - padding: 0.18rem 0.55rem; + padding: 0.22rem 0.6rem; border-radius: 999px; - background: rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.22); color: var(--on-accent); letter-spacing: 0.02em; white-space: nowrap; + position: relative; + z-index: 1; } .suite-card-tag { - padding: 0.65rem 1.1rem 0.1rem; - font-size: 0.82rem; + padding: 0.75rem 1.1rem 0.25rem; + font-size: 0.85rem; color: var(--fg-muted); - line-height: 1.4; + line-height: 1.45; } .suite-card-body { - padding: 0.5rem 0.55rem 0.4rem; + padding: 0.4rem 0.55rem 0.35rem; display: flex; flex-direction: column; } .suite-card-foot { - padding: 0.5rem 1.1rem 1rem; + padding: 0.6rem 1.1rem 1.1rem; text-align: center; + border-top: 1px solid var(--border-soft); + margin-top: 0.25rem; } .suite-card-foot a, .suite-card-foot .cta { font-size: 0.82rem; - color: var(--accent); - font-weight: 500; + color: color-mix(in srgb, var(--suite-color) 80%, var(--fg-strong)); + font-weight: 600; + letter-spacing: 0.01em; text-decoration: none; } -.suite-card.is-link:hover .suite-card-foot .cta { color: var(--fg-strong); } +.suite-card:hover .suite-card-foot .cta { + color: var(--suite-color); +} /* Empty suite (no submissions yet) */ .suite-card.empty .suite-card-body { - padding: 1.25rem 1.1rem 1rem; + padding: 1.5rem 1.1rem 1.25rem; color: var(--fg-faint); font-style: italic; font-size: 0.85rem; @@ -117,7 +153,7 @@ grid-template-columns: 28px minmax(0, 1fr) auto; align-items: center; gap: 0.7rem; - padding: 0.5rem 0.6rem; + padding: 0.55rem 0.7rem; border-radius: var(--r-md); text-decoration: none; color: inherit; @@ -131,8 +167,8 @@ content: ""; position: absolute; top: 0; - left: 0.6rem; - right: 0.6rem; + left: 0.7rem; + right: 0.7rem; height: 1px; background: var(--border-soft); pointer-events: none; @@ -140,6 +176,26 @@ .lb-row:hover::before, .lb-row:hover + .lb-row::before { background: transparent; } +/* Featured #1 row — soft suite-color tint background */ +.lb-row.lb-row--featured { + background: color-mix(in srgb, var(--suite-color) 8%, transparent); + padding-top: 0.7rem; + padding-bottom: 0.7rem; + border-left: 2px solid var(--suite-color); + border-radius: 0 var(--r-md) var(--r-md) 0; + margin-left: -2px; +} +.lb-row.lb-row--featured + .lb-row::before { background: transparent; } +.lb-row.lb-row--featured:hover { + background: color-mix(in srgb, var(--suite-color) 14%, transparent); +} +.lb-row.lb-row--featured .lb-row-name { + font-size: 1rem; +} +.lb-row.lb-row--featured .lb-row-score .score-val { + font-size: 1.15rem; +} + .lb-row-rank { width: 24px; height: 24px; @@ -157,12 +213,20 @@ .lb-row-rank.gold { background: var(--gold); color: #1a1206; border-color: transparent; } .lb-row-rank.silver { background: var(--silver); color: #15171c; border-color: transparent; } .lb-row-rank.bronze { background: var(--bronze); color: #1e0e02; border-color: transparent; } +/* Suite-letter circle on recent rows uses the suite color */ +.lb-row-rank.suite-tag-rank { + background: color-mix(in srgb, var(--suite-color) 18%, transparent); + color: var(--suite-color); + border-color: color-mix(in srgb, var(--suite-color) 35%, transparent); + font-family: var(--font-serif); + font-weight: 600; +} .lb-row-main { min-width: 0; display: flex; flex-direction: column; - gap: 0.1rem; + gap: 0.15rem; } .lb-row-name { font-size: 0.95rem; @@ -180,6 +244,7 @@ font-size: 0.74rem; color: var(--fg-muted); line-height: 1.2; + flex-wrap: wrap; } .lb-row-sub .vendor-dot { width: 7px; @@ -197,6 +262,18 @@ .lb-row-sub .vendor-dot[data-vendor="Intel"] { --vendor-color: var(--v-intel); } .lb-row-sub .sub-sep { color: var(--fg-faint); } +.lb-row-sub .suite-tag { + display: inline-flex; + align-items: center; + gap: 0.3rem; + font-family: var(--font-mono); + font-size: 0.7rem; + padding: 0.05rem 0.45rem; + background: color-mix(in srgb, var(--suite-color) 12%, transparent); + color: color-mix(in srgb, var(--suite-color) 80%, var(--fg-strong)); + border-radius: 999px; +} +.lb-row-sub .date { color: var(--fg-faint); font-size: 0.72rem; } .lb-row-score { text-align: right; @@ -206,17 +283,18 @@ line-height: 1.1; } .lb-row-score .score-val { - font-family: var(--font-mono); - font-size: 1.05rem; - font-weight: 700; + font-family: var(--font-serif); + font-size: 1.15rem; + font-weight: 500; color: var(--fg-strong); letter-spacing: -0.01em; } .lb-row-score .score-unit { + font-family: var(--font-mono); font-size: 0.68rem; color: var(--fg-muted); - text-transform: lowercase; letter-spacing: 0.02em; + margin-top: 0.05rem; } /* Empty placeholder row inside a suite with no entries */ @@ -234,32 +312,47 @@ background: var(--bg-elev); border: 1px solid var(--border-soft); border-radius: var(--r-lg); - padding: 0.4rem 0.5rem; + padding: 0.5rem 0.55rem; box-shadow: var(--shadow-card); } -.recent-list .lb-row .lb-row-sub .suite-tag { - display: inline-flex; - align-items: center; - gap: 0.25rem; - font-family: var(--font-mono); - font-size: 0.7rem; - padding: 0.05rem 0.4rem; - background: var(--bg-elev-2); +.recent-list .lb-row { + /* Recent rows scope --suite-color per-row via inline data-suite */ +} +.recent-list .lb-row[data-suite="A"] { --suite-color: var(--suite-A); } +.recent-list .lb-row[data-suite="B"] { --suite-color: var(--suite-B); } +.recent-list .lb-row[data-suite="C"] { --suite-color: var(--suite-C); } +.recent-list .lb-row[data-suite="D"] { --suite-color: var(--suite-D); } +.recent-list .lb-row[data-suite="E"] { --suite-color: var(--suite-E); } +.recent-list .lb-row[data-suite="F"] { --suite-color: var(--suite-F); } +.recent-list .lb-row[data-suite="G"] { --suite-color: var(--suite-G); } + +/* Stats row inside suite card body (used by /suites listing). */ +.suite-stats { + display: flex; + gap: 1.25rem; + flex-wrap: wrap; + font-size: 0.85rem; color: var(--fg-muted); - border-radius: 999px; } -.recent-list .lb-row .lb-row-sub .date { - color: var(--fg-faint); - font-size: 0.72rem; +.suite-stats strong { + color: var(--fg-strong); + font-family: var(--font-serif); + font-weight: 500; + font-size: 1.05rem; + margin-right: 0.2rem; } +/* When the whole card is a link, kill underline + inherit color. */ +a.suite-card { text-decoration: none; color: inherit; } +a.suite-card:hover { text-decoration: none; } + /* ── Responsive ──────────────────────────────────────────── */ @media (max-width: 700px) { - .suite-card-head { padding: 0.7rem 0.85rem; } - .suite-card-tag { padding: 0.5rem 0.85rem 0; font-size: 0.78rem; } - .suite-card-body { padding: 0.35rem 0.4rem 0.3rem; } - .suite-card-foot { padding: 0.5rem 0.85rem 0.85rem; } - .lb-row { padding: 0.4rem 0.5rem; gap: 0.55rem; } - .lb-row-name { font-size: 0.9rem; } - .lb-row-score .score-val { font-size: 0.95rem; } + .suite-card-head { padding: 0.75rem 0.9rem; } + .suite-card-tag { padding: 0.6rem 0.9rem 0.1rem; font-size: 0.82rem; } + .suite-card-body { padding: 0.3rem 0.4rem 0.25rem; } + .suite-card-foot { padding: 0.55rem 0.9rem 0.85rem; } + .lb-row { padding: 0.45rem 0.55rem; gap: 0.55rem; } + .lb-row-name { font-size: 0.92rem; } + .lb-row-score .score-val { font-size: 1.05rem; } } diff --git a/leaderboard/site/assets/css/layout.css b/leaderboard/site/assets/css/layout.css index e5f0ecf3..686bb01d 100644 --- a/leaderboard/site/assets/css/layout.css +++ b/leaderboard/site/assets/css/layout.css @@ -97,26 +97,38 @@ footer a:hover { color: var(--fg); border-bottom-color: var(--fg); text-decorati /* Page section spacing */ .section { - margin-bottom: 3rem; + margin-bottom: 3.5rem; } .section-header { display: flex; - align-items: baseline; + align-items: flex-end; justify-content: space-between; - margin-bottom: 1.25rem; + margin-bottom: 1.5rem; gap: 1rem; flex-wrap: wrap; + padding-bottom: 0.75rem; + border-bottom: 1px solid var(--border-soft); +} +.section-header .section-title { + display: flex; + flex-direction: column; + gap: 0.4rem; + min-width: 0; } .section-header h2 { margin: 0; - font-size: 1.25rem; - font-weight: 600; + font-family: var(--font-serif); + font-size: 1.6rem; + font-weight: 500; color: var(--fg-strong); - letter-spacing: -0.01em; + letter-spacing: -0.015em; + line-height: 1.15; } .section-header .section-sub { color: var(--fg-muted); - font-size: 0.9rem; + font-size: 0.92rem; + max-width: 52ch; + line-height: 1.45; } /* Loading + empty states */ diff --git a/leaderboard/site/assets/js/views/home.js b/leaderboard/site/assets/js/views/home.js index ed9216e2..4631df24 100644 --- a/leaderboard/site/assets/js/views/home.js +++ b/leaderboard/site/assets/js/views/home.js @@ -1,16 +1,15 @@ // views/home.js — Home page (multi-suite overview). // -// Layout (per OpenCompass / user feedback): -// • Hero: title + tagline + KPI strip + CTAs -// • Suite grid: 7 cards, each a small standalone leaderboard (top 5) -// ┌─ accent header: A · Title [tok/s] ─┐ -// │ tagline │ -// │ ① NVIDIA H200 ×1 5,731 tok/s │ -// │ ② AMD MI300X ×1 5,128 tok/s │ -// │ … │ -// │ View full ranking → │ -// └──────────────────────────────────────────────┘ -// • Recent submissions: same row style in one list card +// Layout (editorial, per-suite color): +// • Hero with subtle radial backdrop, serif h1, KPI strip between +// two thin rules, three CTAs. +// • Suite grid: 7 cards, each a small standalone leaderboard. +// Each card declares data-suite="A".."G" so CSS can paint: +// - header bar in the suite's category color +// - featured #1 row tinted in the suite's color (~8% alpha) +// - section-letter chip and CTA in the same hue +// • Recent submissions: same .lb-row, suite letter circle tinted +// in that submission's suite color. import { SUITE_ORDER, SUITE_META, @@ -23,7 +22,8 @@ export function render({ el }) { const s = summary(); el.innerHTML = `
    -

    AI accelerator benchmark — open and reproducible

    + AccelMark · Benchmark suite +

    AI accelerator benchmark — open and reproducible

    Independent measurements of inference performance across vendors. Every result links back to the runner code that produced it. @@ -44,7 +44,10 @@ export function render({ el }) {

    -

    Rankings by workload

    +
    + 01 · Workloads +

    Rankings by workload

    +
    Each suite measures a different real-world workload. Pick one to dive in.
    @@ -52,7 +55,10 @@ export function render({ el }) {
    -

    Recent submissions

    +
    + 02 · Latest activity +

    Recent submissions

    +
    See all →
    @@ -77,6 +83,7 @@ function renderSuiteCard(suiteId) { const card = document.createElement("article"); card.className = "card suite-card" + (empty ? " empty" : ""); + card.setAttribute("data-suite", meta.letter); const rankingsHref = buildHash("/rankings", { suite: suiteId }); @@ -114,11 +121,11 @@ function renderLbRow(row, suiteId, rank) { const meta = SUITE_META[suiteId]; const value = row[meta.primary.key]; const display = formatPrimary(value, suiteId); - // Split formatted value from trailing unit so we can render unit muted. const { num, unit } = splitNumUnit(display); const medal = rank === 1 ? "gold" : rank === 2 ? "silver" : rank === 3 ? "bronze" : ""; + const featured = rank === 1 ? " lb-row--featured" : ""; return ` - + ${rank} ${esc(row._chip_label)} @@ -143,12 +150,14 @@ function renderRecentRow(row) { const display = formatPrimary(metricVal, row.suite); const { num, unit } = splitNumUnit(display); const suiteLabel = row.suite.replace("suite_", "Suite "); + const letter = meta ? meta.letter : "·"; const a = document.createElement("a"); a.className = "lb-row"; a.href = chipHref(row); + a.setAttribute("data-suite", letter); a.innerHTML = ` - + ${esc(row._chip_label)} diff --git a/leaderboard/site/assets/js/views/suites.js b/leaderboard/site/assets/js/views/suites.js index 9cdb8e95..546e6ee6 100644 --- a/leaderboard/site/assets/js/views/suites.js +++ b/leaderboard/site/assets/js/views/suites.js @@ -1,5 +1,5 @@ // views/suites.js — Suites explorer. In commit 1 this is already informative -// (lightweight static reference page), and commit 3 will add per-suite stats. +// (lightweight static reference page); commit 4 will add per-suite stats. import { SUITE_ORDER, SUITE_META, rowsForSuite } from "../data.js"; import { esc, buildHash } from "../utils.js"; @@ -11,16 +11,21 @@ export function render({ el }) { const submissions = rows.length; const chips = new Set(rows.map((r) => r._chip_slug)).size; return ` - -
    -
    ${meta.letter}
    - ${esc(meta.title)} +
    +
    +
    + ${esc(meta.letter)} + ${esc(meta.title)} +
    + ${esc(meta.primary.label)}
    -

    ${esc(meta.tagline)}

    -
    - ${submissions} submission${submissions === 1 ? "" : "s"} - ${chips} chip${chips === 1 ? "" : "s"} - primary: ${esc(meta.primary.label)} +
    ${esc(meta.tagline)}
    +
    +
    + ${submissions} submission${submissions === 1 ? "" : "s"} + ${chips} chip${chips === 1 ? "" : "s"} +
    `; @@ -28,17 +33,24 @@ export function render({ el }) { el.innerHTML = `
    -

    Benchmark suites

    + Reference · Suites +

    Benchmark suites

    - Each suite targets a distinct deployment workload. Click into one to view its full ranking. + Each suite targets a distinct deployment workload. Click into one to + view its full ranking.

    ${cards}
    -

    Why per-suite, not a single score?

    -

    +

    +
    + Methodology +

    Why per-suite, not a single score?

    +
    +
    +

    AI workloads vary widely — a chip optimized for batched offline throughput may rank poorly on interactive latency. Combining all dimensions into a single number hides those trade-offs. AccelMark keeps every suite raw and diff --git a/leaderboard/site/index.html b/leaderboard/site/index.html index ca4d83a8..59c25580 100644 --- a/leaderboard/site/index.html +++ b/leaderboard/site/index.html @@ -5,7 +5,8 @@ AccelMark — AI Accelerator Leaderboard - + + From 3f206b76a26fb7265edfe25c1af06758a83f9b69 Mon Sep 17 00:00:00 2001 From: Liang Juhao Date: Fri, 15 May 2026 16:25:25 +0800 Subject: [PATCH 05/21] style(leaderboard): center hero, 4+3 grid, fw version + submitter, tighter type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round-3 feedback fixes: Hero - "标题挤在左边" — center the entire hero stack (eyebrow, h1, tagline, KPI strip, CTAs). KPI tiles centered between two hairline rules, with vertical dividers between values. - Drop the leading "—" on the hero eyebrow so a centered single line reads cleanly. Row sub line - Framework version now displayed next to the framework name ("vLLM 0.7.3", "SGLang 0.5.6"). Long dev versions (e.g. "0.19.1rc1.dev339+gedc364896") are normalized via shortVersion() which trims at "+" and ".dev" plus a 12-char hard cap. - Submitter handle ("@username") rendered after precision on the wider row-2 cards and on every recent-submissions row. Row-1 compact cards skip it to keep the 25%-width slot readable. - New helpers: utils.shortVersion(), utils.submitterHandle(). Typography - "字体不统一" — reduce serif to display only (h1, h2, hero KPI numerals). Score values, suite letters, and the suite-stats counts now use sans-bold with tabular-nums so column alignment is crisp but the visual stays consistent. - Suite letter chip dropped from serif → sans-bold for a tighter pairing with the sans card title beside it. Layout - 7 suite cards split into 4+3 on a 12-col grid (row 1: A B C D span 3 each; row 2: E F G span 4 each). Row 1 cards use a stacked head (letter + title row → metric pill below right- aligned) so the title gets the full card width — no more "Single-chip throu…" ellipsis. - Row 1 cards show top 4 entries; row 2 cards show top 5 + submitter. - Mobile <=1100px collapses to 2-col, <=640px to single column. Co-authored-by: Cursor --- leaderboard/site/assets/css/components.css | 55 ++++++----- leaderboard/site/assets/css/home.css | 102 +++++++++++++++++---- leaderboard/site/assets/js/utils.js | 30 ++++++ leaderboard/site/assets/js/views/home.js | 75 ++++++++++----- 4 files changed, 201 insertions(+), 61 deletions(-) diff --git a/leaderboard/site/assets/css/components.css b/leaderboard/site/assets/css/components.css index d3b820ba..129f8561 100644 --- a/leaderboard/site/assets/css/components.css +++ b/leaderboard/site/assets/css/components.css @@ -160,14 +160,16 @@ .kpi { display: flex; flex-direction: column; - gap: 0.25rem; - padding-right: 2rem; + gap: 0.3rem; + align-items: center; + padding: 0 1.5rem; position: relative; + min-width: 5.5rem; } .kpi + .kpi::before { content: ""; position: absolute; - left: -1rem; + left: 0; top: 0.4rem; bottom: 0.4rem; width: 1px; @@ -180,23 +182,25 @@ color: var(--fg-strong); letter-spacing: -0.02em; line-height: 1; + font-variant-numeric: tabular-nums; } .kpi .kpi-label { - font-size: 0.72rem; + font-size: 0.7rem; color: var(--fg-muted); text-transform: uppercase; letter-spacing: 0.12em; font-weight: 500; } -/* Hero — editorial layout with subtle radial spotlight backdrop */ +/* Hero — centered editorial layout with subtle radial backdrop */ .hero { position: relative; - padding: 2.5rem 0 2.75rem; - margin-bottom: 1rem; + padding: 3rem 1rem 3.25rem; + margin-bottom: 1.5rem; isolation: isolate; overflow: hidden; border-radius: var(--r-lg); + text-align: center; } .hero::before { content: ""; @@ -204,20 +208,22 @@ inset: -1rem -3rem; z-index: -1; background: - radial-gradient(45% 70% at 15% 25%, color-mix(in srgb, var(--suite-A) 18%, transparent), transparent 70%), - radial-gradient(40% 60% at 88% 12%, color-mix(in srgb, var(--suite-D) 14%, transparent), transparent 65%), - radial-gradient(50% 80% at 70% 90%, color-mix(in srgb, var(--suite-B) 10%, transparent), transparent 70%); - filter: blur(8px); + radial-gradient(45% 70% at 18% 28%, color-mix(in srgb, var(--suite-A) 16%, transparent), transparent 70%), + radial-gradient(40% 60% at 85% 18%, color-mix(in srgb, var(--suite-D) 13%, transparent), transparent 65%), + radial-gradient(55% 80% at 50% 110%, color-mix(in srgb, var(--suite-B) 10%, transparent), transparent 70%); + filter: blur(10px); pointer-events: none; opacity: 1; } .hero .hero-eyebrow { - margin-bottom: 1.1rem; + margin-bottom: 1.4rem; + justify-content: center; } +.hero .hero-eyebrow::before { display: none; } .hero h1 { - margin: 0 0 0.85rem; + margin: 0 auto 1rem; font-family: var(--font-serif); - font-size: 2.7rem; + font-size: 2.9rem; font-weight: 500; color: var(--fg-strong); letter-spacing: -0.02em; @@ -231,35 +237,38 @@ } .hero .tagline { color: var(--fg-muted); - margin: 0 0 1.75rem; + margin: 0 auto 2rem; font-size: 1.05rem; - line-height: 1.6; - max-width: 58ch; + line-height: 1.65; + max-width: 56ch; } .hero-stats { display: flex; - gap: 2rem; + gap: 0; flex-wrap: wrap; - margin-bottom: 1.75rem; + justify-content: center; + margin: 0 auto 2rem; padding: 1.25rem 0; border-top: 1px solid var(--border-soft); border-bottom: 1px solid var(--border-soft); + max-width: 56rem; } .hero-cta { display: flex; gap: 0.6rem; flex-wrap: wrap; + justify-content: center; } @media (max-width: 640px) { - .hero { padding: 1.5rem 0 1.75rem; } + .hero { padding: 1.75rem 0.5rem 2rem; } .hero h1 { font-size: 1.85rem; } .hero .tagline { font-size: 0.95rem; } - .hero-stats { gap: 1.25rem; padding: 0.9rem 0; } - .kpi { padding-right: 1.25rem; } - .kpi + .kpi::before { left: -0.6rem; } + .hero-stats { gap: 0; padding: 0.9rem 0; } + .kpi { padding: 0 0.85rem; min-width: 4rem; } .kpi .kpi-value { font-size: 1.55rem; } + .kpi .kpi-label { font-size: 0.65rem; letter-spacing: 0.08em; } } /* Rank medals */ diff --git a/leaderboard/site/assets/css/home.css b/leaderboard/site/assets/css/home.css index 9164e045..af78969f 100644 --- a/leaderboard/site/assets/css/home.css +++ b/leaderboard/site/assets/css/home.css @@ -21,6 +21,64 @@ --suite-color: var(--accent-2); } +/* ── 4+3 suite grid ───────────────────────────────────────── + Row 1: 4 compact cards, span 3 cols of a 12-col grid. + Row 2: 3 wider cards, span 4 cols. Cards keep the same + internal structure; row-1 just gets a tighter head layout. */ +.suite-grid { + display: grid; + grid-template-columns: repeat(12, minmax(0, 1fr)); + gap: 1.25rem; +} +.suite-grid > .suite-card { grid-column: span 4; } +.suite-grid > .suite-card.is-compact { grid-column: span 3; } + +@media (max-width: 1100px) { + .suite-grid > .suite-card, + .suite-grid > .suite-card.is-compact { grid-column: span 6; } +} +@media (max-width: 640px) { + .suite-grid > .suite-card, + .suite-grid > .suite-card.is-compact { grid-column: span 12; } +} + +/* Compact variant — row 1 cards need a stacked head so the title + gets the full card width (otherwise "Single-chip throughput" + truncates next to the metric pill at 25% width). */ +.suite-card.is-compact .suite-card-head { + flex-direction: column; + align-items: stretch; + gap: 0.55rem; + padding: 0.75rem 0.95rem 0.7rem; +} +.suite-card.is-compact .suite-card-head .suite-head-left { + width: 100%; +} +.suite-card.is-compact .suite-card-head .suite-title { + font-size: 0.95rem; +} +.suite-card.is-compact .suite-card-head .suite-metric-tag { + align-self: flex-end; + font-size: 0.66rem; + padding: 0.18rem 0.55rem; +} +.suite-card.is-compact .suite-card-tag { + padding: 0.6rem 0.95rem 0.15rem; + font-size: 0.8rem; +} +.suite-card.is-compact .suite-card-body { + padding: 0.35rem 0.4rem 0.3rem; +} +.suite-card.is-compact .suite-card-foot { + padding: 0.55rem 0.95rem 0.95rem; +} +.suite-card.is-compact .lb-row { padding: 0.45rem 0.55rem; gap: 0.55rem; } +.suite-card.is-compact .lb-row-name { font-size: 0.9rem; } +.suite-card.is-compact .lb-row-sub { font-size: 0.72rem; } +.suite-card.is-compact .lb-row-score .score-val { font-size: 0.95rem; } +.suite-card.is-compact .lb-row.lb-row--featured .lb-row-name { font-size: 0.95rem; } +.suite-card.is-compact .lb-row.lb-row--featured .lb-row-score .score-val { font-size: 1.02rem; } + /* ── Suite card ──────────────────────────────────────────── Filled accent header (per-suite color) → tagline → top-5 row list (with #1 visually featured) → centered "View full →" CTA. */ @@ -70,14 +128,14 @@ } .suite-card-head .suite-letter { flex: 0 0 auto; - width: 28px; - height: 28px; + width: 26px; + height: 26px; border-radius: 6px; - background: rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.22); color: var(--on-accent); font-weight: 700; - font-size: 0.9rem; - font-family: var(--font-serif); + font-size: 0.82rem; + letter-spacing: 0.02em; display: inline-flex; align-items: center; justify-content: center; @@ -193,7 +251,7 @@ font-size: 1rem; } .lb-row.lb-row--featured .lb-row-score .score-val { - font-size: 1.15rem; + font-size: 1.1rem; } .lb-row-rank { @@ -218,8 +276,9 @@ background: color-mix(in srgb, var(--suite-color) 18%, transparent); color: var(--suite-color); border-color: color-mix(in srgb, var(--suite-color) 35%, transparent); - font-family: var(--font-serif); - font-weight: 600; + font-family: inherit; + font-weight: 700; + letter-spacing: 0.02em; } .lb-row-main { @@ -262,6 +321,17 @@ .lb-row-sub .vendor-dot[data-vendor="Intel"] { --vendor-color: var(--v-intel); } .lb-row-sub .sub-sep { color: var(--fg-faint); } +.lb-row-sub .vendor-name { color: var(--fg-muted); } +.lb-row-sub .fw-ver { + font-family: var(--font-mono); + font-size: 0.7rem; + color: var(--fg-faint); + margin-left: 0.15rem; +} +.lb-row-sub .submitter { + color: var(--fg-muted); + font-feature-settings: "tnum"; +} .lb-row-sub .suite-tag { display: inline-flex; align-items: center; @@ -283,18 +353,18 @@ line-height: 1.1; } .lb-row-score .score-val { - font-family: var(--font-serif); - font-size: 1.15rem; - font-weight: 500; + font-size: 1rem; + font-weight: 600; color: var(--fg-strong); - letter-spacing: -0.01em; + letter-spacing: -0.005em; + font-variant-numeric: tabular-nums; } .lb-row-score .score-unit { font-family: var(--font-mono); font-size: 0.68rem; color: var(--fg-muted); letter-spacing: 0.02em; - margin-top: 0.05rem; + margin-top: 0.1rem; } /* Empty placeholder row inside a suite with no entries */ @@ -336,9 +406,9 @@ } .suite-stats strong { color: var(--fg-strong); - font-family: var(--font-serif); - font-weight: 500; - font-size: 1.05rem; + font-weight: 700; + font-size: 1rem; + font-variant-numeric: tabular-nums; margin-right: 0.2rem; } diff --git a/leaderboard/site/assets/js/utils.js b/leaderboard/site/assets/js/utils.js index cf4185f8..c63c1526 100644 --- a/leaderboard/site/assets/js/utils.js +++ b/leaderboard/site/assets/js/utils.js @@ -51,6 +51,36 @@ export function fmtDate(s) { return String(s).slice(0, 10); } +// Trim long framework versions to something display-friendly. +// "0.7.3" → "0.7.3" +// "0.19.1rc1.dev339+gedc364896" → "0.19.1rc1" +// "0.18.0rc1" → "0.18.0rc1" +// Strategy: cut at "+" (drops git hash), then at ".dev" (drops dev +// build counter). If still long, hard-cap at 12 chars. +export function shortVersion(v) { + if (!v) return ""; + let s = String(v).split("+")[0]; + s = s.split(".dev")[0]; + if (s.length > 12) s = s.slice(0, 12); + return s; +} + +// Normalize a submitter field to a bare handle (no leading @). +// Accepts GitHub login, email, or "Name " — falls back to the +// part before the first @ for emails. +export function submitterHandle(s) { + if (!s) return ""; + let h = String(s).trim(); + if (!h) return ""; + if (h.startsWith("@")) h = h.slice(1); + if (h.includes("<") && h.includes(">")) { + const m = h.match(/<([^>]+)>/); + if (m) h = m[1]; + } + if (h.includes("@")) h = h.split("@")[0]; // email → local part + return h; +} + // Stable slug for URLs. Lowercase, hyphenated, ASCII-safe. export function slugify(s) { return String(s || "") diff --git a/leaderboard/site/assets/js/views/home.js b/leaderboard/site/assets/js/views/home.js index 4631df24..5e243e71 100644 --- a/leaderboard/site/assets/js/views/home.js +++ b/leaderboard/site/assets/js/views/home.js @@ -1,22 +1,24 @@ // views/home.js — Home page (multi-suite overview). // // Layout (editorial, per-suite color): -// • Hero with subtle radial backdrop, serif h1, KPI strip between -// two thin rules, three CTAs. -// • Suite grid: 7 cards, each a small standalone leaderboard. -// Each card declares data-suite="A".."G" so CSS can paint: -// - header bar in the suite's category color -// - featured #1 row tinted in the suite's color (~8% alpha) -// - section-letter chip and CTA in the same hue -// • Recent submissions: same .lb-row, suite letter circle tinted -// in that submission's suite color. +// • Hero is centered (eyebrow + serif h1 + tagline + KPI strip + CTAs). +// • Suite grid: 4 compact cards on row 1 (A B C D, span 3 of 12), +// 3 wider cards on row 2 (E F G, span 4 of 12). +// • Each suite card declares data-suite="A".."G" so CSS scopes +// --suite-color for the header bar, featured #1 row tint, and CTA. +// • Recent submissions: same .lb-row, suite letter circle tinted in +// that submission's suite color. import { SUITE_ORDER, SUITE_META, rowsForSuite, bestPerChipForSuite, summary, recent, formatPrimary, } from "../data.js"; -import { esc, fmtNum, fmtDate, chipHref, buildHash } from "../utils.js"; +import { esc, fmtNum, fmtDate, chipHref, buildHash, shortVersion, submitterHandle } from "../utils.js"; + +// Row-1 (compact) suites — narrower 25%-ish cards. Anything not here +// goes into row 2 (wider 33% cards). +const COMPACT_SUITES = new Set(["suite_A", "suite_B", "suite_C", "suite_D"]); export function render({ el }) { const s = summary(); @@ -50,7 +52,7 @@ export function render({ el }) {

    Each suite measures a different real-world workload. Pick one to dive in.
    -
    +
    @@ -78,11 +80,13 @@ export function render({ el }) { function renderSuiteCard(suiteId) { const meta = SUITE_META[suiteId]; - const top = bestPerChipForSuite(suiteId).slice(0, 5); + const compact = COMPACT_SUITES.has(suiteId); + const limit = compact ? 4 : 5; + const top = bestPerChipForSuite(suiteId).slice(0, limit); const empty = top.length === 0; const card = document.createElement("article"); - card.className = "card suite-card" + (empty ? " empty" : ""); + card.className = "card suite-card" + (compact ? " is-compact" : "") + (empty ? " empty" : ""); card.setAttribute("data-suite", meta.letter); const rankingsHref = buildHash("/rankings", { suite: suiteId }); @@ -107,7 +111,7 @@ function renderSuiteCard(suiteId) { return card; } - const body = top.map((r, i) => renderLbRow(r, suiteId, i + 1)).join(""); + const body = top.map((r, i) => renderLbRow(r, suiteId, i + 1, compact)).join(""); card.innerHTML = ` ${header}
    ${body}
    @@ -117,24 +121,22 @@ function renderSuiteCard(suiteId) { } // Single ranked row used by suite cards. Anchor → chip detail page. -function renderLbRow(row, suiteId, rank) { +// `compact` flag hides the submitter line so narrow row-1 cards stay +// readable; row-2 (wider) cards show it. +function renderLbRow(row, suiteId, rank, compact = false) { const meta = SUITE_META[suiteId]; const value = row[meta.primary.key]; const display = formatPrimary(value, suiteId); const { num, unit } = splitNumUnit(display); const medal = rank === 1 ? "gold" : rank === 2 ? "silver" : rank === 3 ? "bronze" : ""; const featured = rank === 1 ? " lb-row--featured" : ""; + const fwLine = renderFwSub(row, compact); return ` ${rank} ${esc(row._chip_label)} - - - ${esc(row.vendor)} - · - ${esc(row.framework)}${row.precision ? " · " + esc(row.precision) : ""} - + ${fwLine} ${esc(num)} @@ -144,6 +146,29 @@ function renderLbRow(row, suiteId, rank) { `; } +// Sub line under chip name. Always shows vendor + framework@version + +// precision. Submitter is shown on wider rows only (recent list + +// row-2 suite cards) to keep compact rows tight. +function renderFwSub(row, compact = false) { + const fw = row.framework || ""; + const ver = shortVersion(row.framework_version); + const fwVer = ver ? `${esc(fw)} ${esc(ver)}` : esc(fw); + const precision = row.precision ? ` · ${esc(row.precision)}` : ""; + const handle = submitterHandle(row.submitted_by); + const submitter = (!compact && handle) + ? `·@${esc(handle)}` + : ""; + return ` + + + ${esc(row.vendor)} + · + ${fwVer}${precision} + ${submitter} + + `; +} + function renderRecentRow(row) { const meta = SUITE_META[row.suite]; const metricVal = meta ? row[meta.primary.key] : row.primary_metric; @@ -151,6 +176,9 @@ function renderRecentRow(row) { const { num, unit } = splitNumUnit(display); const suiteLabel = row.suite.replace("suite_", "Suite "); const letter = meta ? meta.letter : "·"; + const handle = submitterHandle(row.submitted_by); + const ver = shortVersion(row.framework_version); + const fwVer = ver ? `${esc(row.framework)} ${esc(ver)}` : esc(row.framework); const a = document.createElement("a"); a.className = "lb-row"; @@ -162,9 +190,12 @@ function renderRecentRow(row) { ${esc(row._chip_label)} - ${esc(row.vendor)} + ${esc(row.vendor)} + · + ${fwVer}${row.precision ? " · " + esc(row.precision) : ""} · ${esc(suiteLabel)} + ${handle ? `·@${esc(handle)}` : ""} · ${esc(fmtDate(row.date))} From 3aab55ed9c9ba7ef2a1feeb58c0fe4e6360f4c53 Mon Sep 17 00:00:00 2001 From: Liang Juhao Date: Fri, 15 May 2026 16:49:06 +0800 Subject: [PATCH 06/21] feat(leaderboard): new hero, richer suite headers, vendor coverage section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round-4 feedback fixes: Hero - Two-line title: "AccelMark Leaderboard" + "A Reproducible Multi-Regime AI Accelerator Benchmark" subtitle. The em-dash formulation is gone. - All sans, no serif anywhere ("字体统一用现在的结果的字体"). Background - The hero radial-gradient backdrop felt boxed-in because it was clipped to a rounded rectangle. Promoted it to body::before with absolute positioning, a 720-px height, and a linear mask that fades the bottom to transparent — the wash now blends into the page bg with no visible boundary and scrolls away with the hero. Suite cards - Uniform 3-col grid again (3 / 3 / 1 instead of the 4 + 3 split). All cards same size, all show top 6 entries. - Card header now carries the tagline (moved out of the body) plus a new meta line with the suite's model, baseline precision, total results, and chip-config count — e.g. "Llama 3 · 8B · BF16 baseline · 18 results · 17 chips". - Suite title bumped to 1.15rem 700. - Long titles ("Quantization efficiency") now wrap instead of ellipsing — the head row uses flex 1 1 auto. Data helpers - data.suiteFacts(id) returns the mode model, mode precision, and per-suite counts. - data.vendorBreakdown() returns one entry per vendor with chips, submissions, suite letters, and the vendor's best chip in its most-populated suite. - utils.shortModel() abbreviates HF-style names ("Meta-Llama-3-8B- Instruct" → "Llama 3 · 8B", "Mixtral-8x7B-Instruct-v0.1" → keeps the × variant). New 02 · Coverage section - "Submissions by vendor" — auto-fit grid of vendor cards. Each card shows the vendor's color stripe and dot, chip + submission counts, the headline top chip with its score, and small letter pills tinted by suite color showing which suites the vendor appears in. Pairs with section 01 to give a second lens on the same dataset. Typography - Drop --font-serif token entirely. Sans only for h1/h2/KPI/score/ card title/suite letter. Mono is retained behind the .mono utility but not used anywhere on home. /suites updated to the same suite-card shape so the two pages share visual language. Co-authored-by: Cursor --- leaderboard/site/assets/css/base.css | 29 +- leaderboard/site/assets/css/components.css | 72 ++--- leaderboard/site/assets/css/home.css | 352 ++++++++++++--------- leaderboard/site/assets/css/layout.css | 5 +- leaderboard/site/assets/js/data.js | 78 +++++ leaderboard/site/assets/js/utils.js | 20 ++ leaderboard/site/assets/js/views/home.js | 144 ++++++--- leaderboard/site/assets/js/views/suites.js | 46 +-- 8 files changed, 489 insertions(+), 257 deletions(-) diff --git a/leaderboard/site/assets/css/base.css b/leaderboard/site/assets/css/base.css index f4d85df9..495c0d24 100644 --- a/leaderboard/site/assets/css/base.css +++ b/leaderboard/site/assets/css/base.css @@ -70,9 +70,10 @@ --gap: 1rem; --gap-lg: 1.5rem; - /* Type */ + /* Type — single sans family used everywhere; mono is kept only for + the `.mono` utility class and for the optional `font-variant-numeric` + tabular figures. Serif token removed (was inconsistent feedback). */ --font-sans: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif; - --font-serif: ui-serif, "Iowan Old Style", "Source Serif Pro", "Charter", Georgia, serif; --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; color-scheme: dark; @@ -135,6 +136,28 @@ body { min-height: 100vh; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; + position: relative; + isolation: isolate; +} +/* Ambient page-top wash — soft multi-stop radial that fades into the + page background. Pinned to the top of the document (absolute, not + fixed) so it scrolls away with the hero; no rounded clip, no hard + edge. */ +body::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 720px; + z-index: -1; + pointer-events: none; + background: + radial-gradient(60% 80% at 12% 0%, color-mix(in srgb, var(--suite-A) 13%, transparent), transparent 70%), + radial-gradient(55% 70% at 90% -4%, color-mix(in srgb, var(--suite-D) 11%, transparent), transparent 65%), + radial-gradient(70% 55% at 50% 0%, color-mix(in srgb, var(--suite-B) 6%, transparent), transparent 75%); + -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 40%, transparent 100%); + mask-image: linear-gradient(180deg, #000 0%, #000 40%, transparent 100%); } a { color: var(--accent); text-decoration: none; } @@ -157,7 +180,7 @@ button { .muted { color: var(--fg-muted); } .faint { color: var(--fg-faint); } .mono { font-family: var(--font-mono); } -.serif { font-family: var(--font-serif); } +.tnum { font-variant-numeric: tabular-nums; } .text-xs { font-size: 0.75rem; } .text-sm { font-size: 0.85rem; } .text-lg { font-size: 1.1rem; } diff --git a/leaderboard/site/assets/css/components.css b/leaderboard/site/assets/css/components.css index 129f8561..f7706200 100644 --- a/leaderboard/site/assets/css/components.css +++ b/leaderboard/site/assets/css/components.css @@ -160,7 +160,7 @@ .kpi { display: flex; flex-direction: column; - gap: 0.3rem; + gap: 0.35rem; align-items: center; padding: 0 1.5rem; position: relative; @@ -176,9 +176,8 @@ background: var(--border-soft); } .kpi .kpi-value { - font-family: var(--font-serif); - font-size: 2rem; - font-weight: 500; + font-size: 1.75rem; + font-weight: 600; color: var(--fg-strong); letter-spacing: -0.02em; line-height: 1; @@ -192,55 +191,37 @@ font-weight: 500; } -/* Hero — centered editorial layout with subtle radial backdrop */ +/* Hero — centered text stack, no rounded clip. Background lives on + body::before so the gradient bleeds into the page naturally. */ .hero { - position: relative; - padding: 3rem 1rem 3.25rem; + padding: 3.25rem 1rem 2.75rem; margin-bottom: 1.5rem; - isolation: isolate; - overflow: hidden; - border-radius: var(--r-lg); text-align: center; } -.hero::before { - content: ""; - position: absolute; - inset: -1rem -3rem; - z-index: -1; - background: - radial-gradient(45% 70% at 18% 28%, color-mix(in srgb, var(--suite-A) 16%, transparent), transparent 70%), - radial-gradient(40% 60% at 85% 18%, color-mix(in srgb, var(--suite-D) 13%, transparent), transparent 65%), - radial-gradient(55% 80% at 50% 110%, color-mix(in srgb, var(--suite-B) 10%, transparent), transparent 70%); - filter: blur(10px); - pointer-events: none; - opacity: 1; -} -.hero .hero-eyebrow { - margin-bottom: 1.4rem; - justify-content: center; -} -.hero .hero-eyebrow::before { display: none; } .hero h1 { - margin: 0 auto 1rem; - font-family: var(--font-serif); - font-size: 2.9rem; - font-weight: 500; + margin: 0 auto 0.55rem; + font-size: 3rem; + font-weight: 700; color: var(--fg-strong); - letter-spacing: -0.02em; - line-height: 1.1; + letter-spacing: -0.025em; + line-height: 1.05; max-width: 22ch; } -.hero h1 em { - font-style: italic; +.hero .hero-sub { + margin: 0 auto 1.4rem; + font-size: 1.15rem; color: var(--fg-strong); - font-weight: 500; + font-weight: 400; + letter-spacing: -0.005em; + line-height: 1.35; + max-width: 40ch; } .hero .tagline { color: var(--fg-muted); - margin: 0 auto 2rem; - font-size: 1.05rem; - line-height: 1.65; - max-width: 56ch; + margin: 0 auto 1.9rem; + font-size: 1rem; + line-height: 1.6; + max-width: 54ch; } .hero-stats { @@ -248,7 +229,7 @@ gap: 0; flex-wrap: wrap; justify-content: center; - margin: 0 auto 2rem; + margin: 0 auto 1.75rem; padding: 1.25rem 0; border-top: 1px solid var(--border-soft); border-bottom: 1px solid var(--border-soft); @@ -264,10 +245,11 @@ @media (max-width: 640px) { .hero { padding: 1.75rem 0.5rem 2rem; } .hero h1 { font-size: 1.85rem; } - .hero .tagline { font-size: 0.95rem; } - .hero-stats { gap: 0; padding: 0.9rem 0; } + .hero .hero-sub { font-size: 1rem; max-width: 32ch; } + .hero .tagline { font-size: 0.92rem; } + .hero-stats { padding: 0.9rem 0; } .kpi { padding: 0 0.85rem; min-width: 4rem; } - .kpi .kpi-value { font-size: 1.55rem; } + .kpi .kpi-value { font-size: 1.4rem; } .kpi .kpi-label { font-size: 0.65rem; letter-spacing: 0.08em; } } diff --git a/leaderboard/site/assets/css/home.css b/leaderboard/site/assets/css/home.css index af78969f..e732180e 100644 --- a/leaderboard/site/assets/css/home.css +++ b/leaderboard/site/assets/css/home.css @@ -1,87 +1,44 @@ -/* home.css — Home page (suite overview grid + recent additions) +/* home.css — Home page (suite overview grid + recent additions + vendor coverage) * * Each suite card declares its `data-suite="A".."G"` which scopes * --suite-color (full saturation, used for header bar + accents) * --suite-tint (~10% alpha, used for #1 row + letter chip bg) - * so the rest of the file can reference them theme-agnostically. */ /* ── Per-suite color tokens via data attribute ──────────── */ -.suite-card[data-suite="A"] { --suite-color: var(--suite-A); } -.suite-card[data-suite="B"] { --suite-color: var(--suite-B); } -.suite-card[data-suite="C"] { --suite-color: var(--suite-C); } -.suite-card[data-suite="D"] { --suite-color: var(--suite-D); } -.suite-card[data-suite="E"] { --suite-color: var(--suite-E); } -.suite-card[data-suite="F"] { --suite-color: var(--suite-F); } -.suite-card[data-suite="G"] { --suite-color: var(--suite-G); } +.suite-card[data-suite="A"], .lb-row[data-suite="A"], .vendor-suite-pill[data-suite="A"] { --suite-color: var(--suite-A); } +.suite-card[data-suite="B"], .lb-row[data-suite="B"], .vendor-suite-pill[data-suite="B"] { --suite-color: var(--suite-B); } +.suite-card[data-suite="C"], .lb-row[data-suite="C"], .vendor-suite-pill[data-suite="C"] { --suite-color: var(--suite-C); } +.suite-card[data-suite="D"], .lb-row[data-suite="D"], .vendor-suite-pill[data-suite="D"] { --suite-color: var(--suite-D); } +.suite-card[data-suite="E"], .lb-row[data-suite="E"], .vendor-suite-pill[data-suite="E"] { --suite-color: var(--suite-E); } +.suite-card[data-suite="F"], .lb-row[data-suite="F"], .vendor-suite-pill[data-suite="F"] { --suite-color: var(--suite-F); } +.suite-card[data-suite="G"], .lb-row[data-suite="G"], .vendor-suite-pill[data-suite="G"] { --suite-color: var(--suite-G); } -/* Default fallback color (also used by .lb-row outside a card) */ +/* Default fallback color */ .suite-card, -.lb-row { - --suite-color: var(--accent-2); -} +.lb-row, +.vendor-suite-pill { --suite-color: var(--accent-2); } -/* ── 4+3 suite grid ───────────────────────────────────────── - Row 1: 4 compact cards, span 3 cols of a 12-col grid. - Row 2: 3 wider cards, span 4 cols. Cards keep the same - internal structure; row-1 just gets a tighter head layout. */ +/* ── Uniform 3-col suite grid ──────────────────────────── */ .suite-grid { display: grid; - grid-template-columns: repeat(12, minmax(0, 1fr)); + grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.25rem; } -.suite-grid > .suite-card { grid-column: span 4; } -.suite-grid > .suite-card.is-compact { grid-column: span 3; } - -@media (max-width: 1100px) { - .suite-grid > .suite-card, - .suite-grid > .suite-card.is-compact { grid-column: span 6; } +@media (max-width: 980px) { + .suite-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (max-width: 640px) { - .suite-grid > .suite-card, - .suite-grid > .suite-card.is-compact { grid-column: span 12; } + .suite-grid { grid-template-columns: 1fr; } } -/* Compact variant — row 1 cards need a stacked head so the title - gets the full card width (otherwise "Single-chip throughput" - truncates next to the metric pill at 25% width). */ -.suite-card.is-compact .suite-card-head { - flex-direction: column; - align-items: stretch; - gap: 0.55rem; - padding: 0.75rem 0.95rem 0.7rem; -} -.suite-card.is-compact .suite-card-head .suite-head-left { - width: 100%; -} -.suite-card.is-compact .suite-card-head .suite-title { - font-size: 0.95rem; -} -.suite-card.is-compact .suite-card-head .suite-metric-tag { - align-self: flex-end; - font-size: 0.66rem; - padding: 0.18rem 0.55rem; -} -.suite-card.is-compact .suite-card-tag { - padding: 0.6rem 0.95rem 0.15rem; - font-size: 0.8rem; -} -.suite-card.is-compact .suite-card-body { - padding: 0.35rem 0.4rem 0.3rem; -} -.suite-card.is-compact .suite-card-foot { - padding: 0.55rem 0.95rem 0.95rem; -} -.suite-card.is-compact .lb-row { padding: 0.45rem 0.55rem; gap: 0.55rem; } -.suite-card.is-compact .lb-row-name { font-size: 0.9rem; } -.suite-card.is-compact .lb-row-sub { font-size: 0.72rem; } -.suite-card.is-compact .lb-row-score .score-val { font-size: 0.95rem; } -.suite-card.is-compact .lb-row.lb-row--featured .lb-row-name { font-size: 0.95rem; } -.suite-card.is-compact .lb-row.lb-row--featured .lb-row-score .score-val { font-size: 1.02rem; } - /* ── Suite card ──────────────────────────────────────────── - Filled accent header (per-suite color) → tagline → top-5 row - list (with #1 visually featured) → centered "View full →" CTA. */ + Header (filled in suite color) contains: + · letter + bigger title + right metric pill + · tagline (short description, moved out of the body) + · meta line (model · precision · N rows · M chips) + Body is the lb-row leaderboard only (top 6). + Footer is the centered "View full ranking →" CTA. */ .suite-card { padding: 0; @@ -101,57 +58,63 @@ .suite-card-head { display: flex; - align-items: center; - justify-content: space-between; - gap: 0.75rem; - padding: 0.9rem 1.1rem; + flex-direction: column; + gap: 0.55rem; + padding: 1rem 1.15rem 1.05rem; background: var(--suite-color); color: var(--on-accent); position: relative; overflow: hidden; } -/* Subtle highlight sheen on the colored bar */ .suite-card-head::after { content: ""; position: absolute; inset: 0; - background: linear-gradient(180deg, rgba(255, 255, 255, 0.08), transparent 60%); + background: linear-gradient(180deg, rgba(255, 255, 255, 0.08), transparent 65%); pointer-events: none; } -.suite-card-head .suite-head-left { +.suite-card-head > * { position: relative; z-index: 1; } + +.suite-head-row1 { display: flex; align-items: center; - gap: 0.7rem; + justify-content: space-between; + gap: 0.75rem; min-width: 0; - position: relative; - z-index: 1; } -.suite-card-head .suite-letter { +.suite-head-left { + display: flex; + align-items: center; + gap: 0.65rem; + min-width: 0; + flex: 1 1 auto; +} +.suite-letter { flex: 0 0 auto; - width: 26px; - height: 26px; + width: 28px; + height: 28px; border-radius: 6px; background: rgba(255, 255, 255, 0.22); color: var(--on-accent); font-weight: 700; - font-size: 0.82rem; + font-size: 0.85rem; letter-spacing: 0.02em; display: inline-flex; align-items: center; justify-content: center; } -.suite-card-head .suite-title { - font-size: 1.02rem; - font-weight: 600; +.suite-title { + flex: 1 1 auto; + min-width: 0; + font-size: 1.15rem; + font-weight: 700; color: var(--on-accent); - letter-spacing: -0.005em; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + letter-spacing: -0.01em; + line-height: 1.25; + overflow-wrap: break-word; } -.suite-card-head .suite-metric-tag { +.suite-metric-tag { flex: 0 0 auto; - font-family: var(--font-mono); font-size: 0.7rem; font-weight: 600; padding: 0.22rem 0.6rem; @@ -160,15 +123,33 @@ color: var(--on-accent); letter-spacing: 0.02em; white-space: nowrap; - position: relative; - z-index: 1; } -.suite-card-tag { - padding: 0.75rem 1.1rem 0.25rem; - font-size: 0.85rem; - color: var(--fg-muted); - line-height: 1.45; +.suite-head-tagline { + font-size: 0.86rem; + line-height: 1.4; + color: rgba(255, 255, 255, 0.86); + margin: 0; +} + +.suite-head-meta { + display: flex; + flex-wrap: wrap; + gap: 0.45rem 0.85rem; + font-size: 0.74rem; + color: rgba(255, 255, 255, 0.78); + letter-spacing: 0.01em; + padding-top: 0.15rem; +} +.suite-head-meta .meta-item { + display: inline-flex; + align-items: center; + gap: 0.3rem; +} +.suite-head-meta .meta-item strong { + color: var(--on-accent); + font-weight: 600; + font-variant-numeric: tabular-nums; } .suite-card-body { @@ -178,24 +159,21 @@ } .suite-card-foot { - padding: 0.6rem 1.1rem 1.1rem; + padding: 0.55rem 1.15rem 1rem; text-align: center; border-top: 1px solid var(--border-soft); - margin-top: 0.25rem; + margin-top: 0.2rem; } .suite-card-foot a, .suite-card-foot .cta { font-size: 0.82rem; - color: color-mix(in srgb, var(--suite-color) 80%, var(--fg-strong)); + color: color-mix(in srgb, var(--suite-color) 78%, var(--fg-strong)); font-weight: 600; letter-spacing: 0.01em; text-decoration: none; } -.suite-card:hover .suite-card-foot .cta { - color: var(--suite-color); -} +.suite-card:hover .suite-card-foot .cta { color: var(--suite-color); } -/* Empty suite (no submissions yet) */ .suite-card.empty .suite-card-body { padding: 1.5rem 1.1rem 1.25rem; color: var(--fg-faint); @@ -208,10 +186,10 @@ .lb-row { position: relative; display: grid; - grid-template-columns: 28px minmax(0, 1fr) auto; + grid-template-columns: 26px minmax(0, 1fr) auto; align-items: center; gap: 0.7rem; - padding: 0.55rem 0.7rem; + padding: 0.5rem 0.65rem; border-radius: var(--r-md); text-decoration: none; color: inherit; @@ -225,8 +203,8 @@ content: ""; position: absolute; top: 0; - left: 0.7rem; - right: 0.7rem; + left: 0.65rem; + right: 0.65rem; height: 1px; background: var(--border-soft); pointer-events: none; @@ -237,8 +215,8 @@ /* Featured #1 row — soft suite-color tint background */ .lb-row.lb-row--featured { background: color-mix(in srgb, var(--suite-color) 8%, transparent); - padding-top: 0.7rem; - padding-bottom: 0.7rem; + padding-top: 0.62rem; + padding-bottom: 0.62rem; border-left: 2px solid var(--suite-color); border-radius: 0 var(--r-md) var(--r-md) 0; margin-left: -2px; @@ -247,36 +225,30 @@ .lb-row.lb-row--featured:hover { background: color-mix(in srgb, var(--suite-color) 14%, transparent); } -.lb-row.lb-row--featured .lb-row-name { - font-size: 1rem; -} -.lb-row.lb-row--featured .lb-row-score .score-val { - font-size: 1.1rem; -} +.lb-row.lb-row--featured .lb-row-name { font-size: 1rem; } +.lb-row.lb-row--featured .lb-row-score .score-val { font-size: 1.08rem; } .lb-row-rank { - width: 24px; - height: 24px; + width: 22px; + height: 22px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; - font-family: var(--font-mono); - font-size: 0.72rem; + font-size: 0.7rem; font-weight: 700; background: var(--bg-elev-2); color: var(--fg-muted); border: 1px solid var(--border-soft); + font-variant-numeric: tabular-nums; } .lb-row-rank.gold { background: var(--gold); color: #1a1206; border-color: transparent; } .lb-row-rank.silver { background: var(--silver); color: #15171c; border-color: transparent; } .lb-row-rank.bronze { background: var(--bronze); color: #1e0e02; border-color: transparent; } -/* Suite-letter circle on recent rows uses the suite color */ .lb-row-rank.suite-tag-rank { background: color-mix(in srgb, var(--suite-color) 18%, transparent); color: var(--suite-color); border-color: color-mix(in srgb, var(--suite-color) 35%, transparent); - font-family: inherit; font-weight: 700; letter-spacing: 0.02em; } @@ -302,7 +274,7 @@ gap: 0.4rem; font-size: 0.74rem; color: var(--fg-muted); - line-height: 1.2; + line-height: 1.25; flex-wrap: wrap; } .lb-row-sub .vendor-dot { @@ -323,21 +295,18 @@ .lb-row-sub .sub-sep { color: var(--fg-faint); } .lb-row-sub .vendor-name { color: var(--fg-muted); } .lb-row-sub .fw-ver { - font-family: var(--font-mono); - font-size: 0.7rem; color: var(--fg-faint); + font-size: 0.7rem; margin-left: 0.15rem; + font-variant-numeric: tabular-nums; } -.lb-row-sub .submitter { - color: var(--fg-muted); - font-feature-settings: "tnum"; -} +.lb-row-sub .submitter { color: var(--fg-muted); } .lb-row-sub .suite-tag { display: inline-flex; align-items: center; gap: 0.3rem; - font-family: var(--font-mono); font-size: 0.7rem; + font-weight: 600; padding: 0.05rem 0.45rem; background: color-mix(in srgb, var(--suite-color) 12%, transparent); color: color-mix(in srgb, var(--suite-color) 80%, var(--fg-strong)); @@ -360,14 +329,12 @@ font-variant-numeric: tabular-nums; } .lb-row-score .score-unit { - font-family: var(--font-mono); font-size: 0.68rem; color: var(--fg-muted); letter-spacing: 0.02em; margin-top: 0.1rem; } -/* Empty placeholder row inside a suite with no entries */ .lb-row.placeholder { color: var(--fg-faint); font-style: italic; @@ -385,16 +352,114 @@ padding: 0.5rem 0.55rem; box-shadow: var(--shadow-card); } -.recent-list .lb-row { - /* Recent rows scope --suite-color per-row via inline data-suite */ + +/* ── Coverage by vendor section ─────────────────────────── */ +.vendor-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); + gap: 1.25rem; +} + +.vendor-card { + padding: 1.1rem 1.2rem 1rem; + display: flex; + flex-direction: column; + gap: 0.85rem; + position: relative; + --vendor-color: var(--fg-faint); +} +.vendor-card[data-vendor="NVIDIA"] { --vendor-color: var(--v-nvidia); } +.vendor-card[data-vendor="AMD"] { --vendor-color: var(--v-amd); } +.vendor-card[data-vendor="Apple"] { --vendor-color: var(--v-apple); } +.vendor-card[data-vendor="Google"] { --vendor-color: var(--v-google); } +.vendor-card[data-vendor="Huawei"] { --vendor-color: var(--v-huawei); } +.vendor-card[data-vendor="Moore Threads"] { --vendor-color: var(--v-moore); } +.vendor-card[data-vendor="Intel"] { --vendor-color: var(--v-intel); } +.vendor-card::before { + content: ""; + position: absolute; + top: 0; bottom: 0; left: 0; + width: 3px; + background: var(--vendor-color); + border-radius: var(--r-lg) 0 0 var(--r-lg); +} + +.vendor-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.6rem; +} +.vendor-name-main { + display: flex; + align-items: center; + gap: 0.55rem; + font-weight: 700; + font-size: 1.02rem; + color: var(--fg-strong); +} +.vendor-name-main::before { + content: ""; + width: 9px; + height: 9px; + border-radius: 50%; + background: var(--vendor-color); +} + +.vendor-stats { + display: flex; + gap: 1.25rem; + font-size: 0.78rem; + color: var(--fg-muted); +} +.vendor-stats .stat strong { + display: block; + font-size: 1.2rem; + font-weight: 700; + color: var(--fg-strong); + font-variant-numeric: tabular-nums; + letter-spacing: -0.01em; + line-height: 1; + margin-bottom: 0.15rem; +} + +.vendor-best { + display: flex; + flex-direction: column; + gap: 0.15rem; + font-size: 0.78rem; + color: var(--fg-muted); +} +.vendor-best-label { + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 0.66rem; + font-weight: 600; +} +.vendor-best-chip { + font-size: 0.92rem; + font-weight: 600; + color: var(--fg-strong); +} + +.vendor-suites { + display: flex; + flex-wrap: wrap; + gap: 0.3rem; +} +.vendor-suite-pill { + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + border-radius: 6px; + font-size: 0.7rem; + font-weight: 700; + background: color-mix(in srgb, var(--suite-color) 14%, transparent); + color: var(--suite-color); + border: 1px solid color-mix(in srgb, var(--suite-color) 30%, transparent); } -.recent-list .lb-row[data-suite="A"] { --suite-color: var(--suite-A); } -.recent-list .lb-row[data-suite="B"] { --suite-color: var(--suite-B); } -.recent-list .lb-row[data-suite="C"] { --suite-color: var(--suite-C); } -.recent-list .lb-row[data-suite="D"] { --suite-color: var(--suite-D); } -.recent-list .lb-row[data-suite="E"] { --suite-color: var(--suite-E); } -.recent-list .lb-row[data-suite="F"] { --suite-color: var(--suite-F); } -.recent-list .lb-row[data-suite="G"] { --suite-color: var(--suite-G); } /* Stats row inside suite card body (used by /suites listing). */ .suite-stats { @@ -412,17 +477,16 @@ margin-right: 0.2rem; } -/* When the whole card is a link, kill underline + inherit color. */ a.suite-card { text-decoration: none; color: inherit; } a.suite-card:hover { text-decoration: none; } /* ── Responsive ──────────────────────────────────────────── */ @media (max-width: 700px) { - .suite-card-head { padding: 0.75rem 0.9rem; } - .suite-card-tag { padding: 0.6rem 0.9rem 0.1rem; font-size: 0.82rem; } + .suite-card-head { padding: 0.85rem 0.95rem; } + .suite-title { font-size: 1.05rem; } .suite-card-body { padding: 0.3rem 0.4rem 0.25rem; } - .suite-card-foot { padding: 0.55rem 0.9rem 0.85rem; } + .suite-card-foot { padding: 0.5rem 0.95rem 0.9rem; } .lb-row { padding: 0.45rem 0.55rem; gap: 0.55rem; } .lb-row-name { font-size: 0.92rem; } - .lb-row-score .score-val { font-size: 1.05rem; } + .lb-row-score .score-val { font-size: 0.95rem; } } diff --git a/leaderboard/site/assets/css/layout.css b/leaderboard/site/assets/css/layout.css index 686bb01d..833ba108 100644 --- a/leaderboard/site/assets/css/layout.css +++ b/leaderboard/site/assets/css/layout.css @@ -117,9 +117,8 @@ footer a:hover { color: var(--fg); border-bottom-color: var(--fg); text-decorati } .section-header h2 { margin: 0; - font-family: var(--font-serif); - font-size: 1.6rem; - font-weight: 500; + font-size: 1.4rem; + font-weight: 700; color: var(--fg-strong); letter-spacing: -0.015em; line-height: 1.15; diff --git a/leaderboard/site/assets/js/data.js b/leaderboard/site/assets/js/data.js index 056234e4..1db294f9 100644 --- a/leaderboard/site/assets/js/data.js +++ b/leaderboard/site/assets/js/data.js @@ -214,3 +214,81 @@ export function rankWithinSuite(row) { const idx = list.findIndex((r) => r.submission === row.submission); return idx >= 0 ? { rank: idx + 1, total: list.length } : null; } + +// Suite-level facts derived from current data — model used, +// baseline precision, total submissions, distinct chips. Used in +// the home suite-card header to give buyers immediate context +// without having to dive in. +export function suiteFacts(suiteId) { + if (!_ready) init(); + const rows = _bySuite.get(suiteId) || []; + if (rows.length === 0) { + return { model: null, precision: null, submissions: 0, chips: 0 }; + } + const model = mode(rows.map((r) => r.model).filter(Boolean)); + const precision = mode(rows.map((r) => r.precision).filter(Boolean)); + const chips = new Set(rows.map((r) => r._chip_slug)).size; + return { model, precision, submissions: rows.length, chips }; +} + +// Per-vendor breakdown for the "Coverage by vendor" home section. +// Returns an array sorted by submission count desc, each entry +// containing the vendor name, chip count, submission count, the +// vendor's best chip in its most-populated suite, and the set of +// suite letters the vendor appears in. +export function vendorBreakdown() { + if (!_ready) init(); + const byVendor = groupBy(_rows, (r) => r.vendor); + const out = []; + for (const [vendor, rows] of byVendor.entries()) { + if (!vendor) continue; + const chips = new Set(rows.map((r) => r._chip_slug)); + const suiteCounts = new Map(); + for (const r of rows) { + suiteCounts.set(r.suite, (suiteCounts.get(r.suite) || 0) + 1); + } + // Most-populated suite the vendor appears in — pick the vendor's + // best chip there as the headline "flagship" entry. + let topSuite = null; + let topCount = -1; + for (const [sid, c] of suiteCounts.entries()) { + if (c > topCount) { topCount = c; topSuite = sid; } + } + const bestRow = topSuite ? bestChipForVendorInSuite(vendor, topSuite) : null; + out.push({ + vendor, + chips: chips.size, + submissions: rows.length, + suites: Array.from(suiteCounts.keys()) + .sort((a, b) => a.localeCompare(b)) + .map((sid) => (SUITE_META[sid] && SUITE_META[sid].letter) || null) + .filter(Boolean), + topSuite, + topRow: bestRow, + }); + } + out.sort((a, b) => b.submissions - a.submissions); + return out; +} + +function bestChipForVendorInSuite(vendor, suiteId) { + const meta = SUITE_META[suiteId]; + if (!meta) return null; + const key = meta.primary.key; + const dir = meta.primary.direction; + const rows = (_bySuite.get(suiteId) || []) + .filter((r) => r.vendor === vendor && r[key] !== null && r[key] !== undefined); + if (rows.length === 0) return null; + return dir === "asc" + ? rows.reduce((a, b) => (a[key] <= b[key] ? a : b)) + : rows.reduce((a, b) => (a[key] >= b[key] ? a : b)); +} + +function mode(arr) { + if (!arr || arr.length === 0) return null; + const c = new Map(); + for (const x of arr) c.set(x, (c.get(x) || 0) + 1); + let best = null, bestC = -1; + for (const [k, n] of c.entries()) if (n > bestC) { best = k; bestC = n; } + return best; +} diff --git a/leaderboard/site/assets/js/utils.js b/leaderboard/site/assets/js/utils.js index c63c1526..58ae145d 100644 --- a/leaderboard/site/assets/js/utils.js +++ b/leaderboard/site/assets/js/utils.js @@ -65,6 +65,26 @@ export function shortVersion(v) { return s; } +// Abbreviate verbose HF-style model names for tight UI slots. +// "Meta-Llama-3-8B-Instruct" → "Llama 3 · 8B" +// "Meta-Llama-3-70B-Instruct" → "Llama 3 · 70B" +// "Llama-3.1-8B-Instruct" → "Llama 3.1 · 8B" +// "Qwen2.5-0.5B-Instruct" → "Qwen 2.5 · 0.5B" +// "Mixtral-8x7B-Instruct-v0.1" → "Mixtral 8×7B" +// anything-else → unchanged +export function shortModel(name) { + if (!name) return ""; + let s = String(name); + s = s.replace(/^Meta-/, ""); // Meta-Llama-3 → Llama-3 + s = s.replace(/-Instruct(?:-v[\d.]+)?$/, ""); // drop trailing variant + // Llama / Qwen with a "-" suffix → split family · size + const m = s.match(/^(Llama|Qwen)[-]?(\d+(?:\.\d+)?)-([\d.]+B)$/i); + if (m) return `${m[1]} ${m[2]} · ${m[3]}`; + // Mixtral 8x7B style → keep + s = s.replace(/(\d+)x(\d+B)/i, "$1×$2"); + return s; +} + // Normalize a submitter field to a bare handle (no leading @). // Accepts GitHub login, email, or "Name " — falls back to the // part before the first @ for emails. diff --git a/leaderboard/site/assets/js/views/home.js b/leaderboard/site/assets/js/views/home.js index 5e243e71..fda58137 100644 --- a/leaderboard/site/assets/js/views/home.js +++ b/leaderboard/site/assets/js/views/home.js @@ -1,31 +1,34 @@ // views/home.js — Home page (multi-suite overview). // -// Layout (editorial, per-suite color): -// • Hero is centered (eyebrow + serif h1 + tagline + KPI strip + CTAs). -// • Suite grid: 4 compact cards on row 1 (A B C D, span 3 of 12), -// 3 wider cards on row 2 (E F G, span 4 of 12). -// • Each suite card declares data-suite="A".."G" so CSS scopes -// --suite-color for the header bar, featured #1 row tint, and CTA. -// • Recent submissions: same .lb-row, suite letter circle tinted in -// that submission's suite color. +// Layout: +// • Hero : centered, two-line title (h1 + sub), tagline, KPI strip, CTAs. +// Background gradient lives on body::before (no hard edges). +// • 01 : Suite grid — uniform 3-col, 7 cards, each with a colored +// header (letter + title + metric tag + tagline + meta line: +// model · precision · N results · M chips) and top-6 entries +// in the body. CTA at the bottom. +// • 02 : Coverage by vendor — auto-fit cards, one per vendor. +// • 03 : Recent submissions — same .lb-row primitive, suite-tinted +// letter circle in the rank slot. import { SUITE_ORDER, SUITE_META, - rowsForSuite, bestPerChipForSuite, + bestPerChipForSuite, suiteFacts, vendorBreakdown, summary, recent, formatPrimary, } from "../data.js"; -import { esc, fmtNum, fmtDate, chipHref, buildHash, shortVersion, submitterHandle } from "../utils.js"; +import { + esc, fmtNum, fmtDate, chipHref, buildHash, + shortVersion, shortModel, submitterHandle, +} from "../utils.js"; -// Row-1 (compact) suites — narrower 25%-ish cards. Anything not here -// goes into row 2 (wider 33% cards). -const COMPACT_SUITES = new Set(["suite_A", "suite_B", "suite_C", "suite_D"]); +const TOP_N = 6; export function render({ el }) { const s = summary(); el.innerHTML = `
    - AccelMark · Benchmark suite -

    AI accelerator benchmark — open and reproducible

    +

    AccelMark Leaderboard

    +

    A Reproducible Multi-Regime AI Accelerator Benchmark

    Independent measurements of inference performance across vendors. Every result links back to the runner code that produced it. @@ -50,7 +53,7 @@ export function render({ el }) { 01 · Workloads

    Rankings by workload

    - Each suite measures a different real-world workload. Pick one to dive in. + Seven workloads, each on a fixed model and protocol. Pick one to dive in.
    @@ -58,7 +61,18 @@ export function render({ el }) {
    - 02 · Latest activity + 02 · Coverage +

    Submissions by vendor

    +
    + Who shows up, how many chips, and where they compete. +
    +
    +
    + +
    +
    +
    + 03 · Latest activity

    Recent submissions

    See all → @@ -72,6 +86,11 @@ export function render({ el }) { grid.appendChild(renderSuiteCard(suiteId)); } + const vendorGrid = el.querySelector("#vendor-grid"); + for (const v of vendorBreakdown()) { + vendorGrid.appendChild(renderVendorCard(v)); + } + const recentEl = el.querySelector("#recent-list"); for (const row of recent(8)) { recentEl.appendChild(renderRecentRow(row)); @@ -80,26 +99,29 @@ export function render({ el }) { function renderSuiteCard(suiteId) { const meta = SUITE_META[suiteId]; - const compact = COMPACT_SUITES.has(suiteId); - const limit = compact ? 4 : 5; - const top = bestPerChipForSuite(suiteId).slice(0, limit); + const facts = suiteFacts(suiteId); + const top = bestPerChipForSuite(suiteId).slice(0, TOP_N); const empty = top.length === 0; const card = document.createElement("article"); - card.className = "card suite-card" + (compact ? " is-compact" : "") + (empty ? " empty" : ""); + card.className = "card suite-card" + (empty ? " empty" : ""); card.setAttribute("data-suite", meta.letter); const rankingsHref = buildHash("/rankings", { suite: suiteId }); + const metaLine = renderSuiteMeta(facts); const header = `
    -
    - ${esc(meta.letter)} - ${esc(meta.title)} +
    +
    + ${esc(meta.letter)} + ${esc(meta.title)} +
    + ${esc(meta.primary.label)}
    - ${esc(meta.primary.label)} +

    ${esc(meta.tagline)}

    +
    ${metaLine}
    -
    ${esc(meta.tagline)}
    `; if (empty) { @@ -111,7 +133,7 @@ function renderSuiteCard(suiteId) { return card; } - const body = top.map((r, i) => renderLbRow(r, suiteId, i + 1, compact)).join(""); + const body = top.map((r, i) => renderLbRow(r, suiteId, i + 1)).join(""); card.innerHTML = ` ${header}
    ${body}
    @@ -120,23 +142,37 @@ function renderSuiteCard(suiteId) { return card; } +function renderSuiteMeta(facts) { + const items = []; + if (facts.model) { + items.push(`${esc(shortModel(facts.model))}`); + } + if (facts.precision) { + items.push(`${esc(facts.precision)} baseline`); + } + if (facts.submissions) { + items.push(`${fmtNum(facts.submissions)} results`); + } + if (facts.chips) { + items.push(`${fmtNum(facts.chips)} chips`); + } + return items.join(""); +} + // Single ranked row used by suite cards. Anchor → chip detail page. -// `compact` flag hides the submitter line so narrow row-1 cards stay -// readable; row-2 (wider) cards show it. -function renderLbRow(row, suiteId, rank, compact = false) { +function renderLbRow(row, suiteId, rank) { const meta = SUITE_META[suiteId]; const value = row[meta.primary.key]; const display = formatPrimary(value, suiteId); const { num, unit } = splitNumUnit(display); const medal = rank === 1 ? "gold" : rank === 2 ? "silver" : rank === 3 ? "bronze" : ""; const featured = rank === 1 ? " lb-row--featured" : ""; - const fwLine = renderFwSub(row, compact); return ` ${rank} ${esc(row._chip_label)} - ${fwLine} + ${renderFwSub(row)} ${esc(num)} @@ -146,16 +182,14 @@ function renderLbRow(row, suiteId, rank, compact = false) { `; } -// Sub line under chip name. Always shows vendor + framework@version + -// precision. Submitter is shown on wider rows only (recent list + -// row-2 suite cards) to keep compact rows tight. -function renderFwSub(row, compact = false) { +// Sub line under chip name: vendor + framework@version + precision + submitter. +function renderFwSub(row) { const fw = row.framework || ""; const ver = shortVersion(row.framework_version); const fwVer = ver ? `${esc(fw)} ${esc(ver)}` : esc(fw); const precision = row.precision ? ` · ${esc(row.precision)}` : ""; const handle = submitterHandle(row.submitted_by); - const submitter = (!compact && handle) + const submitter = handle ? `·@${esc(handle)}` : ""; return ` @@ -169,6 +203,40 @@ function renderFwSub(row, compact = false) { `; } +function renderVendorCard(v) { + const div = document.createElement("article"); + div.className = "card vendor-card"; + div.setAttribute("data-vendor", v.vendor); + + const topMeta = v.topRow && v.topSuite ? SUITE_META[v.topSuite] : null; + const topScore = topMeta && v.topRow + ? formatPrimary(v.topRow[topMeta.primary.key], v.topSuite) + : null; + const topLine = v.topRow + ? `${esc(v.topRow._chip_label)}${topScore ? ` · ${esc(topScore)}` : ""}` + : `No submissions yet`; + + const pills = v.suites.map((l) => ` + ${esc(l)} + `).join(""); + + div.innerHTML = ` +
    + ${esc(v.vendor)} +
    +
    +
    ${fmtNum(v.chips)}chips
    +
    ${fmtNum(v.submissions)}submissions
    +
    +
    + Top entry + ${topLine} +
    +
    ${pills}
    + `; + return div; +} + function renderRecentRow(row) { const meta = SUITE_META[row.suite]; const metricVal = meta ? row[meta.primary.key] : row.primary_metric; @@ -209,8 +277,6 @@ function renderRecentRow(row) { } // "5,731 tok/s" → { num: "5,731", unit: "tok/s" } -// "94.5 %" → { num: "94.5", unit: "%" } -// "—" → { num: "—", unit: "" } function splitNumUnit(s) { if (!s) return { num: "—", unit: "" }; const idx = s.search(/\s[A-Za-z%]/); diff --git a/leaderboard/site/assets/js/views/suites.js b/leaderboard/site/assets/js/views/suites.js index 546e6ee6..5d875b2e 100644 --- a/leaderboard/site/assets/js/views/suites.js +++ b/leaderboard/site/assets/js/views/suites.js @@ -1,30 +1,30 @@ // views/suites.js — Suites explorer. In commit 1 this is already informative // (lightweight static reference page); commit 4 will add per-suite stats. -import { SUITE_ORDER, SUITE_META, rowsForSuite } from "../data.js"; -import { esc, buildHash } from "../utils.js"; +import { SUITE_ORDER, SUITE_META, suiteFacts } from "../data.js"; +import { esc, buildHash, shortModel, fmtNum } from "../utils.js"; export function render({ el }) { const cards = SUITE_ORDER.map((id) => { const meta = SUITE_META[id]; - const rows = rowsForSuite(id); - const submissions = rows.length; - const chips = new Set(rows.map((r) => r._chip_slug)).size; + const facts = suiteFacts(id); return `
    -
    - ${esc(meta.letter)} - ${esc(meta.title)} +
    +
    + ${esc(meta.letter)} + ${esc(meta.title)} +
    + ${esc(meta.primary.label)}
    - ${esc(meta.primary.label)} -
    -
    ${esc(meta.tagline)}
    -
    -
    - ${submissions} submission${submissions === 1 ? "" : "s"} - ${chips} chip${chips === 1 ? "" : "s"} +

    ${esc(meta.tagline)}

    +
    + ${facts.model ? `${esc(shortModel(facts.model))}` : ""} + ${facts.precision ? `${esc(facts.precision)} baseline` : ""} + ${fmtNum(facts.submissions)} results + ${fmtNum(facts.chips)} chips
    @@ -33,15 +33,15 @@ export function render({ el }) { el.innerHTML = `
    - Reference · Suites -

    Benchmark suites

    +

    Benchmark Suites

    +

    Seven workloads, each on a fixed model and protocol

    - Each suite targets a distinct deployment workload. Click into one to - view its full ranking. + Each suite targets a distinct deployment regime. Click into one to view + its full ranking.

    -
    ${cards}
    +
    ${cards}
    @@ -51,9 +51,9 @@ export function render({ el }) {

    - AI workloads vary widely — a chip optimized for batched offline throughput - may rank poorly on interactive latency. Combining all dimensions into a - single number hides those trade-offs. AccelMark keeps every suite raw and + AI workloads vary widely. A chip optimized for batched offline throughput + may rank poorly on interactive latency. Combining every dimension into a + single number hides those trade-offs. AccelMark keeps every suite raw and lets you pick the one that matches your deployment.

    From b32445282684641ae6cad6104aed2d4678b726d1 Mon Sep 17 00:00:00 2001 From: Liang Juhao Date: Fri, 15 May 2026 19:33:20 +0800 Subject: [PATCH 07/21] =?UTF-8?q?feat(leaderboard):=20home=20v2=20?= =?UTF-8?q?=E2=80=94=20chip=20cloud,=20submit=20CTA,=20formal=20metric=20l?= =?UTF-8?q?abels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Home page restyled: 8-row leaderboard cards, "Chips on the leaderboard" cloud (tile size = submission count, color = vendor) replacing the old per-vendor grid, and a new "Submit your result" contributor section. - byline on each lb-row now carries framework + version + submitter handle for richer attribution at a glance. - Suite headers use hardcoded model/protocol tokens (not data-derived) so the page does not jitter on submission changes. - Metric labels rolled out to formal names across data.js (tokens/sec, queries/sec, 2x/4x scaling efficiency, quality efficiency, sustained throughput) — these flow into both the home view and any future view. - Hero subtitle pinned with white-space: nowrap and responsive font ramp so it stops awkwardly wrapping on common widths. - Em dashes in number/date fallbacks replaced with hyphens to play nicer with the editorial type and copy-paste. - .screenshots/ ignored (local-only design QA captures). Co-authored-by: Cursor --- .gitignore | 3 + leaderboard/site/assets/css/components.css | 9 +- leaderboard/site/assets/css/home.css | 233 +++++++----- leaderboard/site/assets/css/layout.css | 3 +- leaderboard/site/assets/js/data.js | 340 ++++++++++++++---- leaderboard/site/assets/js/utils.js | 8 +- .../site/assets/js/views/chip-detail.js | 2 +- leaderboard/site/assets/js/views/compare.js | 2 +- leaderboard/site/assets/js/views/home.js | 149 +++++--- 9 files changed, 536 insertions(+), 213 deletions(-) diff --git a/.gitignore b/.gitignore index 953116da..07e5795e 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,6 @@ backup/ # ── Local-only handoff notes (across-session continuity) ──────────────────── .handoff-*.md + +# ── Local-only design QA screenshots ──────────────────────────────────────── +.screenshots/ diff --git a/leaderboard/site/assets/css/components.css b/leaderboard/site/assets/css/components.css index f7706200..fd02fce8 100644 --- a/leaderboard/site/assets/css/components.css +++ b/leaderboard/site/assets/css/components.css @@ -214,7 +214,7 @@ font-weight: 400; letter-spacing: -0.005em; line-height: 1.35; - max-width: 40ch; + white-space: nowrap; } .hero .tagline { color: var(--fg-muted); @@ -242,10 +242,15 @@ justify-content: center; } +@media (max-width: 880px) { + .hero .hero-sub { font-size: 1rem; } +} +@media (max-width: 720px) { + .hero .hero-sub { font-size: 0.92rem; white-space: normal; max-width: 36ch; } +} @media (max-width: 640px) { .hero { padding: 1.75rem 0.5rem 2rem; } .hero h1 { font-size: 1.85rem; } - .hero .hero-sub { font-size: 1rem; max-width: 32ch; } .hero .tagline { font-size: 0.92rem; } .hero-stats { padding: 0.9rem 0; } .kpi { padding: 0 0.85rem; min-width: 4rem; } diff --git a/leaderboard/site/assets/css/home.css b/leaderboard/site/assets/css/home.css index e732180e..4d498279 100644 --- a/leaderboard/site/assets/css/home.css +++ b/leaderboard/site/assets/css/home.css @@ -189,7 +189,7 @@ grid-template-columns: 26px minmax(0, 1fr) auto; align-items: center; gap: 0.7rem; - padding: 0.5rem 0.65rem; + padding: 0.62rem 0.7rem; border-radius: var(--r-md); text-decoration: none; color: inherit; @@ -215,8 +215,8 @@ /* Featured #1 row — soft suite-color tint background */ .lb-row.lb-row--featured { background: color-mix(in srgb, var(--suite-color) 8%, transparent); - padding-top: 0.62rem; - padding-bottom: 0.62rem; + padding-top: 0.78rem; + padding-bottom: 0.78rem; border-left: 2px solid var(--suite-color); border-radius: 0 var(--r-md) var(--r-md) 0; margin-left: -2px; @@ -269,13 +269,30 @@ white-space: nowrap; } .lb-row-sub { - display: inline-flex; + display: flex; align-items: center; gap: 0.4rem; font-size: 0.74rem; color: var(--fg-muted); line-height: 1.25; - flex-wrap: wrap; + flex-wrap: nowrap; + min-width: 0; +} +.lb-row-sub .fw-line { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.lb-row-byline { + display: block; + font-size: 0.7rem; + color: var(--fg-faint); + line-height: 1.25; + margin-top: 0.05rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .lb-row-sub .vendor-dot { width: 7px; @@ -292,15 +309,14 @@ .lb-row-sub .vendor-dot[data-vendor="Moore Threads"] { --vendor-color: var(--v-moore); } .lb-row-sub .vendor-dot[data-vendor="Intel"] { --vendor-color: var(--v-intel); } -.lb-row-sub .sub-sep { color: var(--fg-faint); } -.lb-row-sub .vendor-name { color: var(--fg-muted); } +.lb-row-sub .sub-sep { color: var(--fg-faint); flex: 0 0 auto; } +.lb-row-sub .vendor-name { color: var(--fg-muted); flex: 0 0 auto; } .lb-row-sub .fw-ver { color: var(--fg-faint); font-size: 0.7rem; margin-left: 0.15rem; font-variant-numeric: tabular-nums; } -.lb-row-sub .submitter { color: var(--fg-muted); } .lb-row-sub .suite-tag { display: inline-flex; align-items: center; @@ -311,8 +327,8 @@ background: color-mix(in srgb, var(--suite-color) 12%, transparent); color: color-mix(in srgb, var(--suite-color) 80%, var(--fg-strong)); border-radius: 999px; + flex: 0 0 auto; } -.lb-row-sub .date { color: var(--fg-faint); font-size: 0.72rem; } .lb-row-score { text-align: right; @@ -353,112 +369,151 @@ box-shadow: var(--shadow-card); } -/* ── Coverage by vendor section ─────────────────────────── */ -.vendor-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); - gap: 1.25rem; +/* ── Chip cloud (02 · Coverage) ──────────────────────────── */ +.chip-cloud { + display: flex; + flex-wrap: wrap; + gap: 0.55rem; + align-items: center; + justify-content: center; + padding: 1rem 0.5rem 0.5rem; } -.vendor-card { - padding: 1.1rem 1.2rem 1rem; - display: flex; - flex-direction: column; - gap: 0.85rem; - position: relative; +.chip-tile { --vendor-color: var(--fg-faint); + display: inline-flex; + align-items: baseline; + gap: 0.5rem; + padding: 0.45rem 0.8rem; + border-radius: 999px; + background: color-mix(in srgb, var(--vendor-color) 14%, var(--bg-elev)); + border: 1px solid color-mix(in srgb, var(--vendor-color) 32%, transparent); + color: var(--fg-strong); + font-weight: 600; + font-size: 0.85rem; + letter-spacing: -0.005em; + text-decoration: none; + transition: transform 120ms ease, background 120ms ease, border-color 120ms ease; + white-space: nowrap; } -.vendor-card[data-vendor="NVIDIA"] { --vendor-color: var(--v-nvidia); } -.vendor-card[data-vendor="AMD"] { --vendor-color: var(--v-amd); } -.vendor-card[data-vendor="Apple"] { --vendor-color: var(--v-apple); } -.vendor-card[data-vendor="Google"] { --vendor-color: var(--v-google); } -.vendor-card[data-vendor="Huawei"] { --vendor-color: var(--v-huawei); } -.vendor-card[data-vendor="Moore Threads"] { --vendor-color: var(--v-moore); } -.vendor-card[data-vendor="Intel"] { --vendor-color: var(--v-intel); } -.vendor-card::before { - content: ""; - position: absolute; - top: 0; bottom: 0; left: 0; - width: 3px; - background: var(--vendor-color); - border-radius: var(--r-lg) 0 0 var(--r-lg); +.chip-tile:hover { + background: color-mix(in srgb, var(--vendor-color) 26%, var(--bg-elev)); + border-color: var(--vendor-color); + transform: translateY(-1px); + text-decoration: none; } +.chip-tile.size-sm { font-size: 0.8rem; padding: 0.38rem 0.7rem; } +.chip-tile.size-md { font-size: 0.92rem; padding: 0.45rem 0.85rem; } +.chip-tile.size-lg { font-size: 1.05rem; padding: 0.55rem 1.05rem; } +.chip-tile.size-xl { font-size: 1.25rem; padding: 0.65rem 1.25rem; } -.vendor-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.6rem; +.chip-tile[data-vendor="NVIDIA"] { --vendor-color: var(--v-nvidia); } +.chip-tile[data-vendor="AMD"] { --vendor-color: var(--v-amd); } +.chip-tile[data-vendor="Apple"] { --vendor-color: var(--v-apple); } +.chip-tile[data-vendor="Google"] { --vendor-color: var(--v-google); } +.chip-tile[data-vendor="Huawei"] { --vendor-color: var(--v-huawei); } +.chip-tile[data-vendor="Moore Threads"] { --vendor-color: var(--v-moore); } +.chip-tile[data-vendor="Intel"] { --vendor-color: var(--v-intel); } + +.chip-tile-count { + font-size: 0.7em; + font-weight: 500; + color: var(--fg-muted); + font-variant-numeric: tabular-nums; + padding-left: 0.4rem; + border-left: 1px solid color-mix(in srgb, var(--vendor-color) 30%, transparent); } -.vendor-name-main { + +.cloud-legend { display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.4rem 1.5rem; + margin-top: 1rem; + padding: 0.85rem 1rem; + border-top: 1px solid var(--border-soft); + border-bottom: 1px solid var(--border-soft); + font-size: 0.8rem; + color: var(--fg-muted); +} +.cloud-legend-item { + --vendor-color: var(--fg-faint); + display: inline-flex; align-items: center; - gap: 0.55rem; - font-weight: 700; - font-size: 1.02rem; - color: var(--fg-strong); + gap: 0.4rem; } -.vendor-name-main::before { - content: ""; +.cloud-legend-item[data-vendor="NVIDIA"] { --vendor-color: var(--v-nvidia); } +.cloud-legend-item[data-vendor="AMD"] { --vendor-color: var(--v-amd); } +.cloud-legend-item[data-vendor="Apple"] { --vendor-color: var(--v-apple); } +.cloud-legend-item[data-vendor="Google"] { --vendor-color: var(--v-google); } +.cloud-legend-item[data-vendor="Huawei"] { --vendor-color: var(--v-huawei); } +.cloud-legend-item[data-vendor="Moore Threads"] { --vendor-color: var(--v-moore); } +.cloud-legend-item[data-vendor="Intel"] { --vendor-color: var(--v-intel); } +.cloud-legend-item .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--vendor-color); + flex: 0 0 auto; } - -.vendor-stats { - display: flex; - gap: 1.25rem; - font-size: 0.78rem; - color: var(--fg-muted); -} -.vendor-stats .stat strong { - display: block; - font-size: 1.2rem; - font-weight: 700; +.cloud-legend-item .name { color: var(--fg-strong); + font-weight: 600; +} +.cloud-legend-item .meta { + color: var(--fg-muted); font-variant-numeric: tabular-nums; - letter-spacing: -0.01em; - line-height: 1; - margin-bottom: 0.15rem; } -.vendor-best { - display: flex; - flex-direction: column; - gap: 0.15rem; - font-size: 0.78rem; - color: var(--fg-muted); +/* ── Submit your result (04 · Contribute) ───────────────── */ +/* Borderless, blends into the page with a soft radial wash that + fades to the page background — visually quieter than a card. */ +.submit-section { margin: 4rem 0 1rem; } +.submit-card { + position: relative; + padding: 3rem 1rem 2.5rem; + text-align: center; + border: none; + background: transparent; } -.vendor-best-label { - text-transform: uppercase; - letter-spacing: 0.08em; - font-size: 0.66rem; - font-weight: 600; +.submit-card::before { + content: ""; + position: absolute; + inset: -1.5rem -4rem; + z-index: -1; + background: + radial-gradient(42% 80% at 26% 50%, color-mix(in srgb, var(--suite-A) 11%, transparent), transparent 65%), + radial-gradient(42% 80% at 74% 50%, color-mix(in srgb, var(--suite-D) 11%, transparent), transparent 65%); + -webkit-mask-image: radial-gradient(80% 95% at 50% 50%, #000 55%, transparent 100%); + mask-image: radial-gradient(80% 95% at 50% 50%, #000 55%, transparent 100%); + pointer-events: none; } -.vendor-best-chip { - font-size: 0.92rem; - font-weight: 600; +.submit-card .eyebrow { justify-content: center; } +.submit-card .eyebrow::before { display: none; } +.submit-title { + font-size: 2.85rem; + font-weight: 700; + margin: 0.6rem 0 0.95rem; color: var(--fg-strong); + letter-spacing: -0.025em; + line-height: 1.05; } - -.vendor-suites { +.submit-body { + margin: 0 auto 1.75rem; + color: var(--fg-muted); + font-size: 1.08rem; + line-height: 1.65; + max-width: 58ch; +} +@media (max-width: 720px) { + .submit-title { font-size: 2.2rem; } + .submit-body { font-size: 1rem; } +} +.submit-cta { display: flex; flex-wrap: wrap; - gap: 0.3rem; -} -.vendor-suite-pill { - display: inline-flex; - align-items: center; justify-content: center; - width: 22px; - height: 22px; - border-radius: 6px; - font-size: 0.7rem; - font-weight: 700; - background: color-mix(in srgb, var(--suite-color) 14%, transparent); - color: var(--suite-color); - border: 1px solid color-mix(in srgb, var(--suite-color) 30%, transparent); + gap: 0.6rem; } /* Stats row inside suite card body (used by /suites listing). */ diff --git a/leaderboard/site/assets/css/layout.css b/leaderboard/site/assets/css/layout.css index 833ba108..4ee18be0 100644 --- a/leaderboard/site/assets/css/layout.css +++ b/leaderboard/site/assets/css/layout.css @@ -126,8 +126,9 @@ footer a:hover { color: var(--fg); border-bottom-color: var(--fg); text-decorati .section-header .section-sub { color: var(--fg-muted); font-size: 0.92rem; - max-width: 52ch; line-height: 1.45; + text-align: right; + flex: 0 1 auto; } /* Loading + empty states */ diff --git a/leaderboard/site/assets/js/data.js b/leaderboard/site/assets/js/data.js index 1db294f9..8194b525 100644 --- a/leaderboard/site/assets/js/data.js +++ b/leaderboard/site/assets/js/data.js @@ -14,48 +14,225 @@ import { groupBy, maxBy, chipSlug } from "./utils.js"; // // `primary.scale` multiplies raw value at display (e.g. 0.945 → 94.5 %). // `primary.decimals` overrides automatic decimal selection. +// Suite workload constants — fixed per suite definition (suites/README.md). +// `inputTokens` / `outputTokens` are the dataset p50s used at benchmark +// time and are NOT derived from data files; they're part of the suite +// contract and only change with a suite revision. export const SUITE_META = { suite_A: { letter: "A", title: "Single-chip throughput", tagline: "How fast can one accelerator serve an 8B model?", - primary: { key: "offline_throughput", label: "tok/s", direction: "desc", unit: "tok/s" }, + description: + "The canonical bandwidth-bound regime. 8B Llama on a single accelerator is small enough to fit comfortably in HBM, large enough that decode is memory-bandwidth-bound rather than compute-bound. This is the bread-and-butter serving workload that anchors most other LLM benchmarks, and the suite where vendor marketing numbers usually land.", + primary: { key: "offline_throughput", label: "tokens/sec", direction: "desc", unit: "tokens/sec" }, + workload: { + model: "meta-llama/Meta-Llama-3-8B-Instruct", + chips: "1", + precision: "BF16", + dataset: "sharegpt_standard_v1", + inputTokens: "~280", + outputTokens: "~310", + }, + scenarios: [ + { name: "accuracy", isExtra: false, + desc: "MMLU subset score against the baseline. Gate for a valid submission." }, + { name: "offline", isExtra: false, + desc: "Max throughput with all requests batched at once.", + metric: { key: "offline_throughput", label: "tokens/sec", direction: "desc", unit: "tokens/sec" } }, + { name: "online", isExtra: false, + desc: "Highest QPS that meets the 500 ms p99 TTFT SLA under Poisson arrivals.", + metric: { key: "online_max_qps", label: "queries/sec", direction: "desc", unit: "queries/sec" } }, + { name: "interactive", isExtra: true, + desc: "Single-stream first-token latency. No concurrency.", + metric: { key: "interactive_ttft_p99", label: "TTFT p99", direction: "asc", unit: "ms", decimals: 0 } }, + { name: "sustained", isExtra: true, + desc: "30 min fixed-concurrency load. Reports throughput stability and throttle ratio.", + metric: { key: "sustained_throughput", label: "sustained throughput", direction: "desc", unit: "tokens/sec" } }, + { name: "speculative", isExtra: true, + desc: "Offline workload with a 1B draft model loaded. Reports acceptance rate." }, + { name: "burst", isExtra: true, + desc: "TTFT p99 during 5x burst windows versus steady. KV pressure test." }, + ], }, suite_B: { letter: "B", title: "Multi-chip throughput", - tagline: "Aggregate throughput across 8 chips.", - primary: { key: "offline_throughput", label: "tok/s", direction: "desc", unit: "tok/s" }, + tagline: "Large-model serving across multiple chips.", + description: + "70B Llama distributed across multiple accelerators. Two effects compound: the model itself no longer fits on one chip (capacity-bound) and tensor-parallel inference shards KV cache, activations, and all-reduce traffic over the interconnect. Both the framework's TP path and the chip's NVLink / Infinity Fabric / scale-out fabric come under test here.", + primary: { key: "offline_throughput", label: "tokens/sec", direction: "desc", unit: "tokens/sec" }, + workload: { + model: "meta-llama/Meta-Llama-3-70B-Instruct", + chips: "flexible (typ. 4 / 8)", + precision: "BF16", + dataset: "sharegpt_standard_v1", + inputTokens: "~280", + outputTokens: "~310", + }, + scenarios: [ + { name: "accuracy", isExtra: false, + desc: "MMLU subset score against the 70B baseline." }, + { name: "offline", isExtra: false, + desc: "Aggregate throughput across N chips serving the 70B model.", + metric: { key: "offline_throughput", label: "tokens/sec", direction: "desc", unit: "tokens/sec" } }, + { name: "online", isExtra: false, + desc: "Highest QPS that meets the 500 ms p99 TTFT SLA at 70B scale.", + metric: { key: "online_max_qps", label: "queries/sec", direction: "desc", unit: "queries/sec" } }, + { name: "interactive", isExtra: true, + desc: "Single-stream TTFT at 70B. Decode-bound." }, + { name: "sustained", isExtra: true, + desc: "30 min fixed load; concurrency 4 (70B leaves less KV headroom than 8B).", + metric: { key: "sustained_throughput", label: "sustained throughput", direction: "desc", unit: "tokens/sec" } }, + { name: "burst", isExtra: true, + desc: "Burst vs steady TTFT p99 at 70B scale." }, + ], }, suite_C: { letter: "C", title: "Quantization efficiency", - tagline: "Quality-adjusted throughput across quantization formats.", - primary: { key: "quant_quality_eff", label: "quality eff.", direction: "desc", unit: "" }, + tagline: "Quality-adjusted throughput across precision formats.", + description: + "The bandwidth-to-compute transition. The same 8B model is run at five precision formats (BF16, FP8, W8A8, W8A16, W4A16); quality efficiency multiplies throughput speedup by the accuracy drop so a chip can't trade quality for speed silently. Reveals which chips have working low-precision tensor cores and which fall back to BF16 on the same instruction.", + primary: { key: "quant_quality_eff", label: "quality efficiency", direction: "desc", unit: "" }, + workload: { + model: "meta-llama/Llama-3.1-8B-Instruct", + chips: "1", + precision: "BF16, FP8, W8A8, W8A16, W4A16", + dataset: "sharegpt_standard_v1", + inputTokens: "~280", + outputTokens: "~310", + }, + scenarios: [ + { name: "accuracy", isExtra: false, + desc: "Per-format accuracy gate (each format has its own threshold)." }, + { name: "offline (×5 formats)", isExtra: false, + desc: "Offline throughput at each precision. Quality efficiency = throughput × accuracy.", + metric: { key: "quant_quality_eff", label: "quality efficiency", direction: "desc", unit: "" } }, + { name: "online", isExtra: true, + desc: "Online QPS sweep per format. Extra: 5 formats × QPS levels is expensive." }, + { name: "sustained", isExtra: true, + desc: "15 min sustained load per format." }, + ], }, suite_D: { letter: "D", - title: "Interactive latency", - tagline: "Single-stream first-token & per-token latency.", - primary: { key: "interactive_ttft_p99", label: "TTFT p99", direction: "asc", unit: "ms", decimals: 0 }, + title: "Long-context inference", + tagline: "28K-token prefill, compute-bound regime.", + description: + "Compute-bound prefill. ~28K-token prompts push arithmetic intensity past the roofline knee, so chips with more raw FLOPS pull ahead of bandwidth-rich ones. The output cap (256 tokens) keeps decode short on purpose; this suite isolates the prefill side and is where Suite A's bandwidth-bound rankings begin to invert.", + primary: { key: "offline_throughput", label: "tokens/sec", direction: "desc", unit: "tokens/sec" }, + workload: { + model: "meta-llama/Llama-3.1-8B-Instruct", + chips: "1", + precision: "BF16; max_model_len 30,208", + dataset: "sharegpt_longctx_v1", + inputTokens: "~28K", + outputTokens: "≤256", + }, + scenarios: [ + { name: "accuracy", isExtra: false, + desc: "MMLU gate against the 8B Llama-3.1 baseline." }, + { name: "offline", isExtra: false, + desc: "Offline throughput at ~28K input tokens. Prefill-bound, tests raw FLOPS.", + metric: { key: "offline_throughput", label: "tokens/sec", direction: "desc", unit: "tokens/sec" } }, + { name: "interactive", isExtra: true, + desc: "Long-context TTFT (~11 s per request at 28K). p90 is primary." }, + { name: "online", isExtra: true, + desc: "Sub-QPS levels (0.5 / 1 / 2). Rate-bound at long context." }, + { name: "sustained", isExtra: true, + desc: "30 min sustained at concurrency 8. Throttle ratio is the headline." }, + { name: "speculative", isExtra: true, + desc: "Long-context offline with 1B draft model. Prefill-bound speculative." }, + ], }, suite_E: { letter: "E", - title: "Scaling efficiency", - tagline: "How well does throughput scale to 2 / 4 / 8 chips?", - primary: { key: "scaling_efficiency_2x", label: "2× efficiency", direction: "desc", unit: "%", scale: 100, decimals: 1 }, + title: "Multi-chip scaling efficiency", + tagline: "How well does 8B throughput scale to 2 / 4 / 8 chips?", + description: + "The Amdahl penalty in numbers. The same 8B model runs at 1×, 2×, and (optionally) 4× / 8× chip counts; the headline metric is 2× scaling efficiency = T_2× / (2 · T_1×). Reveals NVLink / Infinity Fabric / PCIe ceilings, and exposes flagships whose per-chip throughput grew faster than the interconnect did.", + primary: { key: "scaling_efficiency_2x", label: "2× scaling efficiency", direction: "desc", unit: "%", scale: 100, decimals: 1 }, + workload: { + model: "meta-llama/Meta-Llama-3-8B-Instruct", + chips: "1× / 2× required; 4× / 8× optional", + precision: "BF16", + dataset: "sharegpt_standard_v1", + inputTokens: "~280", + outputTokens: "~310", + }, + scenarios: [ + { name: "offline (1× / 2×)", isExtra: false, + desc: "Two-chip scaling efficiency vs single chip. Required for a valid submission.", + metric: { key: "scaling_efficiency_2x", label: "2× scaling efficiency", direction: "desc", unit: "%", scale: 100, decimals: 1 } }, + { name: "offline (4×)", isExtra: false, + desc: "Four-chip scaling efficiency. Optional but commonly reported.", + metric: { key: "scaling_efficiency_4x", label: "4× scaling efficiency", direction: "desc", unit: "%", scale: 100, decimals: 1 } }, + { name: "offline (8×)", isExtra: false, + desc: "Eight-chip scaling. Communication overhead is the binding constraint here." }, + ], }, suite_F: { letter: "F", - title: "Edge / low-power", - tagline: "Smaller models on commodity & edge hardware.", - primary: { key: "offline_throughput", label: "tok/s", direction: "desc", unit: "tok/s" }, + title: "Edge / consumer hardware", + tagline: "Small models on single-GPU edge hardware.", + description: + "The pure-bandwidth lower bound. Qwen2.5-0.5B with ~95-token prompts strips away residual compute interference and short-circuits prefill, exposing raw HBM headroom and software overhead. Commodity GPUs (RTX 4090, A6000) tend to be most competitive per dollar here, and the suite doubles as a regression check for low-VRAM deployments.", + primary: { key: "offline_throughput", label: "tokens/sec", direction: "desc", unit: "tokens/sec" }, + workload: { + model: "Qwen/Qwen2.5-0.5B-Instruct", + chips: "1 (≥4 GB VRAM)", + precision: "BF16", + dataset: "sharegpt_edge_v1", + inputTokens: "~95", + outputTokens: "~150", + }, + scenarios: [ + { name: "accuracy", isExtra: false, + desc: "MMLU gate against the 0.5B baseline." }, + { name: "offline", isExtra: false, + desc: "Offline throughput on the edge dataset (~95 tok prompts).", + metric: { key: "offline_throughput", label: "tokens/sec", direction: "desc", unit: "tokens/sec" } }, + { name: "online", isExtra: false, + desc: "Max QPS at the standard 500 ms p99 TTFT SLA.", + metric: { key: "online_max_qps", label: "queries/sec", direction: "desc", unit: "queries/sec" } }, + { name: "interactive", isExtra: false, + desc: "Single-stream TTFT on consumer hardware.", + metric: { key: "interactive_ttft_p99", label: "TTFT p99", direction: "asc", unit: "ms", decimals: 0 } }, + { name: "sustained", isExtra: true, + desc: "15 min sustained load (shorter than datacenter suites)." }, + ], }, suite_G: { letter: "G", - title: "Sustained load", - tagline: "Throughput stability under prolonged load (30 min).", - primary: { key: "sustained_throughput", label: "tok/s sustained", direction: "desc", unit: "tok/s" }, + title: "Mixture-of-Experts (MoE)", + tagline: "Sparse routing; bandwidth-bound multi-chip serving.", + description: + "Sparse activation. Mixtral 8×7B activates only 2 of 8 experts per token, which keeps arithmetic intensity below dense 8B inference even at multi-chip scale. Chips with high aggregate HBM bandwidth (HBM3e generation) pay off here; pure-FLOPS advantages from compute-bound suites don't translate.", + primary: { key: "sustained_throughput", label: "tokens/sec", direction: "desc", unit: "tokens/sec" }, + workload: { + model: "mistralai/Mixtral-8x7B-Instruct-v0.1", + chips: "≥2 (auto)", + precision: "BF16", + dataset: "sharegpt_standard_v1", + inputTokens: "~280", + outputTokens: "~310", + }, + scenarios: [ + { name: "accuracy", isExtra: false, + desc: "MMLU gate against the Mixtral baseline." }, + { name: "offline", isExtra: false, + desc: "Aggregate MoE throughput. Only 2 of 8 experts activate per token.", + metric: { key: "offline_throughput", label: "tokens/sec", direction: "desc", unit: "tokens/sec" } }, + { name: "online", isExtra: false, + desc: "Max QPS under the 500 ms p99 TTFT SLA on MoE serving.", + metric: { key: "online_max_qps", label: "queries/sec", direction: "desc", unit: "queries/sec" } }, + { name: "interactive", isExtra: true, + desc: "Single-stream TTFT on MoE inference." }, + { name: "sustained", isExtra: true, + desc: "30 min sustained MoE load. Several chips show thermal onset on this suite.", + metric: { key: "sustained_throughput", label: "sustained throughput", direction: "desc", unit: "tokens/sec" } }, + ], }, }; @@ -63,7 +240,7 @@ export const SUITE_META = { // Used by home, rankings, chip-detail, compare — keeps unit / scale rules // in one place. Returns "—" for null / missing values. export function formatPrimary(value, suiteId) { - if (value === null || value === undefined || Number.isNaN(value)) return "—"; + if (value === null || value === undefined || Number.isNaN(value)) return "-"; const meta = SUITE_META[suiteId]; if (!meta) return String(value); const p = meta.primary; @@ -231,57 +408,100 @@ export function suiteFacts(suiteId) { return { model, precision, submissions: rows.length, chips }; } -// Per-vendor breakdown for the "Coverage by vendor" home section. -// Returns an array sorted by submission count desc, each entry -// containing the vendor name, chip count, submission count, the -// vendor's best chip in its most-populated suite, and the set of -// suite letters the vendor appears in. -export function vendorBreakdown() { +// Suite leader — best chip in this suite by the suite's primary metric +// (one row per chip; the same chip can also appear at other chip counts +// elsewhere on the rankings page). Returns null if the suite has no +// numeric data yet. +export function suiteLeader(suiteId) { + const rows = bestPerChipForSuite(suiteId); + return rows.length ? rows[0] : null; +} + +// Find the best row in a suite by an arbitrary metric key + direction. +// Used by the suites page to surface a leader per scenario, not just +// for the suite-level primary metric. Returns null if no rows have a +// numeric value for that key. +export function bestRowByMetric(suiteId, key, direction) { if (!_ready) init(); - const byVendor = groupBy(_rows, (r) => r.vendor); - const out = []; - for (const [vendor, rows] of byVendor.entries()) { - if (!vendor) continue; - const chips = new Set(rows.map((r) => r._chip_slug)); - const suiteCounts = new Map(); - for (const r of rows) { - suiteCounts.set(r.suite, (suiteCounts.get(r.suite) || 0) + 1); + const rows = (_bySuite.get(suiteId) || []) + .filter((r) => r[key] !== null && r[key] !== undefined && Number.isFinite(r[key])); + if (rows.length === 0) return null; + return direction === "asc" + ? rows.reduce((a, b) => (a[key] <= b[key] ? a : b)) + : rows.reduce((a, b) => (a[key] >= b[key] ? a : b)); +} + +// Chip cloud data — one entry per chip (variants like ×1 / ×4 / ×8 +// are aggregated under the base chip name; the linked detail row +// picks the most-submitted variant so the link still resolves). +// Each entry gets a size bucket (sm/md/lg/xl) for the home cloud. +// +// Ordering is intentionally NOT by submission count: tiles are +// shuffled by a deterministic hash so the resulting layout reads +// like a real cloud rather than a sorted bar chart. +export function chipCloudData() { + if (!_ready) init(); + // Aggregate by base chip name across chip-count variants. + const byBase = new Map(); + for (const r of _rows) { + const base = r.chip; + if (!base) continue; + let agg = byBase.get(base); + if (!agg) { + agg = { + label: base, + vendor: r.vendor, + submissions: 0, + _bestSlug: null, + _bestVariantSubs: -1, + _variantSubs: new Map(), + _suites: new Set(), + }; + byBase.set(base, agg); } - // Most-populated suite the vendor appears in — pick the vendor's - // best chip there as the headline "flagship" entry. - let topSuite = null; - let topCount = -1; - for (const [sid, c] of suiteCounts.entries()) { - if (c > topCount) { topCount = c; topSuite = sid; } + agg.submissions += 1; + agg._suites.add(r.suite); + const cur = (agg._variantSubs.get(r._chip_slug) || 0) + 1; + agg._variantSubs.set(r._chip_slug, cur); + if (cur > agg._bestVariantSubs) { + agg._bestVariantSubs = cur; + agg._bestSlug = r._chip_slug; } - const bestRow = topSuite ? bestChipForVendorInSuite(vendor, topSuite) : null; + } + const out = []; + for (const agg of byBase.values()) { out.push({ - vendor, - chips: chips.size, - submissions: rows.length, - suites: Array.from(suiteCounts.keys()) - .sort((a, b) => a.localeCompare(b)) - .map((sid) => (SUITE_META[sid] && SUITE_META[sid].letter) || null) - .filter(Boolean), - topSuite, - topRow: bestRow, + slug: agg._bestSlug, + label: agg.label, + vendor: agg.vendor, + submissions: agg.submissions, + variants: agg._variantSubs.size, + suites: Array.from(agg._suites).sort().map((s) => + (SUITE_META[s] && SUITE_META[s].letter) || null).filter(Boolean), }); } - out.sort((a, b) => b.submissions - a.submissions); + // Bucket by submission count — thresholds tuned to spread current + // distribution (max ~12 subs) into 4 buckets. + for (const c of out) { + c.size = c.submissions >= 8 ? "xl" + : c.submissions >= 4 ? "lg" + : c.submissions >= 2 ? "md" + : "sm"; + } + // Deterministic shuffle: hash of label seeds a stable but + // chaotic-looking order. Stable across reloads. + for (const c of out) c._h = hashString(c.label); + out.sort((a, b) => a._h - b._h); + for (const c of out) delete c._h; return out; } -function bestChipForVendorInSuite(vendor, suiteId) { - const meta = SUITE_META[suiteId]; - if (!meta) return null; - const key = meta.primary.key; - const dir = meta.primary.direction; - const rows = (_bySuite.get(suiteId) || []) - .filter((r) => r.vendor === vendor && r[key] !== null && r[key] !== undefined); - if (rows.length === 0) return null; - return dir === "asc" - ? rows.reduce((a, b) => (a[key] <= b[key] ? a : b)) - : rows.reduce((a, b) => (a[key] >= b[key] ? a : b)); +function hashString(s) { + let h = 0; + for (let i = 0; i < s.length; i++) { + h = ((h << 5) - h + s.charCodeAt(i)) | 0; + } + return h; } function mode(arr) { diff --git a/leaderboard/site/assets/js/utils.js b/leaderboard/site/assets/js/utils.js index 58ae145d..2354ca73 100644 --- a/leaderboard/site/assets/js/utils.js +++ b/leaderboard/site/assets/js/utils.js @@ -14,7 +14,7 @@ export function esc(s) { // Format big numbers compactly: 12453 -> "12,453"; 1234567 -> "1.23M" // Integers (e.g. counts) render with no decimals automatically. export function fmtNum(v, opts = {}) { - if (v === null || v === undefined || Number.isNaN(v)) return "—"; + if (v === null || v === undefined || Number.isNaN(v)) return "-"; const n = Number(v); const { compact = false, decimals } = opts; if (compact && Math.abs(n) >= 1000) { @@ -36,17 +36,17 @@ export function fmtNum(v, opts = {}) { } export function fmtPct(v, decimals = 1) { - if (v === null || v === undefined || Number.isNaN(v)) return "—"; + if (v === null || v === undefined || Number.isNaN(v)) return "-"; return `${Number(v).toFixed(decimals)}%`; } export function fmtMs(v, decimals = 0) { - if (v === null || v === undefined || Number.isNaN(v)) return "—"; + if (v === null || v === undefined || Number.isNaN(v)) return "-"; return `${Number(v).toFixed(decimals)} ms`; } export function fmtDate(s) { - if (!s) return "—"; + if (!s) return "-"; // Accept YYYY-MM-DD or ISO timestamp; return short YYYY-MM-DD. return String(s).slice(0, 10); } diff --git a/leaderboard/site/assets/js/views/chip-detail.js b/leaderboard/site/assets/js/views/chip-detail.js index 23daa7b8..69a07c2d 100644 --- a/leaderboard/site/assets/js/views/chip-detail.js +++ b/leaderboard/site/assets/js/views/chip-detail.js @@ -42,7 +42,7 @@ export function render({ el, params }) {

    ${esc(sample._chip_label)}

    ${esc(sample.vendor)} · - ${esc(sample.memory_gb || "—")}GB memory · + ${esc(sample.memory_gb || "-")}GB memory · ${rs.length} ${rs.length === 1 ? "run" : "runs"} across ${bestPerSuite.size} suite${bestPerSuite.size === 1 ? "" : "s"}

    diff --git a/leaderboard/site/assets/js/views/compare.js b/leaderboard/site/assets/js/views/compare.js index 582248f3..7aff5b64 100644 --- a/leaderboard/site/assets/js/views/compare.js +++ b/leaderboard/site/assets/js/views/compare.js @@ -4,7 +4,7 @@ export function render({ el }) { el.innerHTML = `

    Compare chips

    -

    Side-by-side comparisons across suites — coming in commit 2.

    +

    Side-by-side comparisons across suites. Coming in commit 2.

    diff --git a/leaderboard/site/assets/js/views/home.js b/leaderboard/site/assets/js/views/home.js index fda58137..78fb299d 100644 --- a/leaderboard/site/assets/js/views/home.js +++ b/leaderboard/site/assets/js/views/home.js @@ -13,7 +13,7 @@ import { SUITE_ORDER, SUITE_META, - bestPerChipForSuite, suiteFacts, vendorBreakdown, + bestPerChipForSuite, suiteFacts, chipCloudData, summary, recent, formatPrimary, } from "../data.js"; import { @@ -21,7 +21,7 @@ import { shortVersion, shortModel, submitterHandle, } from "../utils.js"; -const TOP_N = 6; +const TOP_N = 8; export function render({ el }) { const s = summary(); @@ -43,7 +43,7 @@ export function render({ el }) {
    @@ -53,7 +53,7 @@ export function render({ el }) { 01 · Workloads

    Rankings by workload

    - Seven workloads, each on a fixed model and protocol. Pick one to dive in. + Each suite is a fixed model + protocol. Pick one to dive in.
    @@ -62,11 +62,12 @@ export function render({ el }) {
    02 · Coverage -

    Submissions by vendor

    +

    Chips on the leaderboard

    - Who shows up, how many chips, and where they compete. + Tile size = submission count. Color = vendor.
    -
    +
    +
    @@ -75,10 +76,30 @@ export function render({ el }) { 03 · Latest activity

    Recent submissions

    - See all → + See all →
    + +
    +
    + 04 · Contribute +

    Submit your result

    +

    + Benchmark your hardware with a runner script, open a pull request, + and CI re-runs the validation suite before a maintainer reviews. Once + merged, your result lands in the leaderboard within minutes. +

    + +
    +
    `; const grid = el.querySelector("#suite-grid"); @@ -86,10 +107,9 @@ export function render({ el }) { grid.appendChild(renderSuiteCard(suiteId)); } - const vendorGrid = el.querySelector("#vendor-grid"); - for (const v of vendorBreakdown()) { - vendorGrid.appendChild(renderVendorCard(v)); - } + const cloud = el.querySelector("#chip-cloud"); + const legend = el.querySelector("#cloud-legend"); + renderChipCloud(cloud, legend); const recentEl = el.querySelector("#recent-list"); for (const row of recent(8)) { @@ -109,7 +129,7 @@ function renderSuiteCard(suiteId) { const rankingsHref = buildHash("/rankings", { suite: suiteId }); - const metaLine = renderSuiteMeta(facts); + const metaLine = renderSuiteMeta(suiteId, facts); const header = `
    @@ -142,7 +162,8 @@ function renderSuiteCard(suiteId) { return card; } -function renderSuiteMeta(facts) { +function renderSuiteMeta(suiteId, facts) { + const wl = SUITE_META[suiteId] && SUITE_META[suiteId].workload; const items = []; if (facts.model) { items.push(`${esc(shortModel(facts.model))}`); @@ -150,6 +171,9 @@ function renderSuiteMeta(facts) { if (facts.precision) { items.push(`${esc(facts.precision)} baseline`); } + if (wl && wl.inputTokens && wl.outputTokens) { + items.push(`${esc(wl.inputTokens)} → ${esc(wl.outputTokens)} tok`); + } if (facts.submissions) { items.push(`${fmtNum(facts.submissions)} results`); } @@ -182,59 +206,72 @@ function renderLbRow(row, suiteId, rank) { `; } -// Sub line under chip name: vendor + framework@version + precision + submitter. +// Sub block under chip name — two predictable lines so every row in +// a suite card lines up the same way regardless of framework / version +// string length: +// line 1: vendor · framework version · precision +// line 2: @submitter (omitted if absent) function renderFwSub(row) { const fw = row.framework || ""; const ver = shortVersion(row.framework_version); const fwVer = ver ? `${esc(fw)} ${esc(ver)}` : esc(fw); const precision = row.precision ? ` · ${esc(row.precision)}` : ""; const handle = submitterHandle(row.submitted_by); - const submitter = handle - ? `·@${esc(handle)}` + const byline = handle + ? `` : ""; return ` ${esc(row.vendor)} · - ${fwVer}${precision} - ${submitter} + ${fwVer}${precision} + ${byline} `; } -function renderVendorCard(v) { - const div = document.createElement("article"); - div.className = "card vendor-card"; - div.setAttribute("data-vendor", v.vendor); - - const topMeta = v.topRow && v.topSuite ? SUITE_META[v.topSuite] : null; - const topScore = topMeta && v.topRow - ? formatPrimary(v.topRow[topMeta.primary.key], v.topSuite) - : null; - const topLine = v.topRow - ? `${esc(v.topRow._chip_label)}${topScore ? ` · ${esc(topScore)}` : ""}` - : `No submissions yet`; - - const pills = v.suites.map((l) => ` - ${esc(l)} - `).join(""); +function renderChipCloud(container, legendEl) { + const chips = chipCloudData(); + for (const c of chips) { + const a = document.createElement("a"); + a.className = `chip-tile size-${c.size}`; + a.href = `#/chip/${c.slug}`; + a.setAttribute("data-vendor", c.vendor); + const subL = c.submissions === 1 ? "submission" : "submissions"; + const suiteL = c.suites.length === 1 ? "suite" : "suites"; + const variantPart = c.variants > 1 ? ` · ${c.variants} chip-count variants` : ""; + a.setAttribute("title", + `${c.label}: ${c.submissions} ${subL} across ` + + `${c.suites.length} ${suiteL}${variantPart}`); + a.innerHTML = ` + ${esc(c.label)} + ${fmtNum(c.submissions)} + `; + container.appendChild(a); + } - div.innerHTML = ` -
    - ${esc(v.vendor)} -
    -
    -
    ${fmtNum(v.chips)}chips
    -
    ${fmtNum(v.submissions)}submissions
    -
    -
    - Top entry - ${topLine} -
    -
    ${pills}
    - `; - return div; + // Vendor legend below the cloud + if (!legendEl) return; + const byVendor = new Map(); + for (const c of chips) { + const v = byVendor.get(c.vendor) || { vendor: c.vendor, chips: 0, submissions: 0 }; + v.chips += 1; + v.submissions += c.submissions; + byVendor.set(c.vendor, v); + } + const vendors = Array.from(byVendor.values()).sort((a, b) => b.submissions - a.submissions); + legendEl.innerHTML = vendors.map((v) => { + const chipsLbl = v.chips === 1 ? "chip" : "chips"; + const subsLbl = v.submissions === 1 ? "result" : "results"; + return ` + + + ${esc(v.vendor)} + ${fmtNum(v.chips)} ${chipsLbl} · ${fmtNum(v.submissions)} ${subsLbl} + + `; + }).join(""); } function renderRecentRow(row) { @@ -252,6 +289,10 @@ function renderRecentRow(row) { a.className = "lb-row"; a.href = chipHref(row); a.setAttribute("data-suite", letter); + const bylineBits = []; + if (handle) bylineBits.push(`@${esc(handle)}`); + bylineBits.push(esc(fmtDate(row.date))); + const byline = ``; a.innerHTML = ` @@ -260,13 +301,11 @@ function renderRecentRow(row) { ${esc(row.vendor)} · - ${fwVer}${row.precision ? " · " + esc(row.precision) : ""} + ${fwVer}${row.precision ? " · " + esc(row.precision) : ""} · ${esc(suiteLabel)} - ${handle ? `·@${esc(handle)}` : ""} - · - ${esc(fmtDate(row.date))} + ${byline} ${esc(num)} @@ -278,7 +317,7 @@ function renderRecentRow(row) { // "5,731 tok/s" → { num: "5,731", unit: "tok/s" } function splitNumUnit(s) { - if (!s) return { num: "—", unit: "" }; + if (!s) return { num: "-", unit: "" }; const idx = s.search(/\s[A-Za-z%]/); if (idx === -1) return { num: s, unit: "" }; return { num: s.slice(0, idx), unit: s.slice(idx + 1) }; From 105f8a4772dda18c132cf2b495eb756f4457f43d Mon Sep 17 00:00:00 2001 From: Liang Juhao Date: Fri, 15 May 2026 19:33:37 +0800 Subject: [PATCH 08/21] =?UTF-8?q?feat(suites):=20full=20explainer=20?= =?UTF-8?q?=E2=80=94=20roofline=20argument,=20scenarios=20catalog,=20per-s?= =?UTF-8?q?uite=20specs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the placeholder suites view with a long-form explainer that makes the per-suite design argument concrete and scannable. Page structure - Hero: "Workload Suites" + a single, full-width subtitle (no eyebrow, no third tagline line). - 01 Methodology: 2-column layout. Prose on the left explains why a single score collapses information across heterogeneous regimes; an inline SVG roofline diagram on the right plots each suite at its bottleneck region (memory-bound vs compute-bound), with knee marker and per-suite dots colored by suite. - 02 Scenarios catalog (new): the seven protocols (accuracy / offline / online / interactive / sustained / speculative / burst) factored out into a single catalog. Each card has an SVG icon, role tagline, prose description, mini-spec table (metric, direction, threshold, cost), and clickable "Used by" suite-letter chips. - 03 Specifications: per-suite cards now carry a description paragraph paired with a "Concrete finding" sidebar, a spec strip, compact protocol pills, and a horizontal mini-card grid of current leaders. - 04 Datasets and 05 Propose-a-suite CTA round out the page. Interactions - Clicking a "Used by" chip in a scenario card scrolls smoothly to the matching suite section and flashes the card briefly. Uses event delegation with an attach-once guard to survive SPA re-renders. - scroll-margin-top on suite cards clears the sticky top nav so anchor jumps don't tuck the target underneath. Other - index.html loads the new suites.css alongside the existing layout/components/home stylesheets. Co-authored-by: Cursor --- leaderboard/site/assets/css/suites.css | 851 +++++++++++++++++++++ leaderboard/site/assets/js/views/suites.js | 737 ++++++++++++++++-- leaderboard/site/index.html | 1 + 3 files changed, 1545 insertions(+), 44 deletions(-) create mode 100644 leaderboard/site/assets/css/suites.css diff --git a/leaderboard/site/assets/css/suites.css b/leaderboard/site/assets/css/suites.css new file mode 100644 index 00000000..f78d75c5 --- /dev/null +++ b/leaderboard/site/assets/css/suites.css @@ -0,0 +1,851 @@ +/* suites.css — Suites explainer page. + Reuses shared tokens (--suite-A..G, --vendor-*) defined in base.css. */ + +/* Per-suite color binding (mirrors the home selectors). */ +.suite-spec[data-suite="A"], +.inversion-card[data-suite="A"], +.scn-card .scn-suite-letter[data-suite="A"], +.rfl-dot[data-suite="A"] { --suite-color: var(--suite-A); } +.suite-spec[data-suite="B"], +.inversion-card[data-suite="B"], +.scn-card .scn-suite-letter[data-suite="B"], +.rfl-dot[data-suite="B"] { --suite-color: var(--suite-B); } +.suite-spec[data-suite="C"], +.inversion-card[data-suite="C"], +.scn-card .scn-suite-letter[data-suite="C"], +.rfl-dot[data-suite="C"] { --suite-color: var(--suite-C); } +.suite-spec[data-suite="D"], +.inversion-card[data-suite="D"], +.scn-card .scn-suite-letter[data-suite="D"], +.rfl-dot[data-suite="D"] { --suite-color: var(--suite-D); } +.suite-spec[data-suite="E"], +.inversion-card[data-suite="E"], +.scn-card .scn-suite-letter[data-suite="E"], +.rfl-dot[data-suite="E"] { --suite-color: var(--suite-E); } +.suite-spec[data-suite="F"], +.inversion-card[data-suite="F"], +.scn-card .scn-suite-letter[data-suite="F"], +.rfl-dot[data-suite="F"] { --suite-color: var(--suite-F); } +.suite-spec[data-suite="G"], +.inversion-card[data-suite="G"], +.scn-card .scn-suite-letter[data-suite="G"], +.rfl-dot[data-suite="G"] { --suite-color: var(--suite-G); } +.suite-spec, +.inversion-card, +.scn-card .scn-suite-letter, +.rfl-dot { --suite-color: var(--accent-2); } + +/* ── Hero ──────────────────────────────────────────────── */ +.suites-hero { + padding: 3rem 1rem 2.25rem; + margin-bottom: 0.5rem; +} +.suites-hero h1 { + font-size: 3rem; + max-width: none; + line-height: 1.05; + margin-bottom: 1rem; +} +@media (max-width: 760px) { + .suites-hero h1 { font-size: 2.2rem; } +} +.suites-hero .hero-sub { + margin-bottom: 1.4rem; + white-space: nowrap; + max-width: none; +} +@media (max-width: 980px) { + .suites-hero .hero-sub { white-space: normal; max-width: 60ch; } +} + +/* Section header overrides for suites page — stack title with a separate + lede paragraph below it instead of pushing a subtitle to the right + edge (which leaves an unsightly gap when titles are short). */ +.section-header--stacked { + display: block; +} +/* Lede paragraph: full page width, single column, comfortable for prose. */ +.section-lede { + max-width: none; + margin: 0.1rem 0 1.5rem; + font-size: 0.96rem; + line-height: 1.65; + color: var(--fg-muted); +} +.section-lede strong { color: var(--fg-strong); font-weight: 600; } +.section-lede code { + font-family: var(--font-mono); + font-size: 0.88em; + background: var(--bg-elev-2); + padding: 0.05rem 0.35rem; + border-radius: 5px; + color: var(--fg); + border: 1px solid var(--border-soft); +} + +/* ── 01 · Methodology / Why per-suite ───────────────────── */ +.why-grid { + display: grid; + grid-template-columns: minmax(0, 1fr) clamp(260px, 30%, 360px); + gap: 1.5rem; + align-items: start; + margin-bottom: 2rem; +} +@media (max-width: 980px) { + .why-grid { grid-template-columns: 1fr; gap: 1.5rem; } +} +.why-prose { + max-width: none; +} +.why-prose p { + color: var(--fg); + font-size: 1.02rem; + line-height: 1.7; + margin: 0 0 1rem; +} +.why-prose p:last-child { margin-bottom: 0; } +.why-prose strong { color: var(--fg-strong); font-weight: 600; } +.why-prose em { color: var(--fg-strong); font-style: italic; } + +/* ── Roofline mini-diagram (sidebar of the methodology section) ── */ +.roofline-diagram { + background: var(--bg-elev); + border: 1px solid var(--border-soft); + border-radius: var(--r-md); + padding: 0.85rem 0.95rem 0.95rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + align-self: stretch; +} +.roofline-eyebrow { color: var(--fg-muted); } +.roofline-svg { + width: 100%; + height: auto; + display: block; +} +.rfl-axis { + stroke: var(--border); + stroke-width: 1; +} +.rfl-roof { + fill: none; + stroke: color-mix(in srgb, var(--accent-2) 50%, var(--fg-faint)); + stroke-width: 1.6; + stroke-dasharray: 4 3; + stroke-linecap: round; + stroke-linejoin: round; +} +.rfl-knee { + stroke: var(--border-soft); + stroke-width: 1; + stroke-dasharray: 2 3; +} +.rfl-region { + font-size: 8.5px; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; + fill: var(--fg-faint); +} +.rfl-region--bw { fill: color-mix(in srgb, var(--accent-2) 60%, var(--fg-muted)); } +.rfl-region--cp { fill: color-mix(in srgb, var(--suite-D) 60%, var(--fg-muted)); } +.rfl-knee-label { + font-size: 7.5px; + letter-spacing: 0.08em; + text-transform: uppercase; + fill: var(--fg-faint); +} +.rfl-dot circle { + fill: var(--suite-color); + stroke: var(--bg-elev); + stroke-width: 1.6; +} +.rfl-letter { + font-size: 9px; + font-weight: 700; + letter-spacing: -0.02em; + fill: var(--fg-strong); +} +.rfl-axis-label { + font-size: 9px; + font-weight: 600; + letter-spacing: 0.04em; + fill: var(--fg-muted); + text-transform: uppercase; +} +.roofline-caption { + margin: 0; + font-size: 0.8rem; + line-height: 1.5; + color: var(--fg-muted); +} + +/* Inversion cards row — the "evidence" */ +.inversion-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1.1rem; + margin-top: 0.5rem; +} +.inversion-card { + position: relative; + background: var(--bg-elev); + border: 1px solid var(--border-soft); + border-radius: var(--r-lg); + padding: 1.2rem 1.25rem 1.3rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + overflow: hidden; +} +.inversion-card::before { + content: ""; + position: absolute; + inset: 0 0 auto 0; + height: 3px; + background: var(--suite-color); +} +.inversion-card h3 { + margin: 0; + font-size: 1.08rem; + font-weight: 700; + color: var(--fg-strong); + line-height: 1.3; + letter-spacing: -0.01em; +} +.inversion-card p { + margin: 0; + font-size: 0.9rem; + line-height: 1.6; + color: var(--fg-muted); +} +@media (max-width: 980px) { + .inversion-grid { grid-template-columns: 1fr; } +} + +/* ── 02 · Scenarios catalog ─────────────────────────────── */ +.scn-catalog { + list-style: none; + margin: 0; + padding: 0; + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1.05rem; +} +@media (max-width: 1080px) { + .scn-catalog { grid-template-columns: repeat(2, minmax(0, 1fr)); } +} +@media (max-width: 640px) { + .scn-catalog { grid-template-columns: 1fr; } +} + +.scn-card { + background: var(--bg-elev); + border: 1px solid var(--border-soft); + border-radius: var(--r-md); + padding: 1.1rem 1.2rem 1.15rem; + display: flex; + flex-direction: column; + gap: 0.85rem; +} +.scn-card-head { + display: flex; + align-items: center; + gap: 0.85rem; +} +.scn-icon { + flex: 0 0 auto; + width: 40px; + height: 40px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 10px; + background: color-mix(in srgb, var(--accent-2) 9%, var(--bg-elev-2)); + color: color-mix(in srgb, var(--accent-2) 75%, var(--fg-strong)); + border: 1px solid color-mix(in srgb, var(--accent-2) 18%, var(--border-soft)); +} +.scn-icon svg { width: 24px; height: 24px; } +.scn-card-id { + display: flex; + flex-direction: column; + gap: 0.15rem; + min-width: 0; + flex: 1 1 auto; +} +/* Scenario name is the card title — promoted from a small pill to a + proper headline so each card has a clear focal point. */ +.scn-card-name { + margin: 0; + font-family: var(--font-mono, ui-monospace, monospace); + font-size: 1.18rem; + font-weight: 700; + letter-spacing: -0.015em; + color: var(--fg-strong); + line-height: 1.1; +} +.scn-card-role { + font-size: 0.66rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--fg-muted); +} +.scn-card-desc { + margin: 0; + font-size: 0.92rem; + line-height: 1.6; + color: var(--fg); + /* Let the description absorb extra height so the spec block lands at + the same vertical position across every card in a row. */ + flex: 1 0 auto; +} + +/* Mini-spec rows (Metric / Direction / Setting / ...) */ +.scn-card-spec { + margin: 0; + padding: 0.75rem 0.9rem; + background: var(--bg-elev-2); + border-radius: var(--r-sm, 8px); + border: 1px solid var(--border-soft); + display: flex; + flex-direction: column; + gap: 0.4rem; +} +.scn-spec-row { + display: grid; + grid-template-columns: minmax(90px, 32%) minmax(0, 1fr); + gap: 0.7rem; + align-items: baseline; +} +.scn-spec-row dt { + font-size: 0.66rem; + text-transform: uppercase; + letter-spacing: 0.1em; + font-weight: 700; + color: var(--fg-muted); +} +.scn-spec-row dd { + margin: 0; + font-size: 0.86rem; + line-height: 1.4; + color: var(--fg-strong); + font-weight: 500; +} + +.scn-card-applies { + display: flex; + align-items: center; + gap: 0.55rem; + margin-top: 0.05rem; + flex-wrap: wrap; +} +.scn-applies-label { + font-size: 0.68rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--fg-muted); +} +.scn-applies-letters { + display: inline-flex; + align-items: center; + gap: 0.3rem; + flex-wrap: wrap; +} +.scn-applies-sep { + color: var(--fg-faint); + font-weight: 500; + margin: 0 0.05rem; +} +.scn-applies-note { + font-size: 0.68rem; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 600; + color: var(--fg-faint); +} +.scn-suite-letter { + --letter-bg: color-mix(in srgb, var(--suite-color) 18%, transparent); + --letter-border: color-mix(in srgb, var(--suite-color) 35%, transparent); + --letter-fg: color-mix(in srgb, var(--suite-color) 75%, var(--fg-strong)); + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + border-radius: 6px; + background: var(--letter-bg); + border: 1px solid var(--letter-border); + color: var(--letter-fg); + font-size: 0.78rem; + font-weight: 700; + font-feature-settings: "tnum" on; + text-decoration: none; + letter-spacing: -0.02em; + transition: transform 80ms ease, background 120ms ease; +} +.scn-suite-letter:hover { + background: color-mix(in srgb, var(--suite-color) 30%, transparent); + transform: translateY(-1px); + text-decoration: none; +} +.scn-suite-letter--extra { + --letter-bg: transparent; + --letter-border: color-mix(in srgb, var(--suite-color) 40%, transparent); + --letter-fg: color-mix(in srgb, var(--suite-color) 65%, var(--fg-muted)); + border-style: dashed; +} + +/* ── 03 · Suite specifications ─────────────────────────── */ +.suite-spec-list { + display: flex; + flex-direction: column; + gap: 1.75rem; +} +.suite-spec { + position: relative; + background: var(--bg-elev); + border: 1px solid var(--border-soft); + border-radius: var(--r-lg); + overflow: hidden; + /* Clear the sticky top nav (~58 px) plus a little breathing room so + anchor jumps from the scenarios catalog don't tuck the suite card's + top edge underneath the nav bar. */ + scroll-margin-top: 4.75rem; +} +.suite-spec::before { + content: ""; + position: absolute; + top: 0; bottom: 0; left: 0; + width: 4px; + background: var(--suite-color); +} + +/* Brief flash when scrolling to a suite via a "Used by" letter chip. */ +@keyframes suite-spec-flash { + 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--suite-color) 55%, transparent); } + 60% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--suite-color) 0%, transparent); } + 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--suite-color) 0%, transparent); } +} +.suite-spec--flash { + animation: suite-spec-flash 1.2s ease-out 1; +} + +.suite-spec-head { + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + align-items: center; + gap: 1.25rem; + padding: 1.4rem 1.5rem 1.2rem; + border-bottom: 1px solid var(--border-soft); + background: color-mix(in srgb, var(--suite-color) 5%, transparent); +} +.suite-spec-letter { + display: inline-flex; + align-items: center; + justify-content: center; + width: 56px; + height: 56px; + border-radius: 14px; + background: var(--suite-color); + color: var(--on-accent, #fff); + font-size: 1.7rem; + font-weight: 700; + letter-spacing: -0.02em; + flex: 0 0 auto; +} +.suite-spec-title { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; } +.suite-spec-title .eyebrow { color: color-mix(in srgb, var(--suite-color) 70%, var(--fg-muted)); } +.suite-spec-title .eyebrow::before { background: var(--suite-color); } +.suite-spec-title h3 { + margin: 0; + font-size: 1.5rem; + font-weight: 700; + letter-spacing: -0.02em; + color: var(--fg-strong); + line-height: 1.15; +} +.suite-spec-tagline { + margin: 0.1rem 0 0; + color: var(--fg-muted); + font-size: 0.92rem; + line-height: 1.45; +} +.suite-spec-metric { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 0.15rem; + flex: 0 0 auto; +} +.suite-spec-metric .metric-label { + font-size: 0.95rem; + font-weight: 700; + color: color-mix(in srgb, var(--suite-color) 80%, var(--fg-strong)); +} +.suite-spec-metric .metric-direction { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--fg-muted); + font-weight: 600; +} + +@media (max-width: 720px) { + .suite-spec-head { + grid-template-columns: auto minmax(0, 1fr); + grid-template-rows: auto auto; + } + .suite-spec-metric { + grid-column: 1 / -1; + flex-direction: row; + align-items: baseline; + gap: 0.6rem; + } + .suite-spec-letter { width: 44px; height: 44px; font-size: 1.4rem; } + .suite-spec-title h3 { font-size: 1.25rem; } +} + +.suite-spec-body { + display: flex; + flex-direction: column; + gap: 1.15rem; + padding: 1.3rem 1.5rem 1.5rem; +} + +/* Per-suite intro paragraph — sits in the left column of the suite-intro-row + alongside the Current Leaders sidebar. No max-width so it fills the + available column comfortably. */ +.suite-spec-intro { + margin: 0; + font-size: 0.98rem; + line-height: 1.7; + color: var(--fg); +} + +/* ── Horizontal spec strip ─────────────────────────────── */ +.spec-strip { + list-style: none; + margin: 0; + padding: 0.95rem 1.1rem; + background: var(--bg-elev-2); + border-radius: var(--r-md); + display: grid; + grid-template-columns: repeat(6, minmax(0, 1fr)); + gap: 0.95rem 1.25rem; + border: 1px solid var(--border-soft); +} +@media (max-width: 980px) { + .spec-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); } +} +@media (max-width: 560px) { + .spec-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); } +} +.spec-strip li { + display: flex; + flex-direction: column; + gap: 0.15rem; + min-width: 0; +} +.spec-strip .strip-v { overflow-wrap: anywhere; } +.spec-strip .strip-v code { + font-family: var(--font-mono); + font-size: 0.8rem; + background: var(--bg-elev); + padding: 0.08rem 0.4rem; + border-radius: 6px; + color: var(--fg); + border: 1px solid var(--border-soft); + word-break: keep-all; + white-space: nowrap; + display: inline-block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; +} +.spec-strip .strip-k { + font-size: 0.66rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--fg-muted); +} +.spec-strip .strip-v { + font-size: 0.92rem; + font-weight: 500; + color: var(--fg-strong); + line-height: 1.3; +} + +/* ── Suite scenarios (compact pills row) ───────────────── */ +.suite-scns { + display: flex; + align-items: center; + gap: 0.7rem; + flex-wrap: wrap; +} +.suite-scns-label, +.suite-leaders-label { + font-size: 0.68rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--fg-muted); + flex: 0 0 auto; +} +.suite-scns-list { + list-style: none; + margin: 0; + padding: 0; + display: inline-flex; + flex-wrap: wrap; + gap: 0.35rem; +} + +/* Scenario pills — shared between catalog and per-suite contexts. */ +.scn-pill { + display: inline-flex; + align-items: center; + gap: 0.35rem; + font-size: 0.8rem; + font-weight: 600; + padding: 0.22rem 0.6rem; + border-radius: 999px; + white-space: nowrap; +} +.scn-default { + background: color-mix(in srgb, var(--suite-color) 16%, transparent); + color: color-mix(in srgb, var(--suite-color) 75%, var(--fg-strong)); + border: 1px solid color-mix(in srgb, var(--suite-color) 28%, transparent); +} +.scn-extra { + background: var(--bg-elev-2); + color: var(--fg-muted); + border: 1px dashed var(--border); +} +.scn-extra-tag { + font-size: 0.6rem; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--fg-faint); +} +/* ── Suite intro row: description (left) + concrete finding (right) ─── */ +.suite-intro-row { + display: grid; + grid-template-columns: minmax(0, 1fr) clamp(280px, 36%, 400px); + gap: 1.5rem; + align-items: stretch; +} +@media (max-width: 880px) { + .suite-intro-row { grid-template-columns: 1fr; gap: 1.1rem; } +} + +/* ── Current leaders — horizontal mini-card grid ───────── */ +.suite-leaders { + display: flex; + flex-direction: column; + gap: 0.55rem; +} +.suite-leaders-label { + font-size: 0.68rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--fg-muted); +} +.leader-list { + list-style: none; + margin: 0; + padding: 0; + /* Flex with capped card width so a lone leader keeps a sensible size + instead of stretching to full row width, and a full row of four + leaders still divides the space evenly. */ + display: flex; + flex-wrap: wrap; + gap: 0.8rem; +} +.leader-row { + flex: 1 1 220px; + min-width: 220px; + max-width: 300px; + display: flex; + flex-direction: column; + gap: 0.25rem; + padding: 0.7rem 0.85rem 0.75rem; + background: var(--bg-elev-2); + border: 1px solid var(--border-soft); + border-radius: var(--r-md); + border-top: 3px solid color-mix(in srgb, var(--suite-color) 38%, var(--border-soft)); + transition: border-color 120ms ease, background 120ms ease; +} +.leader-row:hover { + border-top-color: var(--suite-color); + background: color-mix(in srgb, var(--suite-color) 4%, var(--bg-elev-2)); +} +.leader-row .scn-pill { + align-self: flex-start; + font-size: 0.7rem; + padding: 0.12rem 0.55rem; +} +.leader-chip { + display: inline-flex; + align-items: center; + gap: 0.45rem; + text-decoration: none; + min-width: 0; + margin-top: 0.05rem; +} +.leader-chip:hover { text-decoration: none; } +.leader-chip:hover .leader-chip-name { color: var(--accent-2); } +.leader-chip-name { + font-weight: 600; + color: var(--fg-strong); + font-size: 0.9rem; + line-height: 1.25; + transition: color 120ms ease; + /* Long chip names wrap to a second line inside their mini-card. */ + white-space: normal; + overflow-wrap: anywhere; + word-break: break-word; +} +.leader-vendor { + --vendor-color: var(--fg-faint); + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--vendor-color); + flex: 0 0 auto; +} +.leader-vendor[data-vendor="NVIDIA"] { --vendor-color: var(--v-nvidia); } +.leader-vendor[data-vendor="AMD"] { --vendor-color: var(--v-amd); } +.leader-vendor[data-vendor="Apple"] { --vendor-color: var(--v-apple); } +.leader-vendor[data-vendor="Google"] { --vendor-color: var(--v-google); } +.leader-vendor[data-vendor="Huawei"] { --vendor-color: var(--v-huawei); } +.leader-vendor[data-vendor="Moore Threads"] { --vendor-color: var(--v-moore); } +.leader-vendor[data-vendor="Intel"] { --vendor-color: var(--v-intel); } +.leader-val { + color: color-mix(in srgb, var(--suite-color) 80%, var(--fg-strong)); + font-weight: 700; + font-size: 1rem; + letter-spacing: -0.005em; + margin-top: 0.15rem; +} +.leader-empty { + color: var(--fg-faint); + font-style: italic; + font-size: 0.82rem; + margin-top: 0.15rem; +} + +/* ── Concrete finding card ───────────────────────────────── */ +.suite-finding { + padding: 1rem 1.1rem 1.05rem; + background: color-mix(in srgb, var(--suite-color) 6%, var(--bg-elev-2)); + border-radius: var(--r-md); + border: 1px solid color-mix(in srgb, var(--suite-color) 18%, transparent); +} +/* Side variant — sits alongside the description paragraph in the + suite-intro-row. Stretches to match the description's height for + a clean two-column block. */ +.suite-finding--side { + align-self: stretch; + display: flex; + flex-direction: column; + gap: 0.4rem; +} +.suite-finding--side .finding-body { + font-size: 0.88rem; +} +.finding-eyebrow { + display: inline-block; + font-size: 0.66rem; + text-transform: uppercase; + letter-spacing: 0.12em; + font-weight: 600; + color: color-mix(in srgb, var(--suite-color) 70%, var(--fg-muted)); + margin-bottom: 0.4rem; +} +.finding-headline { + margin: 0 0 0.4rem; + font-size: 1.02rem; + font-weight: 700; + color: var(--fg-strong); + line-height: 1.3; + letter-spacing: -0.005em; +} +.finding-body { + margin: 0; + font-size: 0.9rem; + line-height: 1.6; + color: var(--fg-muted); +} + +/* ── CTAs ─────────────────────────────────────────────────── */ +.suite-spec-cta { + margin-top: 0.25rem; + padding-top: 1.1rem; + border-top: 1px solid var(--border-soft); + display: flex; + gap: 0.5rem; + flex-wrap: wrap; +} + +/* ── 04 · Datasets table ───────────────────────────────── */ +.dataset-table { + display: grid; + grid-template-columns: 1fr; + background: var(--bg-elev); + border: 1px solid var(--border-soft); + border-radius: var(--r-lg); + overflow: hidden; +} +.dataset-row { + display: grid; + grid-template-columns: + minmax(180px, 1.4fr) + minmax(120px, 1fr) + minmax(70px, 0.6fr) + minmax(110px, 0.9fr) + minmax(110px, 0.9fr) + minmax(220px, 2fr); + gap: 1rem; + align-items: baseline; + padding: 0.85rem 1.1rem; + border-bottom: 1px solid var(--border-soft); + font-size: 0.92rem; +} +.dataset-row:last-child { border-bottom: none; } +.dataset-row--head { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 600; + color: var(--fg-muted); + background: var(--bg-elev-2); +} +.dataset-row .dt-name code { + font-family: var(--font-mono); + font-size: 0.86rem; + background: var(--bg-elev-2); + padding: 0.1rem 0.45rem; + border-radius: 6px; + color: var(--fg-strong); +} +.dataset-row .dt-notes { + color: var(--fg-muted); + font-size: 0.86rem; + line-height: 1.45; +} + +@media (max-width: 880px) { + .dataset-row { + grid-template-columns: 1fr 1fr; + row-gap: 0.35rem; + } + .dataset-row--head { display: none; } + .dataset-row .dt-name { grid-column: 1 / -1; font-weight: 600; } + .dataset-row .dt-notes { grid-column: 1 / -1; } +} diff --git a/leaderboard/site/assets/js/views/suites.js b/leaderboard/site/assets/js/views/suites.js index 5d875b2e..bd2182ad 100644 --- a/leaderboard/site/assets/js/views/suites.js +++ b/leaderboard/site/assets/js/views/suites.js @@ -1,61 +1,710 @@ -// views/suites.js — Suites explorer. In commit 1 this is already informative -// (lightweight static reference page); commit 4 will add per-suite stats. +// views/suites.js — Suites explainer. This is the "what is this +// benchmark?" page: roofline argument + diagram, scenarios catalog +// with per-scenario specs, per-suite specifications, datasets reference. -import { SUITE_ORDER, SUITE_META, suiteFacts } from "../data.js"; -import { esc, buildHash, shortModel, fmtNum } from "../utils.js"; +import { + SUITE_ORDER, SUITE_META, suiteFacts, bestRowByMetric, +} from "../data.js"; +import { esc, fmtNum, buildHash, shortModel } from "../utils.js"; + +const GH_BASE = "https://github.com/JuhaoLiang1997/AccelMark"; + +// One concrete finding per suite, distilled from the paper. Kept short +// enough to fit inside a single card but specific enough to be useful. +const SUITE_FINDINGS = { + suite_A: { + headline: "Offline winner is not the SLA winner", + body: + "H200 leads Suite A offline at 5,731 tokens/sec, but on the same suite's " + + "online tier A100 and A800 sustain 25 queries/sec while H200 caps " + + "at 10. The 500 ms p99 TTFT SLA binds well before the throughput " + + "ceiling, so the chip that leads offline loses the production tier.", + }, + suite_B: { + headline: "Bottom tier is decided by software stack, not VRAM", + body: + "With 8 chips serving 70B BF16, aggregate VRAM is no longer binding. " + + "Ascend 910C ×16 supplies 1,024 GB and still delivers only 723 tokens/sec, " + + "below Ascend 910B2 ×8 at 770 tokens/sec. Doubling the hardware buys " + + "nothing once vllm-ascend's 70B path is the binding constraint.", + }, + suite_C: { + headline: "Speed without quality is meaningless", + body: + "Quality efficiency multiplies throughput by accuracy. On A100, W8A8 " + + "wins at 3,776 (1.20× speedup, +2 pts accuracy) because INT8 tensor " + + "cores engage. FP8 shows zero speedup on A100 because the hardware " + + "path is absent and compute falls back to BF16. On H100 the same FP8 " + + "column flips to roughly 1.5 to 1.8× speedup.", + }, + suite_D: { + headline: "Long context inverts the bandwidth-bound ranking", + body: + "Suite D pushes arithmetic intensity past the roofline knee with " + + "~28K-token prefill, making the workload compute-bound rather than " + + "memory-bandwidth-bound. Rankings invert relative to Suite A: chips " + + "that win on short-prompt decode lose to chips with higher raw FLOPS.", + }, + suite_E: { + headline: "Newest flagship has the worst 2× efficiency", + body: + "RTX 4090 D tops the Suite E 2× efficiency leaderboard because its " + + "lower per-die throughput keeps the communication share small. H200, " + + "the newest flagship, shows the worst NVIDIA 2× efficiency because " + + "per-chip throughput grew faster than NVLink 4.0 bandwidth did.", + }, + suite_F: { + headline: "Edge isolates the pure HBM bandwidth ceiling", + body: + "Suite F uses Qwen2.5-0.5B with ~95-token prompts. Stripping residual " + + "compute-path interference exposes raw memory-bandwidth headroom; " + + "this is where commodity hardware (RTX 4090, A6000) is most " + + "competitive on a per-dollar basis.", + }, + suite_G: { + headline: "Sparse routing rewards bandwidth over FLOPS", + body: + "Mixtral activates only 2 of 8 experts per token, keeping arithmetic " + + "intensity below dense 8B inference even at 8-chip scale. H20-3e " + + "trails A100-40G by ~5% on dense Suite A but beats A100-40G ×8 by " + + "17% on Suite G; its 4,000 GB/s aggregate bandwidth pays off more " + + "than its compute.", + }, +}; + +// Three cross-suite ranking inversions distilled from the paper. +const INVERSION_CARDS = [ + { + eyebrow: "Offline vs Online", + title: "The throughput winner loses the SLA tier", + body: + "Suite A: H200 leads offline at 5,731 tokens/sec, but caps at 10 queries/sec " + + "once the 500 ms p99 TTFT SLA is enforced. A100 and A800 sustain " + + "25 queries/sec on the same hardware tier. Offline and online are not the " + + "same race.", + suite: "A", + }, + { + eyebrow: "Dense vs MoE", + title: "H20-3e: minus 5% on dense, plus 17% on MoE", + body: + "On dense Suite A, H20-3e trails A100-40G by ~5%. On Suite G's " + + "sparse Mixtral routing the same chip leads A100-40G ×8 by 17%. " + + "Sparse activation holds arithmetic intensity below dense 8B, so " + + "bandwidth (not FLOPS) sets the ceiling.", + suite: "G", + }, + { + eyebrow: "Multi-chip Amdahl", + title: "Newest flagship has the worst 2× scaling", + body: + "RTX 4090 D tops the Suite E 2× efficiency table precisely because " + + "its per-die throughput is low; communication occupies a small share " + + "of wall time. H200's per-chip throughput outgrew NVLink 4.0 " + + "bandwidth, so its 2× scaling sits at the bottom of NVIDIA's range.", + suite: "E", + }, +]; + +// Shared scenarios catalog. Each card has: +// icon — small SVG illustrating the load shape +// role — uppercase tagline (what the scenario reveals) +// description — 2 sentences in plain prose +// spec — key/value rows (metric / direction / setting) +// appliesTo — which suite letters use it (default vs. extra) +const SCENARIO_CATALOG = [ + { + name: "accuracy", + icon: "gate", + role: "Quality gate", + description: + "MMLU subset score against the suite's baseline model. Runs before any throughput scenario; a chip that drops accuracy beyond the threshold has every other number on this suite invalidated.", + spec: [ + { k: "Metric", v: "MMLU score (0-100)" }, + { k: "Direction", v: "Higher is better" }, + { k: "Threshold", v: "Suite-specific baseline" }, + { k: "Cost", v: "~5 min / chip" }, + ], + appliesTo: { default: ["A", "B", "C", "D", "F", "G"], extra: [] }, + }, + { + name: "offline", + icon: "batch", + role: "Peak throughput", + description: + "All requests submitted at once, no SLA, no concurrency cap. The pure capability number that establishes the chip's ceiling on this workload.", + spec: [ + { k: "Metric", v: "Aggregate tokens/sec" }, + { k: "Direction", v: "Higher is better" }, + { k: "Concurrency", v: "Unbounded (vendor-tuned)" }, + { k: "Cost", v: "~10 to 15 min / chip" }, + ], + appliesTo: { default: ["A", "B", "C", "D", "E", "F", "G"], extra: [] }, + }, + { + name: "online", + icon: "poisson", + role: "SLA-bound capacity", + description: + "Sweeps offered load under Poisson arrivals and reports the highest queries/sec that still meets the 500 ms p99 TTFT SLA. The number production traffic actually has to honour.", + spec: [ + { k: "Metric", v: "Max queries/sec" }, + { k: "Direction", v: "Higher is better" }, + { k: "SLA", v: "p99 TTFT ≤ 500 ms" }, + { k: "Arrivals", v: "Poisson, vendor sweep" }, + ], + appliesTo: { default: ["A", "B", "F", "G"], extra: ["C", "D"] }, + }, + { + name: "interactive", + icon: "single", + role: "Single-stream latency", + description: + "One request in-flight at a time, no concurrency. The chat-window UX baseline; minimal queueing, dominated by decode latency and software overhead.", + spec: [ + { k: "Metric", v: "TTFT p99 (milliseconds)" }, + { k: "Direction", v: "Lower is better" }, + { k: "Concurrency", v: "1 stream" }, + { k: "Streams", v: "Many short conversations" }, + ], + appliesTo: { default: ["F"], extra: ["A", "B", "D", "G"] }, + }, + { + name: "sustained", + icon: "longblock", + role: "Stability under load", + description: + "Fixed-concurrency load held for 15 to 30 minutes. Reports the throttle ratio between the first and last 60 s windows so thermal throttling and memory fragmentation surface.", + spec: [ + { k: "Metric", v: "Throttle ratio (peak → end)" }, + { k: "Direction", v: "Smaller drop is better" }, + { k: "Duration", v: "15 to 30 minutes" }, + { k: "Load", v: "Fixed concurrency" }, + ], + appliesTo: { default: [], extra: ["A", "B", "C", "D", "F", "G"] }, + }, + { + name: "speculative", + icon: "spec", + role: "Draft-assisted decode", + description: + "Offline workload with a 1B draft model loaded alongside the target. Reports speculative decoding acceptance rate and end-to-end speedup; tells you whether spec-decode is worth the VRAM cost.", + spec: [ + { k: "Metric", v: "tokens/sec + acceptance rate" }, + { k: "Direction", v: "Higher is better" }, + { k: "Draft model", v: "1B (fits beside target)" }, + { k: "Mode", v: "Offline (no SLA)" }, + ], + appliesTo: { default: [], extra: ["A", "D"] }, + }, + { + name: "burst", + icon: "burst", + role: "KV pressure", + description: + "Alternates 5× steady arrival rate (short windows) with steady traffic and reports TTFT p99 during the burst. Stresses the KV cache, admission control, and warm-up paths.", + spec: [ + { k: "Metric", v: "TTFT p99 during burst" }, + { k: "Direction", v: "Lower is better" }, + { k: "Burst", v: "5× steady traffic" }, + { k: "Window", v: "Short pulses + recovery" }, + ], + appliesTo: { default: [], extra: ["A", "B"] }, + }, +]; + +const DATASETS = [ + { + name: "sharegpt_standard_v1", + used: "A · B · C · E · G", + prompts: "500", + inputP50: "~280 tok", + outputP50: "~310 tok", + notes: "Curated to match production LLM-API traffic; token-length p99 ≈ 2,100.", + }, + { + name: "sharegpt_longctx_v1", + used: "D", + prompts: "200", + inputP50: "~28,650 tok", + outputP50: "≤256 tok", + notes: "Multi-turn dialogues concatenated to push prefill past the roofline knee.", + }, + { + name: "sharegpt_edge_v1", + used: "F", + prompts: "500", + inputP50: "~95 tok", + outputP50: "~150 tok", + notes: "Short single-turn prompts; keeps the edge suite bandwidth-isolated.", + }, +]; + +// Tiny SVG icons keyed by scenario.icon — drawn with currentColor so they +// inherit the surrounding text colour and stay legible in both themes. +const SCN_ICONS = { + gate: ` + `, + batch: ` + `, + poisson: ` + `, + single: ` + `, + longblock: ` + `, + spec: ` + `, + burst: ` + `, +}; + +// Roofline mini-diagram. Coordinates are hand-tuned so the seven dots +// land at roughly the right region of the spectrum for each suite. +const ROOFLINE_POINTS = [ + { letter: "F", x: 68, y: 168, label: "right" }, // far left, bandwidth-bound, smallest model + { letter: "A", x: 102, y: 132, label: "right" }, // bandwidth-bound, 8B decode + { letter: "G", x: 118, y: 122, label: "right" }, // MoE, similar region as A + { letter: "B", x: 138, y: 96, label: "left" }, // 70B multi-chip + { letter: "C", x: 168, y: 68, label: "left" }, // quantization, transition + { letter: "E", x: 192, y: 56, label: "right" }, // scaling, near the knee + { letter: "D", x: 268, y: 56, label: "left" }, // compute-bound long context +]; export function render({ el }) { - const cards = SUITE_ORDER.map((id) => { - const meta = SUITE_META[id]; - const facts = suiteFacts(id); - return ` - -
    -
    -
    - ${esc(meta.letter)} - ${esc(meta.title)} -
    - ${esc(meta.primary.label)} -
    -

    ${esc(meta.tagline)}

    -
    - ${facts.model ? `${esc(shortModel(facts.model))}` : ""} - ${facts.precision ? `${esc(facts.precision)} baseline` : ""} - ${fmtNum(facts.submissions)} results - ${fmtNum(facts.chips)} chips -
    + el.innerHTML = ` +
    +

    Workload Suites

    +

    + Each suite anchors a distinct bottleneck region, together sampling + the full inference workload spectrum. +

    +
    +
    + +
    +
    +
    + 01 · Methodology +

    Why per-suite, not a single score?

    - - `; - }).join(""); +
    - el.innerHTML = ` -
    -

    Benchmark Suites

    -

    Seven workloads, each on a fixed model and protocol

    -

    - Each suite targets a distinct deployment regime. Click into one to view - its full ranking. +

    +
    +

    + AI inference workloads span a wide range of arithmetic intensity. + The roofline model makes the consequence concrete: a chip's + effective performance is set by whichever of memory bandwidth or + compute is binding for the workload. Because different workloads + occupy different regions of that spectrum, hardware rankings + are not preserved across them. +

    +

    + A chip optimized for one region, say bandwidth-bound 8B decode, + diverges from a chip optimized for another, say compute-bound + long-context prefill, as soon as the workload moves. Collapsing + heterogeneous workloads into a single composite score hides + exactly the trade-offs a buyer needs to see. +

    +

    + AccelMark operationalizes spectrum sampling: a + set of suites, each anchored to a qualitatively distinct + bottleneck region. Bandwidth-bound serving at 8B (A) and 0.5B + (F); capacity-then-stack-bound 70B multi-chip (B); the + bandwidth-to-compute transition via quantization (C); + compute-bound long-context prefill (D); multi-chip communication + overhead (E); and sparse MoE routing (G). +

    +
    + ${renderRoofline()} +
    + +
    + ${INVERSION_CARDS.map(renderInversion).join("")} +
    +
    + +
    +
    +
    + 02 · Scenarios +

    Seven protocols, one suite at a time

    +
    +
    +

    + Each suite picks a subset of these seven protocols. The metric, + direction, and setting are pinned here once; per-suite cards below + just say which apply. + Default scenarios are required for a valid + submission; extras are opt-in for vendors who want + to characterize a regime further.

    +
      + ${SCENARIO_CATALOG.map(renderScenarioCard).join("")} +
    +
    -
    ${cards}
    +
    +
    + 03 · Specifications +

    Each suite, in detail

    +
    +
    +

    + One self-contained card per suite. The header pins the primary + metric and direction; the workload strip pins the model, hardware + budget, precision, and dataset; the protocols row shows which + scenarios apply; current leaders surface the top chip per metric. +

    +
    + ${SUITE_ORDER.map(renderSuiteSpec).join("")} +
    +
    -
    +
    - Methodology -

    Why per-suite, not a single score?

    + 04 · Datasets +

    Three immutable prompt sets

    -

    - AI workloads vary widely. A chip optimized for batched offline throughput - may rank poorly on interactive latency. Combining every dimension into a - single number hides those trade-offs. AccelMark keeps every suite raw and - lets you pick the one that matches your deployment. +

    + Datasets are content-hash-pinned: once a name is published, the + bytes never change. Revising a dataset means a new version + (_v2, etc.). Every result is tied to a dataset hash so + comparisons stay apples-to-apples across years.

    +
    +
    + Dataset + Used by + Prompts + Input p50 + Output p50 + Why +
    + ${DATASETS.map(renderDataset).join("")} +
    + +
    +
    + 05 · Extend +

    Propose a new suite

    +

    + Have a workload regime AccelMark doesn't cover yet: long-context + serving, speculative decoding economics, a domain-specific + fine-tune? Open a discussion with a one-page sketch of the + bottleneck region and reference SLAs. The contribution flow is + the same as a new result. +

    + +
    +
    + `; + + // Wire up smooth scroll for the scenario catalog's "Used by" suite-letter + // chips. These use #suite-X anchors, but the SPA hash router would + // otherwise interpret the click as a route change. Intercept here and + // scroll the matching
    into view. + // + // Use a once-attach guard: the router rebuilds the view's innerHTML on + // each visit, but the listener is on `el` itself and would accumulate + // across re-renders without this flag. + if (!el.__suitesScrollAttached) { + el.addEventListener("click", (ev) => { + const a = ev.target.closest(".scn-suite-letter"); + if (!a) return; + const href = a.getAttribute("href") || ""; + if (!href.startsWith("#suite-")) return; + ev.preventDefault(); + const id = href.slice(1); + const target = el.querySelector(`#${CSS.escape(id)}`); + if (!target) return; + target.scrollIntoView({ behavior: "smooth", block: "start" }); + // Brief highlight so the user sees which card they landed on. + target.classList.remove("suite-spec--flash"); + // Force reflow so the animation restarts when re-clicked. + void target.offsetWidth; + target.classList.add("suite-spec--flash"); + }); + el.__suitesScrollAttached = true; + } +} + +function renderRoofline() { + const dots = ROOFLINE_POINTS.map((p) => { + const labelDx = p.label === "left" ? -10 : 10; + const textAnchor = p.label === "left" ? "end" : "start"; + return ` + + + ${esc(p.letter)} + + `; + }).join(""); + return ` + + `; +} + +function renderInversion(card) { + return ` +
    + ${esc(card.eyebrow)} +

    ${esc(card.title)}

    +

    ${esc(card.body)}

    +
    + `; +} + +function renderScenarioCard(scn) { + const defaults = scn.appliesTo.default || []; + const extras = scn.appliesTo.extra || []; + const icon = SCN_ICONS[scn.icon] || ""; + return ` +
  • +
    + +
    +

    ${esc(scn.name)}

    + ${esc(scn.role)} +
    +
    +

    ${esc(scn.description)}

    +
    + ${scn.spec.map((row) => ` +
    +
    ${esc(row.k)}
    +
    ${esc(row.v)}
    +
    + `).join("")} +
    +
    + Used by + + ${defaults.map((l) => `${esc(l)}`).join("")} + ${extras.length && defaults.length ? `·` : ""} + ${extras.map((l) => `${esc(l)}`).join("")} + + ${extras.length ? `${defaults.length ? "extras" : "all extra"}` : ""} +
    +
  • + `; +} + +function renderSuiteSpec(suiteId) { + const meta = SUITE_META[suiteId]; + if (!meta) return ""; + const facts = suiteFacts(suiteId); + const finding = SUITE_FINDINGS[suiteId]; + const wl = meta.workload || {}; + const scenarios = meta.scenarios || []; + + const directionLabel = meta.primary.direction === "asc" + ? "Lower is better" + : "Higher is better"; + + const leaderScenarios = scenarios.filter((s) => s.metric); + + return ` +
    +
    + ${esc(meta.letter)} +
    + Suite ${esc(meta.letter)} +

    ${esc(meta.title)}

    +

    ${esc(meta.tagline)}

    +
    + + ${esc(meta.primary.label)} + ${esc(directionLabel)} + +
    + +
    +
    + ${meta.description ? `

    ${esc(meta.description)}

    ` : `
    `} + ${finding ? ` + + ` : ""} +
    + +
      +
    • Model${esc(shortModel(wl.model) || "-")}
    • +
    • Chips${esc(wl.chips || "-")}
    • +
    • Precision${esc(wl.precision || "-")}
    • +
    • Dataset${esc(wl.dataset || "-")}
    • +
    • Tokens (in / out)${esc(wl.inputTokens || "-")} / ${esc(wl.outputTokens || "-")}
    • +
    • Coverage${fmtNum(facts.submissions)} results · ${fmtNum(facts.chips)} chips
    • +
    + +
    + Protocols +
      + ${scenarios.map((s) => ` +
    • + ${esc(s.name)}${s.isExtra ? `extra` : ""} +
    • + `).join("")} +
    +
    + + ${leaderScenarios.length ? ` +
    + Current leaders +
      + ${leaderScenarios.map((s) => renderLeaderRow(suiteId, s)).join("")} +
    +
    + ` : ""} + + +
    +
    + `; +} + +function renderLeaderRow(suiteId, scn) { + const row = bestRowByMetric(suiteId, scn.metric.key, scn.metric.direction); + const pillClass = scn.isExtra ? "scn-extra" : "scn-default"; + if (!row) { + return ` +
  • + ${esc(scn.name)} + No qualifying submissions yet +
  • + `; + } + const val = formatScnMetric(row[scn.metric.key], scn.metric); + return ` +
  • + ${esc(scn.name)} + + + ${esc(row._chip_label)} + + ${esc(val)} +
  • + `; +} + +function formatScnMetric(value, m) { + if (value === null || value === undefined || !Number.isFinite(value)) return "-"; + const scale = m.scale || 1; + const decimals = m.decimals != null + ? m.decimals + : Math.abs(value * scale) >= 100 ? 0 : 1; + const num = (value * scale).toLocaleString("en-US", { + minimumFractionDigits: decimals, + maximumFractionDigits: decimals, + }); + return m.unit ? `${num} ${m.unit}` : num; +} + +function renderDataset(d) { + return ` +
    + ${esc(d.name)} + ${esc(d.used)} + ${esc(d.prompts)} + ${esc(d.inputP50)} + ${esc(d.outputP50)} + ${esc(d.notes)} +
    `; } diff --git a/leaderboard/site/index.html b/leaderboard/site/index.html index 59c25580..c719f82e 100644 --- a/leaderboard/site/index.html +++ b/leaderboard/site/index.html @@ -13,6 +13,7 @@ +