Skip to content

feat(compare): replace index card walls with GPU pair selection matrices#600

Draft
Oseltamivir wants to merge 6 commits into
masterfrom
feat/compare-index-matrix
Draft

feat(compare): replace index card walls with GPU pair selection matrices#600
Oseltamivir wants to merge 6 commits into
masterfrom
feat/compare-index-matrix

Conversation

@Oseltamivir

@Oseltamivir Oseltamivir commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The /compare and /compare-per-dollar index pages rendered one card per comparable GPU pair — ~258 cards across 9+ model sections, split into three vendor groups each. Finding a specific pair meant linearly scanning a card wall, and pairs without data were invisible.

Each model section now renders a single upper-triangle GPU×GPU selection matrix:

  • Full 9-GPU axis on every matrix (identical across models) so benchmark coverage is comparable between sections. Axis order is derived from HW_REGISTRY, not hardcoded: contiguous vendor blocks (NVIDIA, then AMD), oldest→newest within a vendor (H100 → H200 → B200 → B300 → GB200 → GB300 / MI300X → MI325X → MI355X).
  • Three cell states, distinct in both light and dark themes: brand-tinted ringed squares for cross-vendor pairs, neutral fills for same-vendor pairs, dashed-outline ghosts for pairs with no benchmark data (with a legend below each matrix).
  • Vendor group bars span the column headers (green NVIDIA / red AMD) and row labels carry vendor dots plus an architecture subline — replacing the old three per-vendor card groups, since the vendor regions are now contiguous areas of the triangle.
  • Interaction: hover/keyboard-focus highlights the cell's row + column headers and prints the pair name in a readout line; cells scale slightly on hover and get a brand focus ring; native title tooltips.
  • Mobile: the matrix scrolls horizontally with a sticky GPU-label column.

SEO / behavior preserved

  • Every available pair remains a real server-rendered <a> (no client-side model switching), with the pair name as sr-only anchor text and a full accessible label ("DeepSeek R1: H100 vs H200 benchmark comparison").
  • The compare_index_pair_clicked analytics event fires with the same {slug, label} payload as the old cards.
  • Page h1s, descriptions, CTA buttons, per-model id anchors, and JSON-LD are untouched.
  • ComparePairCardLink stays — the /compare-precision and /compare-spec-decode indexes still use it.

Notes for review

  • /zh index pages are intentionally unchanged in this PR — they keep the existing card UI (which is why bucketComparePairsByVendor remains in compare-ssr.ts). The matrix can be ported to the zh siblings in a follow-up.
  • Unofficial-run overlay support: N/A — these are index navigation pages with no chart data path.

Testing

  • New unit suite compare-matrix.test.ts (10 tests): axis ordering invariants, upper-triangle completeness, availability marking, canonical slug construction, cross-vendor flags.
  • New e2e spec compare-index-matrix.cy.ts: matrix renders per model section, cell hrefs are canonical for both routes, click navigates to the pair page, ghost cells render for missing pairs, focus updates the readout.
  • pnpm typecheck, lint, fmt, production build, and the compare-precision / compare-redirect e2e specs all pass against the fixtures server.
  • Visually verified via screenshots: light + dark themes, desktop + 375px mobile (including sticky-column horizontal scroll), and cell focus state.

Note

Medium Risk
Large navigation/UI change on SEO-heavy compare index routes; mitigated by preserving server-rendered link URLs, analytics payloads, and new unit/e2e coverage.

Overview
Replaces the vendor-grouped card grids on /compare, /compare-per-dollar, and their /zh index pages with one GPU×GPU lower-triangle matrix per model section, built server-side via buildCompareMatrix and rendered by the new client ComparePairMatrix component.

Navigation UX: Every registry GPU appears on a shared axis (NVIDIA then AMD, oldest→newest within vendor). Cells with benchmark data stay real <a> links (sr-only pair text, same compare_index_pair_clicked analytics); missing pairs show as ghost cells so coverage gaps are visible. Cross-vendor cells get brand styling; hover/focus drives a readout and row/column highlights.

Cleanup: Per-page groupPairsByVendorForModel and bucketComparePairsByVendor in compare-ssr.ts are removed. ComparePairCardLink is unchanged for precision/spec-decode indexes.

Tests: New compare-matrix.test.ts (ordering, slugs, availability) and Cypress compare-index-matrix.cy.ts (matrices, hrefs, ghosts, focus readout, en/zh routes).

Reviewed by Cursor Bugbot for commit 2c49d6b. Bugbot is set up for automated code reviews on this repo. Configure here.

The /compare and /compare-per-dollar indexes rendered ~258 pair cards
across 9+ model sections, which no longer scales as a navigation
surface. Each model section now renders one upper-triangle GPU×GPU
matrix instead: pairs with benchmark data are server-rendered links
(brand tint for cross-vendor, neutral fill for same-vendor), pairs
without data render as dashed ghost cells so coverage gaps are visible
at a glance.

- Axis order derives from HW_REGISTRY: contiguous vendor blocks
  (NVIDIA then AMD), oldest-to-newest within a vendor, so the
  cross-vendor region forms a rectangle replacing the old three
  vendor card groups
- Hover/focus highlights the row and column headers and shows the
  pair label in a readout line; cells carry full accessible names,
  native tooltips, and sr-only pair-name anchor text for crawlers
- compare_index_pair_clicked fires with the same {slug, label}
  payload as the old cards
- Mobile: horizontal scroll with a sticky GPU-label column
- /zh index pages are intentionally unchanged in this PR
@Oseltamivir
Oseltamivir requested a review from adibarra as a code owner July 20, 2026 11:51
@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
inferencemax-app Ready Ready Preview, Comment Jul 20, 2026 1:21pm

Request Review

Comment thread packages/app/src/app/compare/page.tsx
Comment thread packages/app/src/components/compare/compare-pair-matrix.tsx Outdated
Bring the /zh/compare and /zh/compare-per-dollar index pages to parity
with their English siblings: they now render the ComparePairMatrix
instead of the old vendor-group card walls. The matrix component gains
an { en, zh } STRINGS dict resolved via useLocale(); English strings are
byte-identical. Drops the now-unused per-page VendorGroup helpers and the
bucketComparePairsByVendor helper (plus its interfaces and orphaned
imports) from compare-ssr.ts.

Also fixes a hover/focus bug in ComparePairMatrix: hover and keyboard
focus shared one state, so a pointer entering and leaving a focused cell
cleared the focus highlight. Split into hoveredCell/focusedCell with
active = hoveredCell ?? focusedCell so hover takes precedence while
present and a focused cell's highlight persists after the pointer leaves.

Adds zh coverage to the compare-index-matrix e2e spec.
Comment thread packages/app/src/lib/compare-matrix.ts
buildCompareMatrix built cell.label via compareDisplayLabel in display
(row vs column) order, so the H100×B200 cell read "H100 vs B200" while
its destination page /compare/<model>-b200-vs-h100 titles the pair "B200
vs H100" — and the pre-matrix index cards sent that same alphabetical
label in the compare_index_pair_clicked analytics payload. Tooltip,
sr-only anchor text, readout, and analytics disagreed with the
destination for every pair whose display order differed from A-Z.

Label from the already-sorted [first, second] instead so it matches the
canonical slug, the destination title, and the analytics payload. Update
the CompareMatrixCell.label doc and the unit test accordingly.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5b24bd5. Configure here.

Comment thread packages/app/src/lib/compare-matrix.ts
compareMatrixGpuOrder ranked vendor blocks by each vendor's lowest
registry sort value. That yields NVIDIA-first today, but the registry
interleaves vendors by generation, so a future AMD flagship whose sort
dips below GB300's would silently flip the whole axis to AMD-first and
move the cross-vendor region — contradicting the docblock's stated
NVIDIA-first design.

Pin the order with a VENDOR_BLOCK_ORDER constant (NVIDIA, then AMD):
listed vendors sort by their pinned index, a listed vendor always
precedes an unlisted one, and two unlisted vendors fall back to the
existing best-registry-sort comparison so a future third vendor still
orders deterministically. Within-vendor ordering is unchanged. Docblock
and the contiguity test comment updated to reflect the pinned invariant.
The matrix table previously sized to its content (fixed 32/36px square
cells), leaving roughly half of the card empty on desktop viewports.
Switch to a fixed-layout full-width table: the row-header column keeps a
set width and the nine GPU columns share the remaining space, so cells
grow into wide rectangles that fill the card at any width. Column
headers become horizontal (they no longer need to fit narrow squares),
and the 600px table min-width preserves the horizontal-scroll + sticky
row-label behavior on mobile.
…cells

Flip the GPU-pair selection matrices from upper to lower triangle: rows
now run H200 -> MI355X and columns H100 -> MI325X, with cells below the
diagonal (cells[rowKey][colKey], column display-index < row display-index)
so the storage matches the rendered grid. Slugs, labels, and cross-vendor
flags are unchanged (still canonical alphabetical order).

Also add a persistent clickability affordance: every linked cell centers
a visible north-east arrow glyph (brand-tinted on cross-vendor cells,
muted on same-vendor, brightening on hover) while ghost cells stay empty,
so linked vs no-data cells are distinguishable without hovering.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant