LennartPurucker Claude Opus 5.5 commited on
Commit
e3390a0
·
1 Parent(s): 573e3d0

Benchmark pages as tabs under a compact hero

Browse files

Each benchmark page shows one view at a time, picked from tabs at the top
of its controls: Summary (the default), Leaderboard, Quality vs time, Head
to head and Per dataset. Every view is on the page from the start, so a
switch needs no round trip, and `?view=<key>` links to one.

- The tabs, the one-row Customize bar and the shown view's first card form
one block at full width. The controls sit on a surface a shade apart
from the view, with a firmer line where it starts.
- The tab captions moved to each tab's hover text, with what the selection
covers.
- The hero drops the "Meet our benchmark" eyebrow and the stats band; the
numbers are in the Learn more pills' hover texts.
- The floating bottom selection bar and its Jump to menu are gone.
- The Summary table shows every row instead of scrolling inside a 680px
box, and its heading takes the table's own surface.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Files changed (5) hide show
  1. AGENTS.md +32 -24
  2. main.py +259 -472
  3. pages.py +128 -186
  4. views.py +127 -214
  5. website_texts.py +53 -120
AGENTS.md CHANGED
@@ -57,15 +57,15 @@ Do not edit files under `data/` by hand — they are generated artifacts.
57
  reorders the figure grid, in the browser; it computes nothing and costs no render.
58
  - Every selector lives in one card at the top of the page (`views.selector_card`, built by
59
  `pages._render_control_band` and `pages._render_beyond_control_band`). It is one `gr.HTML` of plain
60
- buttons, not Gradio controls, with a column that says how many datasets the selection covers (beside
61
- only the bar below 1420px, under it on a phone). It starts closed, as a bar styled like the per-dataset
62
- browser's (`.ta-pdbar`) but without its wash of colour, beside that column (it spans the bar and the
63
- lines, so opening only grows it): the title, which is the toggle, and
64
  shortcuts (`website_texts.SELECTOR_EXAMPLES`, `BEYOND_SELECTOR_EXAMPLES`; `views._selector_examples` drops
65
- locked chips) that pick a chip of the card, or several toggles at once, and open it there. They stay in
 
 
 
66
  the bar once it is open, pressed while the selection matches, so a second click undoes them. Opening is client-side only, so the
67
- open state survives every re-render, and a link to the card (the bottom bar's "change", Jump to) opens
68
- it too. A click updates the card in the browser
69
  (`main.taSelector`: chip state, dataset counts, that number, system warnings) and sends the whole
70
  selection as one custom `pick` event (`trigger('pick', …)` in `js_on_load`), whose listener validates
71
  every value (`views.picked`) and returns the page's `gr.State`s. A State re-renders its readers only when its value changed, and
@@ -73,10 +73,23 @@ Do not edit files under `data/` by hand — they are generated artifacts.
73
  comes from the `n_datasets_*` marker of the leaderboard that chip would select; a chip whose leaderboard
74
  is not published cannot be picked. Hover text is written into the card (`title`), from the notes dicts
75
  and `data_loading.AXIS_NOTES` (per row caption).
76
- - The figures under "Your leaderboard" are one grid in one `gr.HTML` (`views.make_figure_grid`): the
77
- overview and the win-rate matrix span the width, the three Pareto fronts (inference, end-to-end, train
78
- time) and the tuning trajectories sit two to a row (one per row below 1100px; an odd one out spans the
79
- row). "I care about" and "Measured by" are not render inputs. `main.taSelector` writes the choice onto
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  the page element (`.ta-page[data-care][data-metric]`, the `gr.TabItem`'s class), and the stylesheet
81
  orders the tiles from it: the figure that answers the choice first, its partner beside it, then the
82
  overview. The figure that leads takes the metric the reader picked and the others the other one, so both
@@ -112,18 +125,17 @@ Do not edit files under `data/` by hand — they are generated artifacts.
112
  system from its name.
113
  - Each top-level page has a `slug` (`pages.PAGES`) that is its tab id and its `?tab=<slug>` link, e.g. `huggingface.co/spaces/TabArena/leaderboard?tab=beyondarena`. `main._open_linked_page` selects it on load, and a click on a page link in the top bar writes it back to the address bar and, inside the Space, to the Hugging Face page URL over `postMessage`. Not `?page=`: Gradio reserves that name (and `deep_link`) for its own routing.
114
  - The top bar (`views.make_page_chrome`) is plain HTML above `gr.Tabs(elem_id="top-tabs")`, whose own tab bar is hidden. A page link clicks the matching Gradio tab button, found by the tab's label (`main.taGoToPage`); with the bar hidden Gradio keeps every button in its overflow menu, where the selected one carries `.selected` rather than `aria-selected`. The bar keeps one row while brand, links and actions fit and gives the links a row of their own otherwise (`main.taFitTopBar` measures, since where that happens depends on the reader's font). Its version chip and the version-history dialog are parsed from `website_texts.VERSION_HISTORY_BUTTON_TEXT` (`views.parse_version_history`), so release entries must keep the `* YYYY/MM/DD-vX.Y.Z:` form. The COI statement and the version history are dialogs: a hidden checkbox (`main.taOpenDialog`) shows an overlay on the visible band, and the dialog scrolls inside itself so a click on the overlay always closes it.
115
- - TabArena and BeyondArena share one hero (`views._page_hero`), coloured like the page's link in the top bar: one panel washed faintly in that colour and headed by a "Meet …" eyebrow (`website_texts.HERO_EYEBROW`, `BEYOND_HERO_EYEBROW`), so the background on the benchmark reads apart from the results below it. Its numbers are read from the data (`views.hero_facts`, `views.beyond_hero_facts`); TabArena's also come from the version history, including the newest entrant, which it takes from a release whose first bullet reads `Add new verified model: <name>, …`. Its citation, its team and the Learn more topics open as dialogs, with toggle ids per page (`views._hero_toggle`) since both pages are in the document at once. `views.learn_more` puts every topic into the page once, so a pill opens without a round trip to the server; the row ends with the page's call to submit a model (`website_texts.HERO_CTA`, `BEYOND_HERO_CTA`), a link out washed in the page's colour. Reference text lives there and not at the bottom of the page: the MCP & API topic (`website_texts.AGENTS_*`, CSV paths and the MCP tools) replaced the Appendix, and BeyondArena's subsets explainer is its Subsets topic, opened from the overview figures too (`views.open_doc`).
116
  - A dialog has a link of its own: while it is open, `?doc=<name>` is in the address bar (and in the Hugging Face page URL), a Learn more topic key or `cite`, `team`, `history`, `coi`; `main.taOpenLinkedDialog` opens it on load once the page `?tab=` names shows, e.g. `?tab=beyondarena&doc=subsets`.
117
  - Inside the Space the visible band comes from iframe-resizer's `getPageInfo`, which the parent answers once and afterwards only on its own scroll and resize. The first answer arrives while the frame is still short, so `main.taReaskPageInfo` asks again whenever the frame's height changes; without that the band stayed unset until the reader first scrolled.
118
  - Gradio rescopes every rule of the launch stylesheet under `.gradio-container.gradio-container-<version> .contain`. A top-level rule also keeps its written form, a rule inside `@media` does not, so a media query cannot style `.contain` or its ancestors (`.main`, `.gradio-container`); use a top-level rule with `clamp()` there instead.
119
  - `LBContainer` (`data_loading.py`) loads one subset's `website_leaderboard.csv`, lazily unzips its PNGs, and inlines its interactive HTML plots.
120
- - The cross-subset overview, titled Summary (`views.make_cross_subset_overview`), is one block: its heading (the title, what the table shows on hover) and the table. The columns: the rank in the sorted column; the Method column, sticky, with the search in its header's corner (an icon opening a box that finds a method by name), on the table's own opaque surface (`--ta-xo-bg`), the organization's logo and the name in its family colour with its variant, a system's tag (🔒 for a closed-source API, 🤖 with LLMs; `constants.tag_emoji`), ✔️, `$` and ↗; then Overall Elo and Overall improvability, the two metrics the figures show, and the train and inference costs, as in-cell bars (as long as the value is high in its column, on the log scale for the costs, so lower is shorter; coloured amber for the best through violet for the worst, `views.BAR_STOPS` as `--bar-dark` / `--bar-light`); a divider (`ta-xo-sep`) after each of those two groups; then the breakdowns by task, size and target as heat cells. There is no type column and no metric menu. Under the Method header two keys (`views._th_key`): the colours of the names by short family names (`views._method_legend`, full names on hover), and the marks ✔️, `$`, ↗ and the system tags; `*` (imputed) is keyed under the headline Elo and `†` under the train time, each only when a row on screen has it; the medals need no key. The breakdowns start folded into stub columns after the others (`views._FOLDING_GROUPS`; their cells carry `data-g`, the stubs `data-stub`, and the table's `data-open` names the open ones); a stub opens its group, the ✕ in its heading folds it, a group a selector comes to point at opens by itself (the table's `data-auto`), and each open group has an Elo | Improv. switch: every breakdown column is rendered in both metrics (`data-m`), the table's `data-m-<group>` shows one. In the browser (`main.taOverviewTable`): a click on a number heading sorts by it (best first, again reverses; the sort is kept by the heading's `data-label`, the column id, and falls back to the headline Elo when its column folds or switches away); the search hides rows; the sort, the open groups, their metrics and the search survive re-renders (`window.taOverviewState`). Each number's hover gives its rank in the column (and the Elo 95% CI). A "new" badge marks the methods the last three entrant-adding releases of the version history named (`views._recent_additions`).
121
  - Logos: the `Organization` column of `website_leaderboard.csv` comes from `MethodMetadata.organization` upstream; `views.ORGANIZATION_LOGOS` maps it to a file in `assets/logos/` (64 px PNGs through LFS, SVGs as text), served with `gr.set_static_paths` (`main.main`). A logo fills its tile at its own shape, with no backdrop; one whose artwork is dark on transparency (Amazon, NVIDIA, Perpetual ML) has a white tile baked into the file, since it would vanish on the dark table. An organization without a logo shows its initial, a method without one an empty tile; artifacts generated before the column existed show no logos. A new organization needs its name spelled as upstream spells it, plus a logo file and an entry here.
122
  - The reactive render is driven by one `gr.State` per axis: selecting a tab or toggle updates that state, and the `@gr.render` block in `pages.py` rebuilds the subset's figures and table.
123
- - The full leaderboard table is `views.make_leaderboard`, a card like the figure tiles that opens in the browser (`main.taToggleTable`) and embeds the generated `leaderboard_table.html`, so it reuses the explorers' family colours, variant chips, tag chips and imputation markers rather than reimplementing them. Its toolbar (search, family toggles, variants, a Methods and a Columns menu), the 1-based positions and the default columns (Elo, Improvability, runtimes) are upstream's; the "new" badges come from here, the methods `views._recent_additions` names, as `data-new` sent by `main.taSendNew`. `views.make_leaderboard_gradio` is the fallback for subsets whose artifacts predate that file; its filtering lives in `views.filter_leaderboard`, kept pure so it can be tested without a Gradio context. Neither path may reintroduce the `gradio_leaderboard` custom component, which pinned Gradio < 6.
124
- - The per-dataset browser (`views.make_per_dataset_block`, embedding `per_dataset_explorer.html`) hangs
125
- collapsed right under the cross-subset overview ("Dataset by dataset"): both look across the whole
126
- benchmark, unlike "Your leaderboard" below. From 1100px wide it shows its dataset list beside the
127
  chosen dataset (upstream). It is read from the `tasks_all/datasets_all` sibling of the selected cell, not from the cell itself: a dataset's own
128
  rank and gap do not depend on which *other* datasets share its leaderboard, so upstream publishes one per
129
  (entrants, imputation, splits) rather than for all 60 task/dataset cells. The current task and
@@ -134,14 +146,10 @@ Do not edit files under `data/` by hand — they are generated artifacts.
134
  splits, α = 0.05, no verdict below five splits), all computed upstream in
135
  `plot/interactive/per_dataset_explorer.py`; nothing here recomputes them. Its "Target" filter
136
  (balanced / imbalanced) reads the `target_imbalanced` verdict from the dataset metadata upstream.
137
- - The dataset browser and the full table are collapsed by default; the browser's frame is only built once
138
- it has been opened (its artifact is the largest the site ships). The selection bar at the bottom hides
139
- while an open browser fills the view (`main.taOverDataBrowser`).
140
  - Section headings come from `views.section_head`: a plain title, a muted line beside it saying what the
141
- section holds, actions at the right, and the longer text as the title's hover. "Your leaderboard" (the
142
- selected leaderboard's figures and table) is rendered with its selection, its line from `views.lb_spec`:
143
- the number of datasets, split by task where the selection spans both kinds, then the selector card's
144
- summary terms. Each figure tile is one flat card with one header row: the figure's name, its caption from
145
  `website_texts.FIGURE_CAPTIONS` (shown only where it fits whole, `main.taFitCaptions`; the title's hover
146
  carries it either way), and ghost buttons "Customize" (the explorer's controls; paper view is the default), a
147
  "Download" menu (SVG, PDF, PNG; shown in paper view) and ⤢.
 
57
  reorders the figure grid, in the browser; it computes nothing and costs no render.
58
  - Every selector lives in one card at the top of the page (`views.selector_card`, built by
59
  `pages._render_control_band` and `pages._render_beyond_control_band`). It is one `gr.HTML` of plain
60
+ buttons, not Gradio controls. It starts closed, as one row (`.ta-pdbar` without its wash of colour): the
61
+ title "Customize", which is the toggle, small
 
 
62
  shortcuts (`website_texts.SELECTOR_EXAMPLES`, `BEYOND_SELECTOR_EXAMPLES`; `views._selector_examples` drops
63
+ locked chips) that pick a chip of the card, or several toggles at once, and open it there, and at its end
64
+ how many datasets the selection covers. The shortcuts scroll sideways where the row cannot hold them, on
65
+ a line of their own on a phone. Open, the lines grow under the bar, headed by what the choices change
66
+ (`website_texts.SELECTOR_SUB`). The shortcuts stay in
67
  the bar once it is open, pressed while the selection matches, so a second click undoes them. Opening is client-side only, so the
68
+ open state survives every re-render. A click updates the card in the browser
 
69
  (`main.taSelector`: chip state, dataset counts, that number, system warnings) and sends the whole
70
  selection as one custom `pick` event (`trigger('pick', …)` in `js_on_load`), whose listener validates
71
  every value (`views.picked`) and returns the page's `gr.State`s. A State re-renders its readers only when its value changed, and
 
73
  comes from the `n_datasets_*` marker of the leaderboard that chip would select; a chip whose leaderboard
74
  is not published cannot be picked. Hover text is written into the card (`title`), from the notes dicts
75
  and `data_loading.AXIS_NOTES` (per row caption).
76
+ - A benchmark page shows one view at a time, picked from tabs at the top of its controls
77
+ (`views.view_bar`, `website_texts.VIEW_TABS`): Summary, the default (the summary table across subsets;
78
+ BeyondArena's overview images), Leaderboard (the Elo overview chart and the full table, open), Quality
79
+ vs time (the Pareto fronts and tuning trajectories), Head to head (the win-rate matrix) and Per dataset
80
+ (the per-dataset browser). Each tab says on hover what its view shows and what the selection covers
81
+ (from `views.lb_spec`); the bar carries the Follow picker. The selector card hangs under it, and the
82
+ shown view under the card as its body on the card's surface (`.block.ta-sel-block` and `.ta-view` pulled
83
+ up over the block gap), so the tabs, the choices and the view read as one panel. The bar renders with the selection (for the Follow list) and marks the page's shown view
84
+ each time it is drawn (`main.taSyncViewTabs`). Every view is a `gr.Column(elem_classes=["ta-view",
85
+ "ta-view-<key>"])` with its own `@gr.render` blocks, all on the page from the start; the browser shows
86
+ the one the page element's `data-view` names (`main.taShowView`, the generated `__TA_VIEW_RULES__`), so
87
+ a switch costs no round trip, loads the view's explorers (`taLoadFrames`) and goes into the address as
88
+ `?view=<key>` (`taOpenLinkedView` reads it back, after `?tab=`).
89
+ - Each figure view is one grid in one `gr.HTML` (`views.make_figure_grid(lb, keys)`, the keys from
90
+ `views.VIEW_TILES`): the overview and the win-rate matrix span the width, the three Pareto fronts
91
+ (inference, end-to-end, train time) and the tuning trajectories sit two to a row (one per row below
92
+ 1100px; an odd one out spans the row). "I care about" and "Measured by" are not render inputs. `main.taSelector` writes the choice onto
93
  the page element (`.ta-page[data-care][data-metric]`, the `gr.TabItem`'s class), and the stylesheet
94
  orders the tiles from it: the figure that answers the choice first, its partner beside it, then the
95
  overview. The figure that leads takes the metric the reader picked and the others the other one, so both
 
125
  system from its name.
126
  - Each top-level page has a `slug` (`pages.PAGES`) that is its tab id and its `?tab=<slug>` link, e.g. `huggingface.co/spaces/TabArena/leaderboard?tab=beyondarena`. `main._open_linked_page` selects it on load, and a click on a page link in the top bar writes it back to the address bar and, inside the Space, to the Hugging Face page URL over `postMessage`. Not `?page=`: Gradio reserves that name (and `deep_link`) for its own routing.
127
  - The top bar (`views.make_page_chrome`) is plain HTML above `gr.Tabs(elem_id="top-tabs")`, whose own tab bar is hidden. A page link clicks the matching Gradio tab button, found by the tab's label (`main.taGoToPage`); with the bar hidden Gradio keeps every button in its overflow menu, where the selected one carries `.selected` rather than `aria-selected`. The bar keeps one row while brand, links and actions fit and gives the links a row of their own otherwise (`main.taFitTopBar` measures, since where that happens depends on the reader's font). Its version chip and the version-history dialog are parsed from `website_texts.VERSION_HISTORY_BUTTON_TEXT` (`views.parse_version_history`), so release entries must keep the `* YYYY/MM/DD-vX.Y.Z:` form. The COI statement and the version history are dialogs: a hidden checkbox (`main.taOpenDialog`) shows an overlay on the visible band, and the dialog scrolls inside itself so a click on the overlay always closes it.
128
+ - TabArena and BeyondArena share one hero (`views._page_hero`), coloured like the page's link in the top bar: one panel washed faintly in that colour, so the background on the benchmark reads apart from the results below it: the lead sentence with its links, then the Learn more row. The Learn more pills carry the benchmark's numbers in their hover texts (`website_texts.HERO_HINTS`, `BEYOND_HERO_HINTS`), read from the data (`views.hero_facts`, `views.beyond_hero_facts`). Its citation, its team and the Learn more topics open as dialogs, with toggle ids per page (`views._hero_toggle`) since both pages are in the document at once. `views.learn_more` puts every topic into the page once, so a pill opens without a round trip to the server; the row ends with the page's call to submit a model (`website_texts.HERO_CTA`, `BEYOND_HERO_CTA`), a link out washed in the page's colour. Reference text lives there and not at the bottom of the page: the MCP & API topic (`website_texts.AGENTS_*`, CSV paths and the MCP tools) replaced the Appendix, and BeyondArena's subsets explainer is its Subsets topic, opened from the overview figures too (`views.open_doc`).
129
  - A dialog has a link of its own: while it is open, `?doc=<name>` is in the address bar (and in the Hugging Face page URL), a Learn more topic key or `cite`, `team`, `history`, `coi`; `main.taOpenLinkedDialog` opens it on load once the page `?tab=` names shows, e.g. `?tab=beyondarena&doc=subsets`.
130
  - Inside the Space the visible band comes from iframe-resizer's `getPageInfo`, which the parent answers once and afterwards only on its own scroll and resize. The first answer arrives while the frame is still short, so `main.taReaskPageInfo` asks again whenever the frame's height changes; without that the band stayed unset until the reader first scrolled.
131
  - Gradio rescopes every rule of the launch stylesheet under `.gradio-container.gradio-container-<version> .contain`. A top-level rule also keeps its written form, a rule inside `@media` does not, so a media query cannot style `.contain` or its ancestors (`.main`, `.gradio-container`); use a top-level rule with `clamp()` there instead.
132
  - `LBContainer` (`data_loading.py`) loads one subset's `website_leaderboard.csv`, lazily unzips its PNGs, and inlines its interactive HTML plots.
133
+ - The cross-subset overview, the Summary view (`views.make_cross_subset_overview`), is one table at its full length (it scrolls sideways only). The columns: the rank in the sorted column; the Method column, sticky, with the search in its header's corner (an icon opening a box that finds a method by name), on the table's own opaque surface (`--ta-xo-bg`), the organization's logo and the name in its family colour with its variant, a system's tag (🔒 for a closed-source API, 🤖 with LLMs; `constants.tag_emoji`), ✔️, `$` and ↗; then Overall Elo and Overall improvability, the two metrics the figures show, and the train and inference costs, as in-cell bars (as long as the value is high in its column, on the log scale for the costs, so lower is shorter; coloured amber for the best through violet for the worst, `views.BAR_STOPS` as `--bar-dark` / `--bar-light`); a divider (`ta-xo-sep`) after each of those two groups; then the breakdowns by task, size and target as heat cells. There is no type column and no metric menu. Under the Method header two keys (`views._th_key`): the colours of the names by short family names (`views._method_legend`, full names on hover), and the marks ✔️, `$`, ↗ and the system tags; `*` (imputed) is keyed under the headline Elo and `†` under the train time, each only when a row on screen has it; the medals need no key. The breakdowns start folded into stub columns after the others (`views._FOLDING_GROUPS`; their cells carry `data-g`, the stubs `data-stub`, and the table's `data-open` names the open ones); a stub opens its group, the ✕ in its heading folds it, a group a selector comes to point at opens by itself (the table's `data-auto`), and each open group has an Elo | Improv. switch: every breakdown column is rendered in both metrics (`data-m`), the table's `data-m-<group>` shows one. In the browser (`main.taOverviewTable`): a click on a number heading sorts by it (best first, again reverses; the sort is kept by the heading's `data-label`, the column id, and falls back to the headline Elo when its column folds or switches away); the search hides rows; the sort, the open groups, their metrics and the search survive re-renders (`window.taOverviewState`). Each number's hover gives its rank in the column (and the Elo 95% CI). A "new" badge marks the methods the last three entrant-adding releases of the version history named (`views._recent_additions`), taken from a release whose first bullet reads `Add new verified model: <name>, …`.
134
  - Logos: the `Organization` column of `website_leaderboard.csv` comes from `MethodMetadata.organization` upstream; `views.ORGANIZATION_LOGOS` maps it to a file in `assets/logos/` (64 px PNGs through LFS, SVGs as text), served with `gr.set_static_paths` (`main.main`). A logo fills its tile at its own shape, with no backdrop; one whose artwork is dark on transparency (Amazon, NVIDIA, Perpetual ML) has a white tile baked into the file, since it would vanish on the dark table. An organization without a logo shows its initial, a method without one an empty tile; artifacts generated before the column existed show no logos. A new organization needs its name spelled as upstream spells it, plus a logo file and an entry here.
135
  - The reactive render is driven by one `gr.State` per axis: selecting a tab or toggle updates that state, and the `@gr.render` block in `pages.py` rebuilds the subset's figures and table.
136
+ - The full leaderboard table is `views.make_leaderboard`, a card like the figure tiles, open in the Leaderboard view, that hides and shows in the browser (`main.taToggleTable`) and embeds the generated `leaderboard_table.html`, so it reuses the explorers' family colours, variant chips, tag chips and imputation markers rather than reimplementing them. Its toolbar (search, family toggles, variants, a Methods and a Columns menu), the 1-based positions and the default columns (Elo, Improvability, runtimes) are upstream's; the "new" badges come from here, the methods `views._recent_additions` names, as `data-new` sent by `main.taSendNew`. `views.make_leaderboard_gradio` is the fallback for subsets whose artifacts predate that file; its filtering lives in `views.filter_leaderboard`, kept pure so it can be tested without a Gradio context. Neither path may reintroduce the `gradio_leaderboard` custom component, which pinned Gradio < 6.
137
+ - The per-dataset browser (`views.make_per_dataset_block`, embedding `per_dataset_explorer.html`) is the
138
+ Per dataset view; its frame loads when the view is first shown. From 1100px wide it shows its dataset list beside the
 
139
  chosen dataset (upstream). It is read from the `tasks_all/datasets_all` sibling of the selected cell, not from the cell itself: a dataset's own
140
  rank and gap do not depend on which *other* datasets share its leaderboard, so upstream publishes one per
141
  (entrants, imputation, splits) rather than for all 60 task/dataset cells. The current task and
 
146
  splits, α = 0.05, no verdict below five splits), all computed upstream in
147
  `plot/interactive/per_dataset_explorer.py`; nothing here recomputes them. Its "Target" filter
148
  (balanced / imbalanced) reads the `target_imbalanced` verdict from the dataset metadata upstream.
 
 
 
149
  - Section headings come from `views.section_head`: a plain title, a muted line beside it saying what the
150
+ section holds, actions at the right, and the longer text as the title's hover. The selection's line
151
+ (`views.lb_spec`, under the view tabs): the number of datasets, split by task where the selection spans
152
+ both kinds, then the selection's terms. Each figure tile is one flat card with one header row: the figure's name, its caption from
 
153
  `website_texts.FIGURE_CAPTIONS` (shown only where it fits whole, `main.taFitCaptions`; the title's hover
154
  carries it either way), and ghost buttons "Customize" (the explorer's controls; paper view is the default), a
155
  "Download" menu (SVG, PDF, PNG; shown in paper view) and ⤢.
main.py CHANGED
@@ -4,6 +4,7 @@ import gradio as gr
4
 
5
  import json
6
 
 
7
  from api import register_api
8
  from data_loading import BEYOND_DATA_DIR, DATA_DIR
9
  from pages import PAGES, render_page
@@ -13,10 +14,6 @@ from views import LOGO_DIR, VERSION_HISTORY_TOGGLE, make_page_chrome
13
  CSS = """
14
  /* Use the full page width instead of gradio's narrow centered column. */
15
  .gradio-container { max-width: 100% !important; }
16
- /* Leave room for the fixed selection bar (.ta-selbar sits 18px off the bottom and is ~42px
17
- tall). Without this the page still scrolls to its end, but the end sits underneath the bar,
18
- which reads as not being able to get to the bottom. */
19
- .gradio-container { padding-bottom: 78px !important; }
20
  .gradio-container { padding-top: 6px !important; }
21
 
22
  /* --- Colour emoji ----------------------------------------------------------
@@ -118,17 +115,16 @@ CSS = """
118
  then one line per choice with the captions in one column down the left, and beside the lines a
119
  column that says what the selection covers, its number set like the hero's. A chip in a row that
120
  filters the datasets carries the number of datasets the leaderboard would cover with it picked.
121
- The card starts closed, as its bar alone, beside the column that says what the selection covers:
122
- the per-dataset browser's bar (`.ta-pdbar`), its title the button and offering shortcuts into the
123
- card. Opened, the lines grow under the bar and the column grows with them. */
124
  .ta-sel-block .html-container { padding: 0; }
125
  .ta-sel {
126
  --ta-page-color: var(--ta-accent);
127
  --ta-pd-color: var(--ta-page-color);
128
  display: grid;
129
- grid-template-columns: minmax(0, 1fr) 212px;
130
- grid-template-rows: auto 1fr;
131
- grid-template-areas: "bar result" "main result" "warns warns";
132
  overflow: hidden;
133
  border: 1px solid var(--ta-line-soft);
134
  border-radius: 14px;
@@ -140,20 +136,21 @@ CSS = """
140
  selling points, a second orange band read as more of the same. The icon, the chevron and the
141
  shortcuts carry the colour instead. */
142
  .ta-sel-bar { grid-area: bar; }
143
- .ta-sel .ta-sel-bar { background: transparent; }
144
  .ta-sel .ta-sel-bar:hover { background: color-mix(in srgb, var(--ta-ink) 4%, transparent); }
145
- .ta-sel:not([data-open="true"]) > :is(.ta-sel-main, .ta-sel-warns),
146
- .ta-sel:not([data-open="true"]) .ta-sel-lead { display: none; }
147
  .ta-sel-main { grid-area: main; min-width: 0; padding: 0 18px 2px; }
148
- .ta-sel-main > .ta-sel-line:first-child { border-top: 0; }
149
- .ta-sel-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 14px; }
150
- .ta-sel-title { margin: 0; font-size: 1.08rem; letter-spacing: -0.01em; }
 
 
151
  /* The title opens the card, its chevron filled in the page's colour while there is more to see,
152
  quiet and turned once the card is open. */
153
  .ta-sel-toggle {
154
  display: inline-flex;
155
  align-items: center;
156
- gap: 10px;
157
  margin: 0;
158
  padding: 0;
159
  border: 0;
@@ -167,8 +164,8 @@ CSS = """
167
  display: grid;
168
  place-items: center;
169
  flex: none;
170
- width: 24px;
171
- height: 24px;
172
  border: 1px solid transparent;
173
  border-radius: 50%;
174
  background: var(--ta-page-color);
@@ -177,8 +174,8 @@ CSS = """
177
  }
178
  .ta-sel-chev::before {
179
  content: "";
180
- width: 7px;
181
- height: 7px;
182
  border-style: solid;
183
  border-color: var(--ta-on-accent);
184
  border-width: 0 2px 2px 0;
@@ -192,24 +189,35 @@ CSS = """
192
  transform: rotate(180deg);
193
  }
194
  .ta-sel[data-open="true"] .ta-sel-chev::before { border-color: var(--ta-ink); }
195
- /* The shortcuts: dashed, as suggestions rather than settings, each a "+" until it is picked. */
196
- .ta-sel-try { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 7px; }
197
- .ta-sel-try-lead { margin-right: 2px; color: var(--ta-muted); font-size: 0.8rem; font-weight: 600; }
 
 
 
 
 
 
 
 
 
 
198
  .ta-sel-ex {
 
199
  margin: 0;
200
- padding: 3px 11px 3px 9px;
201
  border: 1px dashed color-mix(in srgb, var(--ta-page-color) 60%, transparent);
202
  border-radius: 999px;
203
  background: transparent;
204
  color: var(--ta-ink);
205
  font: inherit;
206
- font-size: 0.8rem;
207
  font-weight: 600;
208
  white-space: nowrap;
209
  cursor: pointer;
210
  transition: background .14s ease, border-color .14s ease;
211
  }
212
- .ta-sel-ex::before { content: "+"; margin-right: 5px; color: var(--ta-page-color); font-weight: 800; }
213
  .ta-sel-ex:hover,
214
  .ta-sel-ex[aria-pressed="true"] {
215
  border-style: solid;
@@ -218,18 +226,12 @@ CSS = """
218
  .ta-sel-ex[aria-pressed="true"]::before { content: "\\2713"; }
219
  .ta-sel-ex:focus-visible { outline: 2px solid var(--ta-page-color); outline-offset: 2px; }
220
  .ta-sel-ex:disabled { opacity: 0.4; cursor: not-allowed; }
221
- /* On a phone the shortcuts keep to one row that scrolls sideways, fading out at the edge it
222
- continues past, and the icon stays with the title. */
 
 
223
  @media (max-width: 640px) {
224
- .ta-sel-bar { align-items: flex-start !important; }
225
- .ta-sel-try {
226
- flex-wrap: nowrap;
227
- overflow-x: auto;
228
- scrollbar-width: none;
229
- mask-image: linear-gradient(90deg, #000 85%, transparent);
230
- }
231
- .ta-sel-try::-webkit-scrollbar { display: none; }
232
- .ta-sel-try-lead { flex: none; }
233
  }
234
  /* The chip a shortcut picked pulses as the card opens, so the reader sees where it lives. */
235
  .ta-sel .ta-chip.ta-flash { animation: ta-sel-flash 0.9s ease-out 2; }
@@ -390,31 +392,135 @@ CSS = """
390
  .ta-sel .ta-chip[role="switch"][aria-checked="true"] .ta-chip-mark { background: var(--ta-page-color); }
391
  .ta-sel .ta-chip[role="switch"][aria-checked="true"] .ta-chip-mark::after { transform: translateX(12px); }
392
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
  /* What the selection covers: the number of datasets, beside the bar and the lines. */
394
  .ta-sel-result {
395
  grid-area: result;
396
  display: flex;
397
- flex-direction: column;
398
- justify-content: center;
399
  min-width: 0;
400
- padding: 12px 18px;
401
  border-left: 1px solid var(--ta-line-soft);
402
- background: linear-gradient(200deg, color-mix(in srgb, var(--ta-page-color) 13%, transparent), transparent 70%);
403
  }
 
404
  .ta-sel-total {
405
  display: flex;
406
- flex-wrap: wrap;
407
  align-items: baseline;
408
- gap: 2px 8px;
409
  margin: 0;
410
  color: var(--ta-ink);
411
- font-size: 0.95rem;
412
  font-weight: 700;
 
413
  }
414
  .ta-sel-unit { white-space: nowrap; }
415
  .ta-sel-num {
416
  color: var(--ta-page-color);
417
- font-size: 2.15rem;
418
  font-weight: 800;
419
  font-variant-numeric: tabular-nums;
420
  letter-spacing: -0.03em;
@@ -454,25 +560,6 @@ CSS = """
454
  .ta-sel-warn-body ul { padding-left: 1.2em; }
455
  .ta-sel-warn-body > :last-child { margin-bottom: 0; }
456
 
457
- /* Where the lines need the card's width, the number stays beside the bar alone and the lines take the
458
- whole width under both. */
459
- @media (max-width: 1420px) {
460
- .ta-sel { grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: none; grid-template-areas: "bar result" "main main" "warns warns"; }
461
- .ta-sel-result { padding: 10px 18px; }
462
- .ta-sel[data-open="true"] .ta-sel-result { border-bottom: 1px solid var(--ta-line-soft); }
463
- .ta-sel-num { font-size: 1.45rem; }
464
- }
465
- /* On a phone the number goes under the bar, as one line. */
466
- @media (max-width: 640px) {
467
- .ta-sel { grid-template-columns: minmax(0, 1fr); grid-template-areas: "bar" "result" "main" "warns"; }
468
- .ta-sel-result {
469
- flex-direction: row;
470
- justify-content: flex-start;
471
- border-top: 1px solid var(--ta-line-soft);
472
- border-left: 0;
473
- background: linear-gradient(100deg, color-mix(in srgb, var(--ta-page-color) 13%, transparent), transparent 60%);
474
- }
475
- }
476
  /* Where the line cannot hold both rows, the second takes a line of its own, caption in the column. */
477
  @media (max-width: 1180px) {
478
  .ta-sel-line { flex-direction: column; }
@@ -526,6 +613,8 @@ CSS = """
526
  --ta-card: rgba(255, 255, 255, 0.04);
527
  --ta-strip: rgba(255, 255, 255, 0.06);
528
  --ta-raised: #1b1b22;
 
 
529
  --ta-line: rgba(255, 255, 255, 0.2);
530
  --ta-line-soft: rgba(255, 255, 255, 0.1);
531
  --ta-ink: #f4f4f5;
@@ -543,137 +632,8 @@ CSS = """
543
  orange of its old tab, BeyondArena the teal of its hero. */
544
  --ta-tabarena: #fb923c;
545
  --ta-beyondarena: #2dd4bf;
546
- /* The dot that marks a benchmark as alive (its latest release). */
547
- --ta-live: #4ade80;
548
- }
549
-
550
- /* The bar is fixed, so the block that holds it takes no room, and its column gives back the gap
551
- before it. */
552
- .ta-selbar-host, .ta-selbar-host > .html-container { padding: 0; }
553
- .column:has(> .ta-selbar-host) { margin-top: -16px; }
554
- /* Sticky summary of the current selection, revealed once the control card scrolls away (see
555
- main.taWatchControls), with the page's "Jump to" menu at its end. Bottom-centre: it is a
556
- status line for the whole page and the one element that floats over it. */
557
- .ta-selbar {
558
- position: fixed;
559
- left: 50%;
560
- top: calc(var(--ta-vp-h) - 18px);
561
- z-index: 60;
562
- display: flex;
563
- align-items: center;
564
- gap: 4px;
565
- max-width: min(94vw, 1100px);
566
- padding: 5px;
567
- border: 1px solid var(--ta-line);
568
- border-radius: 999px;
569
- background: rgba(24, 24, 28, 0.94);
570
- box-shadow: var(--ta-shadow);
571
- color: #dfe5f5;
572
- font-family: inherit;
573
- /* Hidden until the observer says the controls are off screen. `top` anchors the bar's own
574
- top edge, so it is pulled back up by its full height to sit on the line above. */
575
- opacity: 0;
576
- pointer-events: none;
577
- transform: translate(-50%, calc(-100% + 12px)) translateY(var(--ta-vp-top));
578
- /* The band offset rides in the same transform, so following the screen costs no layout;
579
- only opacity and the reveal slide are transitioned, or every scroll would ease. */
580
- transition: opacity .16s ease;
581
- }
582
- .ta-selbar.ta-show {
583
- opacity: 1;
584
- pointer-events: auto;
585
- transform: translate(-50%, -100%) translateY(var(--ta-vp-top));
586
- }
587
- /* The summary half scrolls sideways on a narrow screen; the menu beside it cannot sit inside
588
- that scroller, or the list opening above it would be clipped. */
589
- .ta-selbar-view {
590
- display: flex;
591
- align-items: center;
592
- gap: 7px;
593
- min-width: 0;
594
- padding: 3px 6px 3px 10px;
595
- border-radius: 999px;
596
- color: inherit !important;
597
- text-decoration: none !important;
598
- overflow-x: auto;
599
- scrollbar-width: none;
600
- }
601
- .ta-selbar-view:hover { background: var(--ta-chip); }
602
- .ta-selbar-lead {
603
- flex: 0 0 auto;
604
- font-size: 0.66em;
605
- font-weight: 700;
606
- letter-spacing: 0.09em;
607
- text-transform: uppercase;
608
- opacity: 0.5;
609
- }
610
- .ta-selchip {
611
- flex: 0 0 auto;
612
- padding: 3px 9px;
613
- border-radius: 999px;
614
- background: rgba(158, 176, 208, 0.16);
615
- font-size: 0.8em;
616
- font-weight: 600;
617
- white-space: nowrap;
618
  }
619
 
620
- .ta-selbar-go {
621
- flex: 0 0 auto;
622
- margin-left: 3px;
623
- padding: 3px 10px;
624
- border-radius: 999px;
625
- background: rgba(110, 140, 245, 0.34);
626
- color: #eef2ff;
627
- font-size: 0.78em;
628
- font-weight: 700;
629
- white-space: nowrap;
630
- }
631
- .ta-jumpmenu { position: relative; flex: 0 0 auto; }
632
- .ta-jumpmenu > summary {
633
- padding: 6px 12px;
634
- border-left: 1px solid var(--ta-line-soft);
635
- border-radius: 0 999px 999px 0;
636
- font-size: 0.8em;
637
- font-weight: 700;
638
- white-space: nowrap;
639
- list-style: none;
640
- cursor: pointer;
641
- user-select: none;
642
- }
643
- .ta-jumpmenu > summary::-webkit-details-marker { display: none; }
644
- .ta-jumpmenu > summary::after { content: " \\25B4"; opacity: 0.6; }
645
- .ta-jumpmenu > summary:hover,
646
- .ta-jumpmenu[open] > summary { background: var(--ta-chip); }
647
- .ta-jumpmenu-list {
648
- position: absolute;
649
- right: 0;
650
- bottom: calc(100% + 10px);
651
- display: flex;
652
- flex-direction: column;
653
- min-width: 250px;
654
- padding: 6px;
655
- border: 1px solid var(--ta-line);
656
- border-radius: 12px;
657
- background: var(--ta-raised);
658
- box-shadow: var(--ta-shadow);
659
- }
660
- .ta-jumpmenu-list a,
661
- .ta-jumpmenu-list button {
662
- display: block;
663
- padding: 7px 10px;
664
- border: 0;
665
- border-radius: 7px;
666
- background: none;
667
- color: var(--ta-ink) !important;
668
- font: inherit;
669
- font-size: 0.86em;
670
- font-weight: 600;
671
- text-align: left;
672
- text-decoration: none !important;
673
- cursor: pointer;
674
- }
675
- .ta-jumpmenu-list a:hover,
676
- .ta-jumpmenu-list button:hover { background: var(--ta-accent-tint); }
677
 
678
 
679
  /* The models-vs-systems warning. It appears the moment a system joins the field, and it is the
@@ -910,25 +870,7 @@ CSS = """
910
  .ta-expand { padding: 4px 10px !important; font-size: 0.95rem !important; line-height: 1; }
911
  /* The full table's card (views.make_leaderboard) takes the tile look, below the grid. */
912
  .ta-lbtile { margin: 0 0 18px 0; }
913
- /* The dataset browser hangs right under the overview table it extends (views.make_per_dataset_block).
914
- On TabArena it is the table's own bottom bar: pulled up over the 16px between blocks so its top
915
- border lies on the table's bottom one, the corners where they meet squared. BeyondArena's sits
916
- under its overview figures instead, a block of its own. */
917
- .ta-pdrawer { margin-top: 2px !important; }
918
- .ta-pdrawer:has(#ta-perdataset-section) {
919
- margin-top: -17px !important;
920
- border-top-left-radius: 0 !important;
921
- border-top-right-radius: 0 !important;
922
- }
923
- .ta-page:has(#ta-perdataset-section) .ta-xo-scroll {
924
- border-bottom-left-radius: 0;
925
- border-bottom-right-radius: 0;
926
- }
927
- /* Its bar asks to be opened: a wash of the page's own colour (TabArena orange, BeyondArena teal, as in
928
- the top bar and the overview table above it), an icon, the count in the caption and a filled
929
- button, and the whole bar takes the click (main.taPdBarClick). Open, the button goes quiet. */
930
- .ta-pdrawer { --ta-pd-color: var(--ta-tabarena); }
931
- .ta-pdrawer:has(#beyond-perdataset-section) { --ta-pd-color: var(--ta-beyondarena); }
932
  .ta-pdbar {
933
  cursor: pointer;
934
  flex-wrap: nowrap !important;
@@ -955,48 +897,9 @@ CSS = """
955
  color: var(--ta-pd-color);
956
  background: color-mix(in srgb, var(--ta-pd-color) 20%, transparent);
957
  }
958
- .ta-pdtext { display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; min-width: 0; }
959
- .ta-pdbar .ta-figbar-title { font-size: 1.08rem; }
960
- .ta-pdtoggle {
961
- flex: none;
962
- margin: 0 !important;
963
- padding: 9px 18px;
964
- border: 1px solid transparent;
965
- border-radius: 999px;
966
- background: var(--ta-pd-color);
967
- color: var(--ta-on-accent);
968
- font: inherit;
969
- font-size: 0.9rem;
970
- font-weight: 700;
971
- white-space: nowrap;
972
- cursor: pointer;
973
- box-shadow: 0 4px 14px color-mix(in srgb, var(--ta-pd-color) 32%, transparent);
974
- transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease;
975
- }
976
- .ta-pdbar:hover .ta-pdtoggle {
977
- filter: brightness(1.08);
978
- transform: translateY(-1px);
979
- box-shadow: 0 6px 18px color-mix(in srgb, var(--ta-pd-color) 42%, transparent);
980
- }
981
- .ta-pdtoggle:focus-visible { outline: 2px solid var(--ta-pd-color); outline-offset: 3px; }
982
- .ta-pdbar.is-open .ta-pdtoggle,
983
- .ta-pdbar.is-open:hover .ta-pdtoggle {
984
- padding: 5px 13px;
985
- border-color: var(--ta-line-soft);
986
- background: transparent;
987
- color: var(--ta-ink);
988
- font-weight: 600;
989
- box-shadow: none;
990
- filter: none;
991
- transform: none;
992
- }
993
  @media (max-width: 640px) {
994
  .ta-pdbar { flex-wrap: wrap !important; }
995
- .ta-pdbar .ta-pdtext { flex-basis: 0; }
996
- .ta-pdbar .ta-figbar-actions { width: 100%; }
997
- .ta-pdbar:not(.is-open) .ta-pdtoggle { width: 100%; }
998
  }
999
- html.ta-tile-open .ta-selbar { display: none !important; }
1000
 
1001
  /* --- Follow a method (views.follow_picker, the ◎ on each overview row, main.taToggleFollow) ----- */
1002
  .ta-follow { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin: 2px 0 4px 0; }
@@ -1173,7 +1076,6 @@ html.ta-tile-open .ta-selbar { display: none !important; }
1173
  .ta-xo-scroll {
1174
  --ta-xo-bg: color-mix(in srgb, #ffffff 4%, var(--ta-page));
1175
  overflow: auto;
1176
- max-height: 680px;
1177
  border: 1px solid var(--ta-line-soft);
1178
  border-radius: 12px;
1179
  background: var(--ta-xo-bg);
@@ -1200,12 +1102,14 @@ html.ta-tile-open .ta-selbar { display: none !important; }
1200
  top: 0;
1201
  z-index: 3;
1202
  height: 32px;
1203
- background: var(--ta-raised);
1204
  color: var(--ta-ink);
1205
  font-size: 0.8rem;
1206
  font-weight: 700;
1207
  }
1208
  .ta-xo thead tr:nth-child(2) th { top: 32px; }
 
 
1209
  .ta-xo .ta-group-h {
1210
  border-bottom-color: var(--ta-line);
1211
  color: var(--ta-muted);
@@ -1348,7 +1252,7 @@ html.ta-tile-open .ta-selbar { display: none !important; }
1348
  background: var(--ta-xo-bg);
1349
  text-align: left;
1350
  }
1351
- .ta-xo thead .ta-xo-model { z-index: 4; background: var(--ta-raised); }
1352
  .ta-xo tbody tr:hover .ta-xo-model { background: color-mix(in srgb, var(--ta-ink) 5%, var(--ta-xo-bg)); }
1353
  .ta-xo-id { display: inline-flex; align-items: center; gap: 8px; }
1354
  /* A logo fills its tile at its own shape and colours; the few whose artwork would vanish on a dark
@@ -1712,33 +1616,24 @@ button.ta-jump { background: none; color: inherit; font: inherit; font-size: 0.9
1712
  .ta-ci-head { opacity: 0.55; font-weight: 400; font-size: 0.82em; }
1713
 
1714
  /* --- A benchmark page's hero (views._page_hero) ---------------------------------------------
1715
- One sentence on what the benchmark is with its links beside it, a band of four selling points
1716
- whose numbers come from the data, and the Learn more row. Its colour is the page's own, as on
1717
- the page's link in the top bar. */
1718
- /* The hero is the page's background, so it sits apart from the results under it: one panel washed
1719
- faintly in the page's colour, headed by a small "Meet …" eyebrow. */
1720
  .ta-phero {
1721
  --ta-page-color: var(--ta-accent);
1722
  display: flex;
1723
  flex-direction: column;
1724
- gap: 12px;
1725
- padding: 14px 18px 16px;
1726
  border: 1px solid color-mix(in srgb, var(--ta-page-color) 16%, var(--ta-line-soft));
1727
  border-radius: 16px;
1728
  background: linear-gradient(
1729
  160deg, color-mix(in srgb, var(--ta-page-color) 7%, transparent), color-mix(in srgb, var(--ta-ink) 2%, transparent) 70%
1730
  );
1731
  }
1732
- .ta-phero-eyebrow {
1733
- margin-bottom: -4px;
1734
- color: var(--ta-page-color);
1735
- font-size: 0.7rem;
1736
- font-weight: 700;
1737
- letter-spacing: 0.1em;
1738
- text-transform: uppercase;
1739
- }
1740
  @media (max-width: 560px) {
1741
- .ta-phero { padding: 12px 12px 14px; }
1742
  }
1743
  /* The block gap already parts the hero from the top bar and the card under it, and the panel's
1744
  edges line up with the cards below, so Gradio's padding around the block goes. */
@@ -1834,93 +1729,6 @@ button.ta-jump { background: none; color: inherit; font: inherit; font-size: 0.9
1834
  .ta-split > :focus-visible { outline: none; }
1835
  .ta-split:has(:focus-visible) { outline: 2px solid var(--ta-accent); outline-offset: 2px; }
1836
 
1837
- /* The selling points: one band of four, each a big value with a label and a line under it, and
1838
- each opening the details behind it. A numeric value counts up when the hero first shows: the
1839
- number is drawn from a CSS counter animated by `ta-count-up` (HEAD), while the text in the
1840
- page stays the real number for screen readers and copying. */
1841
- .ta-stats {
1842
- display: grid;
1843
- grid-template-columns: repeat(4, minmax(0, 1fr));
1844
- overflow: hidden;
1845
- border: 1px solid var(--ta-line-soft);
1846
- border-radius: 14px;
1847
- background:
1848
- linear-gradient(100deg, color-mix(in srgb, var(--ta-page-color) 10%, transparent), transparent 60%),
1849
- var(--ta-card);
1850
- }
1851
- .ta-stat {
1852
- display: flex;
1853
- align-items: center;
1854
- gap: 14px;
1855
- min-width: 0;
1856
- margin: 0;
1857
- padding: 12px 18px;
1858
- border: 0;
1859
- border-left: 1px solid var(--ta-line-soft);
1860
- border-radius: 0;
1861
- background: none;
1862
- color: inherit;
1863
- font: inherit;
1864
- text-align: left;
1865
- text-decoration: none;
1866
- cursor: pointer;
1867
- transition: background .14s ease;
1868
- }
1869
- .ta-stat:first-child { border-left: 0; }
1870
- .ta-stat:hover { background: var(--ta-chip); }
1871
- .ta-stat:focus-visible { outline: 2px solid var(--ta-accent); outline-offset: -2px; }
1872
- .ta-stat-num {
1873
- flex: 0 0 auto;
1874
- color: var(--ta-page-color);
1875
- font-size: 2.15rem;
1876
- font-weight: 800;
1877
- font-variant-numeric: tabular-nums;
1878
- letter-spacing: -0.03em;
1879
- line-height: 1;
1880
- }
1881
- .ta-stat-num[data-count] {
1882
- --ta-count: var(--ta-count-to);
1883
- counter-reset: ta-count var(--ta-count);
1884
- animation: ta-count-up 1.3s cubic-bezier(0.16, 1, 0.3, 1) both;
1885
- }
1886
- .ta-stat-num[data-count]::after {
1887
- content: counter(ta-count);
1888
- content: counter(ta-count) / "";
1889
- }
1890
- .ta-stat-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
1891
- .ta-stat-lbl { color: var(--ta-ink); font-size: 0.95rem; font-weight: 700; line-height: 1.25; }
1892
- .ta-stat-line { color: var(--ta-muted); font-size: 0.8rem; line-height: 1.35; }
1893
- .ta-stat[data-live] .ta-stat-line::before {
1894
- content: "";
1895
- display: inline-block;
1896
- width: 7px;
1897
- height: 7px;
1898
- margin-right: 6px;
1899
- border-radius: 50%;
1900
- background: var(--ta-live);
1901
- vertical-align: 1px;
1902
- animation: ta-live-ping 2.4s ease-out infinite;
1903
- }
1904
- .ta-sr-only {
1905
- position: absolute;
1906
- width: 1px;
1907
- height: 1px;
1908
- overflow: hidden;
1909
- clip-path: inset(50%);
1910
- white-space: nowrap;
1911
- }
1912
- @media (prefers-reduced-motion: reduce) {
1913
- .ta-stat-num[data-count], .ta-stat[data-live] .ta-stat-line::before { animation: none; }
1914
- }
1915
- @media (max-width: 1100px) {
1916
- .ta-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
1917
- .ta-stat:nth-child(3) { border-left: 0; }
1918
- .ta-stat:nth-child(n + 3) { border-top: 1px solid var(--ta-line-soft); }
1919
- }
1920
- @media (max-width: 560px) {
1921
- .ta-stat { flex-direction: column; align-items: flex-start; gap: 6px; padding: 12px 14px; }
1922
- .ta-stat-num { font-size: 1.85rem; }
1923
- }
1924
 
1925
  /* "Your Benchmark?" invite page: two "path" cards side by side. */
1926
  .ta-paths {
@@ -2114,11 +1922,9 @@ button.ta-jump { background: none; color: inherit; font: inherit; font-size: 0.9
2114
  into the page's top-right corner, above the bar's actions; the bar steps down below it. */
2115
  html:has(#huggingface-space-header) .ta-topbar { margin-top: 50px; }
2116
 
2117
- /* A little air above a section that a link jumped to. The figure panels are targets too (the
2118
- "Below:" row links to each one), and they are addressed by prefix because their ids carry the
2119
- subset they belong to. */
2120
- #ta-controls-anchor, #ta-overview-section, #lb-detailed, #ta-perdataset-section,
2121
- #beyond-controls-anchor, #beyond-lb-detailed, #beyond-perdataset-section,
2122
  [id^="ta-fig-"] { scroll-margin-top: 16px; }
2123
  /* Anchor navigation is the only jump that works inside the Space (a scripted scroll cannot move
2124
  a cross-origin parent), so the easing has to come from CSS rather than from `scrollIntoView`. */
@@ -2399,6 +2205,18 @@ html { scroll-behavior: smooth; }
2399
  }
2400
  """
2401
 
 
 
 
 
 
 
 
 
 
 
 
 
2402
  # --- The opt-in light theme -------------------------------------------------
2403
  # Everything `CSS` above draws in dark-theme colours, restated for a light page. Every selector
2404
  # is scoped to `html.ta-light`, which only the top bar's theme toggle sets (`taSetTheme` in
@@ -2439,7 +2257,6 @@ html.ta-light {
2439
  --ta-shadow: 0 1px 2px rgba(60, 50, 30, 0.08);
2440
  --ta-tabarena: #c2410c;
2441
  --ta-beyondarena: #0f766e;
2442
- --ta-live: #16a34a;
2443
  /* THEME points Gradio's light body pair at the dark values (see THEME below); the rest of
2444
  its light theme is white, restated here on the soft surfaces. */
2445
  --body-background-fill: var(--ta-page);
@@ -2513,17 +2330,6 @@ __TA_FAMILY_INK__
2513
  html.ta-light .ta-theme-toggle .ta-ico-sun { display: none; }
2514
  html.ta-light .ta-theme-toggle .ta-ico-moon { display: inline; }
2515
 
2516
- /* Section chips, sticky selection bar */
2517
- html.ta-light .ta-selbar {
2518
- border-color: var(--ta-line);
2519
- background: rgba(246, 245, 241, 0.96);
2520
- box-shadow: 0 6px 22px rgba(60, 50, 30, 0.2);
2521
- color: #1f2740;
2522
- }
2523
- html.ta-light .ta-selchip { background: var(--ta-chip); }
2524
- html.ta-light .ta-selbar-go { background: var(--ta-accent-tint); color: var(--ta-accent-ink); }
2525
- html.ta-light .ta-jumpmenu-list { box-shadow: 0 10px 30px rgba(60, 50, 30, 0.2); }
2526
-
2527
  /* Figure panels and the full-table card. The figures inside stay on their white paper. */
2528
  html.ta-light .ta-figpanel,
2529
  html.ta-light .ta-tile,
@@ -2592,15 +2398,6 @@ HEAD = """
2592
  the same side; the script below does the rest. */
2593
  :root { color-scheme: dark; }
2594
  html, body, gradio-app { background: #0f0f11; }
2595
- /* The hero's animations (see `.ta-stat-num` and `.ta-stat[data-live]` in `CSS`). They live here
2596
- rather than in `CSS`, whose rescoping is written for rules with selectors, not at-rules.
2597
- `--ta-count` is registered as an integer so that it animates rather than flips. */
2598
- @property --ta-count { syntax: "<integer>"; inherits: false; initial-value: 0; }
2599
- @keyframes ta-count-up { from { --ta-count: 0; } }
2600
- @keyframes ta-live-ping {
2601
- 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ta-live) 55%, transparent); }
2602
- 70%, 100% { box-shadow: 0 0 0 6px transparent; }
2603
- }
2604
  /* The selector chip a shortcut picked (`.ta-sel .ta-chip.ta-flash` in `CSS`). */
2605
  @keyframes ta-sel-flash {
2606
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
@@ -2830,8 +2627,6 @@ window.taExpandTile = function (uid) {
2830
  tile.before(backdrop);
2831
  tile.classList.add("ta-tile-max");
2832
  tile.querySelector(".ta-expand").setAttribute("aria-pressed", "true");
2833
- // The bottom pill would sit on top of the backdrop.
2834
- document.documentElement.classList.add("ta-tile-open");
2835
  taSetBand(taBand);
2836
  };
2837
  function taCollapseTile(tile) {
@@ -2839,7 +2634,6 @@ function taCollapseTile(tile) {
2839
  const btn = tile.querySelector(".ta-expand");
2840
  if (btn) btn.setAttribute("aria-pressed", "false");
2841
  document.querySelectorAll(".ta-tile-backdrop").forEach((backdrop) => backdrop.remove());
2842
- document.documentElement.classList.remove("ta-tile-open");
2843
  }
2844
  document.addEventListener("keydown", (ev) => {
2845
  if (ev.key === "Escape") document.querySelectorAll(".ta-tile-max").forEach(taCollapseTile);
@@ -3015,7 +2809,11 @@ window.taGoToPage = function (link) {
3015
  const target = taTabButton(link.dataset.tab);
3016
  if (target) target.click();
3017
  const slug = link.dataset.slug;
3018
- const query = slug === window.taPageSlugs[0] ? "" : "?tab=" + slug;
 
 
 
 
3019
  try { history.replaceState(null, "", location.pathname + query + location.hash); } catch (err) {}
3020
  if (window.parent !== window) {
3021
  try { window.parent.postMessage({ queryString: query }, "https://huggingface.co"); } catch (err) {}
@@ -3419,15 +3217,69 @@ window.taSelector = function (root, send) {
3419
  });
3420
  paint();
3421
  };
3422
- /* A link to a selector card (the bottom bar's "change", the Jump to menu) opens the card, in the
3423
- capture phase so that it is open before `taJumpToAnchor` measures where it is. */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3424
  document.addEventListener("click", (event) => {
3425
  const path = typeof event.composedPath === "function" ? event.composedPath() : [event.target];
3426
- const link = path.find((node) => node && node.matches && node.matches('a[href^="#"]'));
3427
- const block = link && document.getElementById(link.getAttribute("href").slice(1));
3428
- const card = block && block.querySelector(".ta-sel");
3429
- if (card && card._taSetOpen) card._taSetOpen(true);
3430
  }, true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3431
  /* Copy a block's text, as the citation dialog's button does. Where the clipboard is not offered
3432
  (some embeds refuse it), the text is selected instead so the reader can copy it by hand. */
3433
  window.taCopy = function (button, id) {
@@ -3461,8 +3313,8 @@ window.taCloseDialogs = function () {
3461
  document.addEventListener("click", (ev) => {
3462
  if (ev.target.classList && ev.target.classList.contains("ta-dialog-overlay")) window.taCloseDialogs();
3463
  });
3464
- function taCloseJumpMenus(except) {
3465
- const open = "details.ta-jumpmenu[open], details.ta-dl[open], details.ta-follow-pick[open]";
3466
  document.querySelectorAll(open).forEach((menu) => {
3467
  if (menu !== except) menu.open = false;
3468
  });
@@ -3470,34 +3322,25 @@ function taCloseJumpMenus(except) {
3470
  document.addEventListener("keydown", (ev) => {
3471
  if (ev.key !== "Escape") return;
3472
  window.taCloseDialogs();
3473
- taCloseJumpMenus(null);
3474
  });
3475
- /* A pick closes the menu (the bottom bar's, the overview's metric menu, a panel's downloads), and
3476
- so does a click anywhere else. Clicks on its own summary are left to the browser, which opens and closes it.
3477
- The Follow picker stays open through its picks, since it takes several. */
3478
  document.addEventListener("click", (ev) => {
3479
- const menus = "details.ta-jumpmenu, details.ta-dl, details.ta-follow-pick";
3480
  const menu = ev.target.closest && ev.target.closest(menus);
3481
- taCloseJumpMenus(menu);
3482
- if (menu && ev.target.closest(".ta-jumpmenu-list a, .ta-jumpmenu-list button, .ta-dl-menu button")) menu.open = false;
3483
  });
3484
 
3485
- /* Reveal the selection bar once the control card has scrolled out of view, and hide it again
3486
- while an open dataset browser fills the view (taOverDataBrowser): that browser has its own
3487
- contender, sort and filter controls, so a second floating bar of selectors over it competes for
3488
- the same job.
3489
-
3490
- IntersectionObservers rather than a scroll handler: no work on frames where nothing crossed a
3491
- boundary. Re-attached after Gradio re-renders, since the elements are replaced. */
3492
- let taControlsSeen = null;
3493
-
3494
  /* The visible band, written onto the elements pinned to it rather than onto :root. A custom
3495
  property set on the root is inherited by every element on the page, so each scroll report
3496
  restyled all six thousand of them (~15 ms) while the reader was scrolling, in the frame whose
3497
  main thread also has to paint what scrolls into view; on these few it restyles only them.
3498
  `taBand` keeps the numbers for the scripts below; null while our viewport is the screen,
3499
  where the stylesheet's own defaults apply. */
3500
- const TA_BAND_READERS = ".ta-selbar, .ta-dialog-overlay, .ta-tile-max, .ta-tile-backdrop, #ta-debug-readout";
3501
  let taBand = null;
3502
  function taSetBand(band) {
3503
  taBand = band;
@@ -3513,46 +3356,12 @@ function taSetBand(band) {
3513
  taLoadFramesInBand();
3514
  }
3515
 
3516
- /* Is an open dataset browser across the middle of the reader's view? It has its own contender,
3517
- filter and sort controls, so the selection bar would compete with it there. Asked of the visible
3518
- band rather than of an IntersectionObserver, because the two environments move different
3519
- things: top-level the page scrolls, so the frame's own top moves and --ta-vp-top stays 0;
3520
- embedded, the Space's frame is as tall as its content and never scrolls, so the browser's top is
3521
- a fixed document position and the band moves instead. Comparing the two covers both. */
3522
- function taOverDataBrowser() {
3523
- const frame = document.querySelector("iframe.ta-explorer[title^='Per-dataset'][src]");
3524
- if (!frame) return false;
3525
- const bandTop = taBand ? taBand.top : 0;
3526
- const bandH = taBand ? taBand.h : window.innerHeight;
3527
- const box = frame.getBoundingClientRect();
3528
- const middle = bandTop + bandH * 0.5;
3529
- return box.top <= middle && box.bottom >= middle;
3530
- }
3531
-
3532
- function taSyncSelbar() {
3533
- const bar = document.querySelector(".ta-selbar");
3534
- if (bar) bar.classList.toggle("ta-show", taControlsSeen === false && !taOverDataBrowser());
3535
- }
3536
- function taWatchControls() {
3537
- const anchor = document.getElementById("ta-controls-anchor");
3538
- const bar = document.querySelector(".ta-selbar");
3539
- if (!anchor || !bar) return;
3540
- taSyncSelbar();
3541
- if (anchor.dataset.taWatched) return;
3542
- anchor.dataset.taWatched = "1";
3543
- new IntersectionObserver((entries) => {
3544
- taControlsSeen = entries[0].isIntersecting;
3545
- taSyncSelbar();
3546
- }, { rootMargin: "-40px 0px 0px 0px" }).observe(anchor);
3547
- }
3548
-
3549
  /* --- Keep --ta-vp-top / --ta-vp-h on the visible band ----------------------
3550
  Only does anything when this page's viewport is not the screen. Embedded in a Hugging Face
3551
  Space it is not: HF sizes the Space iframe to the app's whole content, so the iframe never
3552
  scrolls, the outer page does, and our viewport is the entire document. Everything pinned to a
3553
- screen edge then pins to the top of a six-thousand-pixel page instead: the selection bar
3554
- waits at the very bottom, and opening a dialog blacks out the whole page with its content
3555
- parked far above the screen.
3556
 
3557
  The parent is cross-origin, so its scroll position is unreadable. IntersectionObserver's
3558
  implicit root is the top-level viewport though, and it reports the intersection in *our*
@@ -3637,7 +3446,6 @@ function taOnPageInfo(info) {
3637
  taSetBand(null);
3638
  } else {
3639
  taSetBand({ top: Math.round(top), h: Math.round(bottom - top) });
3640
- taSyncSelbar(); // the band moved, so what the reader has reached changed with it
3641
  }
3642
  taReportViewport();
3643
  }
@@ -3752,7 +3560,6 @@ function taTrackViewport() {
3752
  // then there are two scrollports rather than none. Both cases come out right here,
3753
  // because a page that cannot scroll has `scrollY === 0` anyway.
3754
  taSetBand({ top: Math.round(top), h: Math.round(bottom - top) });
3755
- taSyncSelbar();
3756
  taReportViewport();
3757
  }, { threshold: TA_STEPS });
3758
 
@@ -3773,7 +3580,6 @@ new MutationObserver(() => {
3773
  // Coalesce a burst of mutations into one pass; the guards above make it idempotent.
3774
  clearTimeout(window._taStampTimer);
3775
  window._taStampTimer = setTimeout(() => {
3776
- taWatchControls();
3777
  taTrackViewport();
3778
  taSetBand(taBand);
3779
  // A re-render brings fresh frames to load, a page switch a new page.
@@ -3786,10 +3592,7 @@ new MutationObserver(() => {
3786
  taFitTopBar();
3787
  }, 60);
3788
  }).observe(document.body, { childList: true, subtree: true });
3789
- // Top-level there is no band to track, so the section's own position is what moves.
3790
- window.addEventListener("scroll", taSyncSelbar, { passive: true });
3791
  document.addEventListener("DOMContentLoaded", () => {
3792
- taWatchControls();
3793
  taTrackViewport();
3794
  taReportViewport();
3795
  taSyncThemeChip();
@@ -3868,7 +3671,7 @@ window.taSendExclude = function (frame) {
3868
  /* How tall the reader's visible band is, for the per-dataset browser's "fit to screen" button.
3869
  The frame cannot work this out for itself: it is sized to its own content, so its
3870
  `innerHeight` is the length of its page rather than of the screen. We already track the band
3871
- for the selection bar and the dialogs (`taBand`), so send that. */
3872
  window.taSendPerDatasetViewport = function (frame) {
3873
  if (frame.dataset.task === undefined) return;
3874
  const height = taBand && taBand.h > 200 ? taBand.h : window.innerHeight;
@@ -3878,22 +3681,6 @@ window.taSendPerDatasetViewport = function (frame) {
3878
  } catch (err) { /* not ready; it will ask again with its next height report */ }
3879
  };
3880
 
3881
- /* That button is HTML inside the panel's title bar, so it cannot be a Gradio component itself;
3882
- it forwards the click to the hidden one that actually reaches the server. */
3883
- /* The dataset browser's bar opens and closes it from anywhere on it, not only from its button; the
3884
- download menu inside it keeps its own clicks. */
3885
- window.taPdBarClick = function (ev, uid) {
3886
- if (ev.target.closest(".ta-dl")) return;
3887
- window.taTogglePerDataset(uid);
3888
- };
3889
- window.taTogglePerDataset = function (uid) {
3890
- // Gradio puts `elem_id` on the <button> itself for gr.Button, and on a wrapper for most
3891
- // other components; accept either so this does not depend on which.
3892
- const node = document.getElementById(uid + "-toggle");
3893
- const real = node && (node.tagName === "BUTTON" ? node : node.querySelector("button"));
3894
- if (real) real.click();
3895
- };
3896
-
3897
  /* --- Leaderboard table sorting ---------------------------------------------
3898
  Click a header to sort the rows beneath it; click again to reverse. Numeric
3899
  columns are marked `data-type="num"` and every cell carries the raw value in
 
4
 
5
  import json
6
 
7
+ import website_texts
8
  from api import register_api
9
  from data_loading import BEYOND_DATA_DIR, DATA_DIR
10
  from pages import PAGES, render_page
 
14
  CSS = """
15
  /* Use the full page width instead of gradio's narrow centered column. */
16
  .gradio-container { max-width: 100% !important; }
 
 
 
 
17
  .gradio-container { padding-top: 6px !important; }
18
 
19
  /* --- Colour emoji ----------------------------------------------------------
 
115
  then one line per choice with the captions in one column down the left, and beside the lines a
116
  column that says what the selection covers, its number set like the hero's. A chip in a row that
117
  filters the datasets carries the number of datasets the leaderboard would cover with it picked.
118
+ The card starts closed, as its bar alone, one row: the title is the button, small shortcuts into
119
+ the card follow it, and the number of datasets the selection covers ends it. Opened, the lines
120
+ grow under the bar, headed by what the choices change. */
121
  .ta-sel-block .html-container { padding: 0; }
122
  .ta-sel {
123
  --ta-page-color: var(--ta-accent);
124
  --ta-pd-color: var(--ta-page-color);
125
  display: grid;
126
+ grid-template-columns: minmax(0, 1fr) auto;
127
+ grid-template-areas: "bar result" "main main" "warns warns";
 
128
  overflow: hidden;
129
  border: 1px solid var(--ta-line-soft);
130
  border-radius: 14px;
 
136
  selling points, a second orange band read as more of the same. The icon, the chevron and the
137
  shortcuts carry the colour instead. */
138
  .ta-sel-bar { grid-area: bar; }
139
+ .ta-sel .ta-sel-bar { gap: 12px !important; padding: 7px 14px !important; background: transparent; }
140
  .ta-sel .ta-sel-bar:hover { background: color-mix(in srgb, var(--ta-ink) 4%, transparent); }
141
+ .ta-sel:not([data-open="true"]) > :is(.ta-sel-main, .ta-sel-warns) { display: none; }
 
142
  .ta-sel-main { grid-area: main; min-width: 0; padding: 0 18px 2px; }
143
+ .ta-sel-lead { margin: 0; padding: 10px 0 2px; color: var(--ta-muted); font-size: 0.84rem; }
144
+ .ta-sel-main > .ta-sel-lead + .ta-sel-line { border-top: 0; }
145
+ .ta-sel .ta-pdicon { width: 28px; height: 28px; border-radius: 8px; }
146
+ .ta-sel .ta-pdicon svg { width: 15px; height: 15px; }
147
+ .ta-sel .ta-sel-title { flex: none; margin: 0; font-size: 1rem; letter-spacing: -0.01em; white-space: nowrap; }
148
  /* The title opens the card, its chevron filled in the page's colour while there is more to see,
149
  quiet and turned once the card is open. */
150
  .ta-sel-toggle {
151
  display: inline-flex;
152
  align-items: center;
153
+ gap: 8px;
154
  margin: 0;
155
  padding: 0;
156
  border: 0;
 
164
  display: grid;
165
  place-items: center;
166
  flex: none;
167
+ width: 20px;
168
+ height: 20px;
169
  border: 1px solid transparent;
170
  border-radius: 50%;
171
  background: var(--ta-page-color);
 
174
  }
175
  .ta-sel-chev::before {
176
  content: "";
177
+ width: 6px;
178
+ height: 6px;
179
  border-style: solid;
180
  border-color: var(--ta-on-accent);
181
  border-width: 0 2px 2px 0;
 
189
  transform: rotate(180deg);
190
  }
191
  .ta-sel[data-open="true"] .ta-sel-chev::before { border-color: var(--ta-ink); }
192
+ /* The shortcuts: small and dashed, as suggestions rather than settings, each a "+" until it is
193
+ picked. They keep to the bar's row, which scrolls sideways where it cannot hold them, fading out
194
+ at the edge it continues past. */
195
+ .ta-sel-try {
196
+ display: flex;
197
+ flex: 1 1 auto;
198
+ align-items: center;
199
+ gap: 5px;
200
+ min-width: 0;
201
+ overflow-x: auto;
202
+ scrollbar-width: none;
203
+ }
204
+ .ta-sel-try::-webkit-scrollbar { display: none; }
205
  .ta-sel-ex {
206
+ flex: none;
207
  margin: 0;
208
+ padding: 2px 9px 2px 7px;
209
  border: 1px dashed color-mix(in srgb, var(--ta-page-color) 60%, transparent);
210
  border-radius: 999px;
211
  background: transparent;
212
  color: var(--ta-ink);
213
  font: inherit;
214
+ font-size: 0.74rem;
215
  font-weight: 600;
216
  white-space: nowrap;
217
  cursor: pointer;
218
  transition: background .14s ease, border-color .14s ease;
219
  }
220
+ .ta-sel-ex::before { content: "+"; margin-right: 4px; color: var(--ta-page-color); font-weight: 800; }
221
  .ta-sel-ex:hover,
222
  .ta-sel-ex[aria-pressed="true"] {
223
  border-style: solid;
 
226
  .ta-sel-ex[aria-pressed="true"]::before { content: "\\2713"; }
227
  .ta-sel-ex:focus-visible { outline: 2px solid var(--ta-page-color); outline-offset: 2px; }
228
  .ta-sel-ex:disabled { opacity: 0.4; cursor: not-allowed; }
229
+ @media (max-width: 1100px) {
230
+ .ta-sel-try { mask-image: linear-gradient(90deg, #000 85%, transparent); }
231
+ }
232
+ /* On a phone the shortcuts take a line of their own under the title. */
233
  @media (max-width: 640px) {
234
+ .ta-sel .ta-sel-try { flex-basis: 100%; }
 
 
 
 
 
 
 
 
235
  }
236
  /* The chip a shortcut picked pulses as the card opens, so the reader sees where it lives. */
237
  .ta-sel .ta-chip.ta-flash { animation: ta-sel-flash 0.9s ease-out 2; }
 
392
  .ta-sel .ta-chip[role="switch"][aria-checked="true"] .ta-chip-mark { background: var(--ta-page-color); }
393
  .ta-sel .ta-chip[role="switch"][aria-checked="true"] .ta-chip-mark::after { transform: translateX(12px); }
394
 
395
+ /* The page's views (views.view_bar): tabs at the top of its controls, quiet labels with the shown one
396
+ in ink over a rail in the page's colour, like the top bar's page links, each saying on hover what
397
+ its view shows; Follow at the right. The selector card hangs under the bar, joined to it as one
398
+ card, so its choices read as applying to every view. On a phone the tabs scroll sideways in one
399
+ row and Follow takes a line of its own. */
400
+ .ta-viewbar-block .html-container { padding: 0 !important; }
401
+ .ta-viewbar {
402
+ --ta-page-color: var(--ta-tabarena);
403
+ display: flex;
404
+ flex-wrap: wrap;
405
+ align-items: center;
406
+ gap: 0 16px;
407
+ padding: 0 12px 0 10px;
408
+ border: 1px solid var(--ta-line-soft);
409
+ border-bottom: 0;
410
+ border-radius: 14px 14px 0 0;
411
+ background: var(--ta-toolbar);
412
+ }
413
+ .ta-viewbar[data-page="beyondarena"] { --ta-page-color: var(--ta-beyondarena); }
414
+ /* Pulled up over the gap between blocks so the card's top border lies on the bar's foot. Gradio puts
415
+ the class on the block and on its inner element too, so only the block (`.block`) moves. */
416
+ .ta-page:has(.ta-viewbar) .block.ta-sel-block { margin-top: -16px !important; }
417
+ /* The tabs and the card share a surface a shade apart from the view under them, as a toolbar over its
418
+ page, with a firmer line where the view starts. */
419
+ .ta-page:has(.ta-viewbar) .ta-sel { border-radius: 0; border-bottom-color: var(--ta-line); background: var(--ta-toolbar); }
420
+ /* The shown view starts on the card's foot: its first card, or the first row of a figure grid, takes
421
+ the card's bottom border for its top edge, with square corners where the two meet. Which tiles make
422
+ that row follows "I care about" (the order rules at `.ta-figgrid`). */
423
+ .ta-page:has(.ta-viewbar) .ta-view { margin-top: -16px !important; }
424
+ .ta-view .ta-figpanel { margin-top: 0; }
425
+ .ta-view :is(.ta-xo-scroll, .ta-figpanel, .ta-view-card:not(.ta-view-card ~ .ta-view-card)),
426
+ .ta-view .ta-figgrid > .ta-tile:first-child,
427
+ .ta-page[data-care="total"] .ta-view .ta-tile[data-key="total"],
428
+ .ta-page[data-care="train"] .ta-view .ta-tile[data-key="train"] {
429
+ border-top: 0 !important;
430
+ border-top-left-radius: 0 !important;
431
+ border-top-right-radius: 0 !important;
432
+ }
433
+ @media (min-width: 1101px) {
434
+ .ta-view .ta-figgrid > .ta-tile:not(.ta-tile-wide):first-child + .ta-tile:not(.ta-tile-wide) {
435
+ border-top: 0;
436
+ border-top-left-radius: 0;
437
+ border-top-right-radius: 0;
438
+ }
439
+ .ta-page[data-care="train"] .ta-view .ta-figgrid:has(> [data-key="train"]) > .ta-tile[data-key="total"] {
440
+ border-top: 1px solid var(--ta-line-soft);
441
+ border-top-left-radius: 14px;
442
+ border-top-right-radius: 14px;
443
+ }
444
+ }
445
+ /* One figure per row: only the one put first joins. */
446
+ @media (max-width: 1100px) {
447
+ .ta-page[data-care="total"] .ta-view .ta-figgrid:has(> [data-key="total"]) > .ta-tile:first-child:not([data-key="total"]),
448
+ .ta-page[data-care="train"] .ta-view .ta-figgrid:has(> [data-key="train"]) > .ta-tile:first-child:not([data-key="train"]) {
449
+ border-top: 1px solid var(--ta-line-soft) !important;
450
+ border-top-left-radius: 14px !important;
451
+ border-top-right-radius: 14px !important;
452
+ }
453
+ }
454
+ .ta-viewtabs { display: flex; flex: none; gap: 2px; max-width: 100%; overflow-x: auto; scrollbar-width: none; }
455
+ .ta-viewtabs::-webkit-scrollbar { display: none; }
456
+ .ta-viewtab {
457
+ flex: none;
458
+ margin: 0;
459
+ padding: 12px 14px 10px;
460
+ border: 0;
461
+ border-bottom: 2px solid transparent;
462
+ background: none;
463
+ color: var(--ta-muted);
464
+ font: inherit;
465
+ font-size: 0.95rem;
466
+ font-weight: 600;
467
+ white-space: nowrap;
468
+ cursor: pointer;
469
+ transition: color .14s ease, border-color .14s ease;
470
+ }
471
+ .ta-viewtab:hover { color: var(--ta-ink); }
472
+ .ta-viewtab[aria-selected="true"] { border-bottom-color: var(--ta-page-color); color: var(--ta-ink); }
473
+ .ta-viewtab:focus-visible { outline: 2px solid var(--ta-page-color); outline-offset: -2px; border-radius: 6px; }
474
+ /* What follows the tabs: a warning about the selection, or a note of the shown view. */
475
+ .ta-viewnotes { flex: 1 1 auto; min-width: 0; margin: 0; padding: 8px 0; color: var(--ta-muted); font-size: 0.84rem; line-height: 1.4; }
476
+ .ta-viewnotes:empty { display: none; }
477
+ .ta-viewnote-link {
478
+ margin: 0 0 0 6px;
479
+ padding: 0;
480
+ border: 0;
481
+ background: none;
482
+ color: var(--ta-ink);
483
+ font: inherit;
484
+ font-weight: 600;
485
+ text-decoration: underline dotted;
486
+ text-underline-offset: 3px;
487
+ cursor: pointer;
488
+ }
489
+ .ta-viewnotes .ta-head-warn { margin-left: 8px; }
490
+ .ta-viewbar .ta-follow { flex: none; margin: 0 0 0 auto; }
491
+ @media (max-width: 640px) {
492
+ .ta-viewbar { padding: 0 8px 8px; }
493
+ .ta-viewnotes { flex-basis: 100%; padding: 2px 4px 6px; }
494
+ }
495
+ /* One view shows at a time: the one the page names (`data-view`, main.taShowView), the first until
496
+ it names one. */
497
+ __TA_VIEW_RULES__
498
+
499
  /* What the selection covers: the number of datasets, beside the bar and the lines. */
500
  .ta-sel-result {
501
  grid-area: result;
502
  display: flex;
503
+ align-items: center;
 
504
  min-width: 0;
505
+ padding: 6px 16px;
506
  border-left: 1px solid var(--ta-line-soft);
507
+ background: linear-gradient(260deg, color-mix(in srgb, var(--ta-page-color) 13%, transparent), transparent 90%);
508
  }
509
+ .ta-sel[data-open="true"] .ta-sel-result { border-bottom: 1px solid var(--ta-line-soft); }
510
  .ta-sel-total {
511
  display: flex;
 
512
  align-items: baseline;
513
+ gap: 6px;
514
  margin: 0;
515
  color: var(--ta-ink);
516
+ font-size: 0.84rem;
517
  font-weight: 700;
518
+ white-space: nowrap;
519
  }
520
  .ta-sel-unit { white-space: nowrap; }
521
  .ta-sel-num {
522
  color: var(--ta-page-color);
523
+ font-size: 1.2rem;
524
  font-weight: 800;
525
  font-variant-numeric: tabular-nums;
526
  letter-spacing: -0.03em;
 
560
  .ta-sel-warn-body ul { padding-left: 1.2em; }
561
  .ta-sel-warn-body > :last-child { margin-bottom: 0; }
562
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
563
  /* Where the line cannot hold both rows, the second takes a line of its own, caption in the column. */
564
  @media (max-width: 1180px) {
565
  .ta-sel-line { flex-direction: column; }
 
613
  --ta-card: rgba(255, 255, 255, 0.04);
614
  --ta-strip: rgba(255, 255, 255, 0.06);
615
  --ta-raised: #1b1b22;
616
+ /* The controls over a page's views (the tabs and the selector card). */
617
+ --ta-toolbar: color-mix(in srgb, var(--ta-ink) 7%, var(--ta-page));
618
  --ta-line: rgba(255, 255, 255, 0.2);
619
  --ta-line-soft: rgba(255, 255, 255, 0.1);
620
  --ta-ink: #f4f4f5;
 
632
  orange of its old tab, BeyondArena the teal of its hero. */
633
  --ta-tabarena: #fb923c;
634
  --ta-beyondarena: #2dd4bf;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635
  }
636
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637
 
638
 
639
  /* The models-vs-systems warning. It appears the moment a system joins the field, and it is the
 
870
  .ta-expand { padding: 4px 10px !important; font-size: 0.95rem !important; line-height: 1; }
871
  /* The full table's card (views.make_leaderboard) takes the tile look, below the grid. */
872
  .ta-lbtile { margin: 0 0 18px 0; }
873
+ /* The selector card's bar (views.selector_card), washed in the page's colour (`--ta-pd-color`). */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
874
  .ta-pdbar {
875
  cursor: pointer;
876
  flex-wrap: nowrap !important;
 
897
  color: var(--ta-pd-color);
898
  background: color-mix(in srgb, var(--ta-pd-color) 20%, transparent);
899
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
900
  @media (max-width: 640px) {
901
  .ta-pdbar { flex-wrap: wrap !important; }
 
 
 
902
  }
 
903
 
904
  /* --- Follow a method (views.follow_picker, the ◎ on each overview row, main.taToggleFollow) ----- */
905
  .ta-follow { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin: 2px 0 4px 0; }
 
1076
  .ta-xo-scroll {
1077
  --ta-xo-bg: color-mix(in srgb, #ffffff 4%, var(--ta-page));
1078
  overflow: auto;
 
1079
  border: 1px solid var(--ta-line-soft);
1080
  border-radius: 12px;
1081
  background: var(--ta-xo-bg);
 
1102
  top: 0;
1103
  z-index: 3;
1104
  height: 32px;
1105
+ background: var(--ta-xo-bg);
1106
  color: var(--ta-ink);
1107
  font-size: 0.8rem;
1108
  font-weight: 700;
1109
  }
1110
  .ta-xo thead tr:nth-child(2) th { top: 32px; }
1111
+ /* The heading is on the table's own surface; a firmer line under it starts the rows. */
1112
+ .ta-xo thead :is(tr:last-child th, th[rowspan]) { border-bottom-color: var(--ta-line); }
1113
  .ta-xo .ta-group-h {
1114
  border-bottom-color: var(--ta-line);
1115
  color: var(--ta-muted);
 
1252
  background: var(--ta-xo-bg);
1253
  text-align: left;
1254
  }
1255
+ .ta-xo thead .ta-xo-model { z-index: 4; background: var(--ta-xo-bg); }
1256
  .ta-xo tbody tr:hover .ta-xo-model { background: color-mix(in srgb, var(--ta-ink) 5%, var(--ta-xo-bg)); }
1257
  .ta-xo-id { display: inline-flex; align-items: center; gap: 8px; }
1258
  /* A logo fills its tile at its own shape and colours; the few whose artwork would vanish on a dark
 
1616
  .ta-ci-head { opacity: 0.55; font-weight: 400; font-size: 0.82em; }
1617
 
1618
  /* --- A benchmark page's hero (views._page_hero) ---------------------------------------------
1619
+ One sentence on what the benchmark is with its links beside it, then the Learn more row, whose
1620
+ pills quote the benchmark's numbers from the data. Its colour is the page's own, as on the
1621
+ page's link in the top bar. The hero is the page's background, so it sits apart from the
1622
+ results under it: one panel washed faintly in the page's colour. */
 
1623
  .ta-phero {
1624
  --ta-page-color: var(--ta-accent);
1625
  display: flex;
1626
  flex-direction: column;
1627
+ gap: 10px;
1628
+ padding: 12px 18px;
1629
  border: 1px solid color-mix(in srgb, var(--ta-page-color) 16%, var(--ta-line-soft));
1630
  border-radius: 16px;
1631
  background: linear-gradient(
1632
  160deg, color-mix(in srgb, var(--ta-page-color) 7%, transparent), color-mix(in srgb, var(--ta-ink) 2%, transparent) 70%
1633
  );
1634
  }
 
 
 
 
 
 
 
 
1635
  @media (max-width: 560px) {
1636
+ .ta-phero { padding: 12px; }
1637
  }
1638
  /* The block gap already parts the hero from the top bar and the card under it, and the panel's
1639
  edges line up with the cards below, so Gradio's padding around the block goes. */
 
1729
  .ta-split > :focus-visible { outline: none; }
1730
  .ta-split:has(:focus-visible) { outline: 2px solid var(--ta-accent); outline-offset: 2px; }
1731
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1732
 
1733
  /* "Your Benchmark?" invite page: two "path" cards side by side. */
1734
  .ta-paths {
 
1922
  into the page's top-right corner, above the bar's actions; the bar steps down below it. */
1923
  html:has(#huggingface-space-header) .ta-topbar { margin-top: 50px; }
1924
 
1925
+ /* A little air above a section that a link jumped to: the view bars, the selector cards, and the
1926
+ figure panels, addressed by prefix because their ids carry the subset they belong to. */
1927
+ #ta-views-anchor, #beyond-views-anchor, #ta-controls-anchor, #beyond-controls-anchor,
 
 
1928
  [id^="ta-fig-"] { scroll-margin-top: 16px; }
1929
  /* Anchor navigation is the only jump that works inside the Space (a scripted scroll cannot move
1930
  a cross-origin parent), so the easing has to come from CSS rather than from `scrollIntoView`. */
 
2205
  }
2206
  """
2207
 
2208
+
2209
+ def _view_rules(keys: list[str]) -> str:
2210
+ """The rules that show one view and its notes: the one a page names in ``data-view``, the
2211
+ first until it names one."""
2212
+ pages = [(".ta-page:not([data-view])", keys[0])] + [(f'.ta-page[data-view="{key}"]', key) for key in keys]
2213
+ views = ",\n".join(f"{page} .ta-view:not(.ta-view-{key})" for page, key in pages)
2214
+ notes = ",\n".join(f'{page} .ta-viewnote:not([data-view="{key}"])' for page, key in pages)
2215
+ return f"{views} {{ display: none !important; }}\n{notes} {{ display: none; }}"
2216
+
2217
+
2218
+ CSS = CSS.replace("__TA_VIEW_RULES__", _view_rules([key for key, _label in website_texts.VIEW_TABS]))
2219
+
2220
  # --- The opt-in light theme -------------------------------------------------
2221
  # Everything `CSS` above draws in dark-theme colours, restated for a light page. Every selector
2222
  # is scoped to `html.ta-light`, which only the top bar's theme toggle sets (`taSetTheme` in
 
2257
  --ta-shadow: 0 1px 2px rgba(60, 50, 30, 0.08);
2258
  --ta-tabarena: #c2410c;
2259
  --ta-beyondarena: #0f766e;
 
2260
  /* THEME points Gradio's light body pair at the dark values (see THEME below); the rest of
2261
  its light theme is white, restated here on the soft surfaces. */
2262
  --body-background-fill: var(--ta-page);
 
2330
  html.ta-light .ta-theme-toggle .ta-ico-sun { display: none; }
2331
  html.ta-light .ta-theme-toggle .ta-ico-moon { display: inline; }
2332
 
 
 
 
 
 
 
 
 
 
 
 
2333
  /* Figure panels and the full-table card. The figures inside stay on their white paper. */
2334
  html.ta-light .ta-figpanel,
2335
  html.ta-light .ta-tile,
 
2398
  the same side; the script below does the rest. */
2399
  :root { color-scheme: dark; }
2400
  html, body, gradio-app { background: #0f0f11; }
 
 
 
 
 
 
 
 
 
2401
  /* The selector chip a shortcut picked (`.ta-sel .ta-chip.ta-flash` in `CSS`). */
2402
  @keyframes ta-sel-flash {
2403
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
 
2627
  tile.before(backdrop);
2628
  tile.classList.add("ta-tile-max");
2629
  tile.querySelector(".ta-expand").setAttribute("aria-pressed", "true");
 
 
2630
  taSetBand(taBand);
2631
  };
2632
  function taCollapseTile(tile) {
 
2634
  const btn = tile.querySelector(".ta-expand");
2635
  if (btn) btn.setAttribute("aria-pressed", "false");
2636
  document.querySelectorAll(".ta-tile-backdrop").forEach((backdrop) => backdrop.remove());
 
2637
  }
2638
  document.addEventListener("keydown", (ev) => {
2639
  if (ev.key === "Escape") document.querySelectorAll(".ta-tile-max").forEach(taCollapseTile);
 
2809
  const target = taTabButton(link.dataset.tab);
2810
  if (target) target.click();
2811
  const slug = link.dataset.slug;
2812
+ const params = new URLSearchParams(slug === window.taPageSlugs[0] ? "" : "tab=" + slug);
2813
+ const tabs = document.querySelector(`.ta-viewbar[data-page="${slug}"]`);
2814
+ const view = tabs && tabs.closest(".ta-page") && tabs.closest(".ta-page").dataset.view;
2815
+ if (view && view !== taFirstView()) params.set("view", view);
2816
+ const query = params.toString() ? "?" + params.toString() : "";
2817
  try { history.replaceState(null, "", location.pathname + query + location.hash); } catch (err) {}
2818
  if (window.parent !== window) {
2819
  try { window.parent.postMessage({ queryString: query }, "https://huggingface.co"); } catch (err) {}
 
3217
  });
3218
  paint();
3219
  };
3220
+ /* --- A page's views (views.view_bar) ----------------------------------------------------------
3221
+ Every view is on the page from the start; the page shows the one its `data-view` names (the
3222
+ stylesheet's view rules, the first until it names one). A tab switches it in the browser: the tabs mark the shown one, its explorers load (they wait until
3223
+ they are seen, `taLoadFrames`), tile captions are measured again now that they have a width
3224
+ (`taFitCaptions`), and the view goes into the address as `?view=<key>` (dropped for the first),
3225
+ inside the Space into the Hugging Face page's URL too, so the address opens it again. */
3226
+ function taFirstView() {
3227
+ const tab = document.querySelector(".ta-viewtab");
3228
+ return tab ? tab.dataset.view : "";
3229
+ }
3230
+ /* The tab bar is drawn again with every new selection (for its Follow list), so it marks the view
3231
+ the page already shows. */
3232
+ window.taSyncViewTabs = function (root) {
3233
+ const page = root.closest(".ta-page");
3234
+ const view = (page && page.dataset.view) || taFirstView();
3235
+ root.querySelectorAll(".ta-viewtab").forEach((tab) => {
3236
+ tab.setAttribute("aria-selected", String(tab.dataset.view === view));
3237
+ });
3238
+ if (page) taSyncFollow(page);
3239
+ };
3240
+ window.taShowView = function (page, view, record = true) {
3241
+ if (!page || !view) return;
3242
+ page.dataset.view = view;
3243
+ page.querySelectorAll(".ta-viewtab").forEach((tab) => {
3244
+ tab.setAttribute("aria-selected", String(tab.dataset.view === view));
3245
+ });
3246
+ window.taLoadFrames();
3247
+ taFitCaptions();
3248
+ if (record) taSetViewParam(view);
3249
+ };
3250
+ function taSetViewParam(view) {
3251
+ const params = new URLSearchParams(location.search);
3252
+ if (view && view !== taFirstView()) params.set("view", view);
3253
+ else params.delete("view");
3254
+ const query = params.toString() ? "?" + params.toString() : "";
3255
+ if (query === location.search) return;
3256
+ try { history.replaceState(null, "", location.pathname + query + location.hash); } catch (err) {}
3257
+ if (window.parent !== window) {
3258
+ try { window.parent.postMessage({ queryString: query }, "https://huggingface.co"); } catch (err) {}
3259
+ }
3260
+ }
3261
  document.addEventListener("click", (event) => {
3262
  const path = typeof event.composedPath === "function" ? event.composedPath() : [event.target];
3263
+ const tab = path.find((node) => node && node.matches && node.matches(".ta-viewtab"));
3264
+ if (tab) window.taShowView(tab.closest(".ta-page"), tab.dataset.view);
 
 
3265
  }, true);
3266
+ /* A shared link's view belongs to the page the link names, which shows only once the server has
3267
+ selected it, so this waits for that page's tabs (up to 15 s); a name the page has no view for is
3268
+ dropped from the address. */
3269
+ (function taOpenLinkedView() {
3270
+ const params = new URLSearchParams(location.search);
3271
+ const view = params.get("view");
3272
+ if (!view || !/^[a-z0-9]+$/.test(view)) return;
3273
+ const slug = params.get("tab") || window.taPageSlugs[0];
3274
+ let tries = 0;
3275
+ const attempt = () => {
3276
+ const tab = document.querySelector(`.ta-viewbar[data-page="${slug}"] .ta-viewtab[data-view="${view}"]`);
3277
+ if (tab) window.taShowView(tab.closest(".ta-page"), view, false);
3278
+ else if (document.querySelector(`.ta-viewbar[data-page="${slug}"]`) || ++tries >= 60) taSetViewParam("");
3279
+ else setTimeout(attempt, 250);
3280
+ };
3281
+ attempt();
3282
+ })();
3283
  /* Copy a block's text, as the citation dialog's button does. Where the clipboard is not offered
3284
  (some embeds refuse it), the text is selected instead so the reader can copy it by hand. */
3285
  window.taCopy = function (button, id) {
 
3313
  document.addEventListener("click", (ev) => {
3314
  if (ev.target.classList && ev.target.classList.contains("ta-dialog-overlay")) window.taCloseDialogs();
3315
  });
3316
+ function taCloseMenus(except) {
3317
+ const open = "details.ta-dl[open], details.ta-follow-pick[open]";
3318
  document.querySelectorAll(open).forEach((menu) => {
3319
  if (menu !== except) menu.open = false;
3320
  });
 
3322
  document.addEventListener("keydown", (ev) => {
3323
  if (ev.key !== "Escape") return;
3324
  window.taCloseDialogs();
3325
+ taCloseMenus(null);
3326
  });
3327
+ /* A pick closes a panel's download menu, and so does a click anywhere else. Clicks on its own
3328
+ summary are left to the browser, which opens and closes it. The Follow picker stays open
3329
+ through its picks, since it takes several. */
3330
  document.addEventListener("click", (ev) => {
3331
+ const menus = "details.ta-dl, details.ta-follow-pick";
3332
  const menu = ev.target.closest && ev.target.closest(menus);
3333
+ taCloseMenus(menu);
3334
+ if (menu && ev.target.closest(".ta-dl-menu button")) menu.open = false;
3335
  });
3336
 
 
 
 
 
 
 
 
 
 
3337
  /* The visible band, written onto the elements pinned to it rather than onto :root. A custom
3338
  property set on the root is inherited by every element on the page, so each scroll report
3339
  restyled all six thousand of them (~15 ms) while the reader was scrolling, in the frame whose
3340
  main thread also has to paint what scrolls into view; on these few it restyles only them.
3341
  `taBand` keeps the numbers for the scripts below; null while our viewport is the screen,
3342
  where the stylesheet's own defaults apply. */
3343
+ const TA_BAND_READERS = ".ta-dialog-overlay, .ta-tile-max, .ta-tile-backdrop, #ta-debug-readout";
3344
  let taBand = null;
3345
  function taSetBand(band) {
3346
  taBand = band;
 
3356
  taLoadFramesInBand();
3357
  }
3358
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3359
  /* --- Keep --ta-vp-top / --ta-vp-h on the visible band ----------------------
3360
  Only does anything when this page's viewport is not the screen. Embedded in a Hugging Face
3361
  Space it is not: HF sizes the Space iframe to the app's whole content, so the iframe never
3362
  scrolls, the outer page does, and our viewport is the entire document. Everything pinned to a
3363
+ screen edge then pins to the top of a six-thousand-pixel page instead: opening a dialog
3364
+ blacks out the whole page with its content parked far above the screen.
 
3365
 
3366
  The parent is cross-origin, so its scroll position is unreadable. IntersectionObserver's
3367
  implicit root is the top-level viewport though, and it reports the intersection in *our*
 
3446
  taSetBand(null);
3447
  } else {
3448
  taSetBand({ top: Math.round(top), h: Math.round(bottom - top) });
 
3449
  }
3450
  taReportViewport();
3451
  }
 
3560
  // then there are two scrollports rather than none. Both cases come out right here,
3561
  // because a page that cannot scroll has `scrollY === 0` anyway.
3562
  taSetBand({ top: Math.round(top), h: Math.round(bottom - top) });
 
3563
  taReportViewport();
3564
  }, { threshold: TA_STEPS });
3565
 
 
3580
  // Coalesce a burst of mutations into one pass; the guards above make it idempotent.
3581
  clearTimeout(window._taStampTimer);
3582
  window._taStampTimer = setTimeout(() => {
 
3583
  taTrackViewport();
3584
  taSetBand(taBand);
3585
  // A re-render brings fresh frames to load, a page switch a new page.
 
3592
  taFitTopBar();
3593
  }, 60);
3594
  }).observe(document.body, { childList: true, subtree: true });
 
 
3595
  document.addEventListener("DOMContentLoaded", () => {
 
3596
  taTrackViewport();
3597
  taReportViewport();
3598
  taSyncThemeChip();
 
3671
  /* How tall the reader's visible band is, for the per-dataset browser's "fit to screen" button.
3672
  The frame cannot work this out for itself: it is sized to its own content, so its
3673
  `innerHeight` is the length of its page rather than of the screen. We already track the band
3674
+ for the dialogs (`taBand`), so send that. */
3675
  window.taSendPerDatasetViewport = function (frame) {
3676
  if (frame.dataset.task === undefined) return;
3677
  const height = taBand && taBand.h > 200 ? taBand.h : window.innerHeight;
 
3681
  } catch (err) { /* not ready; it will ask again with its next height report */ }
3682
  };
3683
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3684
  /* --- Leaderboard table sorting ---------------------------------------------
3685
  Click a header to sort the rows beneath it; click again to reverse. Numeric
3686
  columns are marked `data-type="num"` and every cell carries the raw value in
pages.py CHANGED
@@ -8,8 +8,8 @@ hosted elsewhere). Adding a leaderboard is a one-entry edit to :data:`PAGES`.
8
 
9
  from __future__ import annotations
10
 
11
- import html
12
  import itertools
 
13
  from dataclasses import dataclass
14
  from pathlib import Path
15
 
@@ -46,7 +46,6 @@ from views import (
46
  METRIC_NOTES,
47
  Choice,
48
  ChoiceRow,
49
- make_selection_bar,
50
  METRIC_LABELS,
51
  make_beyond_hero,
52
  make_beyond_overview_figure,
@@ -54,16 +53,16 @@ from views import (
54
  has_end_to_end,
55
  make_tabarena_hero,
56
  lb_spec,
57
- open_doc,
58
  follow_picker,
 
59
  make_figure_grid,
60
  make_leaderboard,
61
  make_per_dataset_block,
62
  metric_definitions_html,
63
  picked,
64
  render_markdown,
65
- section_head,
66
  selector_card,
 
67
  )
68
 
69
 
@@ -316,31 +315,6 @@ def _render_control_band(
316
  api_visibility="private",
317
  )
318
 
319
- # The bottom bar re-renders on any selector change, so it can never describe a stale view.
320
- @gr.render(
321
- inputs=[
322
- entrants_state,
323
- care_state,
324
- metric_state,
325
- tasks_state,
326
- datasets_state,
327
- impute_state,
328
- splits_state,
329
- commercial_state,
330
- ]
331
- )
332
- def _render_selection_bar(entrants, care, metric, tasks, datasets, imputation, splits, noncommercial):
333
- make_selection_bar(
334
- entrants=entrants,
335
- care=care,
336
- metric=metric,
337
- tasks=tasks,
338
- datasets=datasets,
339
- imputation=imputation,
340
- splits=splits,
341
- include_noncommercial=noncommercial,
342
- )
343
-
344
 
345
  def render_internal_page(page: LeaderboardPage) -> None:
346
  make_tabarena_hero(page.data_root, _tabarena_docs())
@@ -353,6 +327,21 @@ def render_internal_page(page: LeaderboardPage) -> None:
353
  tasks_state = gr.State("all")
354
  datasets_state = gr.State("all")
355
  commercial_state = gr.State(True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
 
357
  _render_control_band(
358
  page.data_root,
@@ -366,105 +355,82 @@ def render_internal_page(page: LeaderboardPage) -> None:
366
  commercial_state,
367
  )
368
 
369
- # -- The Summary across all subsets. It follows the selectors too: the column each one points at
370
- # leads its group and is tinted, so the reader's own view is where their eye already is.
371
- @gr.render(inputs=[entrants_state, tasks_state, datasets_state, care_state, commercial_state])
372
- def _render_overview(entrants, tasks, datasets, care, noncommercial):
373
- make_cross_subset_overview(
374
- page.data_root,
375
- entrants=entrants,
376
- tasks=tasks,
377
- datasets=datasets,
378
- care=care,
379
- include_noncommercial=noncommercial,
380
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
381
 
382
- # -- Dataset by dataset: the browser hangs under the overview table, collapsed. Both look across
383
- # the whole benchmark rather than at the one leaderboard selected below. Its own render block, so
384
- # that opening it rebuilds nothing else.
385
- per_dataset_open = gr.State(False)
386
 
387
- @gr.render(
388
- inputs=[
389
- per_dataset_open,
390
- entrants_state,
391
- impute_state,
392
- splits_state,
393
- tasks_state,
394
- datasets_state,
395
- commercial_state,
396
- ]
397
- )
398
- def _render_per_dataset(opened, ent, imp, spl, tsk, dst, noncommercial):
399
- subset = Subset(entrants=ent, imputation=imp, splits=spl, tasks=tsk, datasets=dst)
400
- lb = LBContainer(
401
- data_root=page.data_root, subset=subset, name=subset_name(subset), include_noncommercial=noncommercial
402
- )
403
- toggle = make_per_dataset_block(lb, opened=opened, anchor="ta-perdataset-section")
404
- toggle.click(lambda now: not now, per_dataset_open, per_dataset_open, api_visibility="private")
405
 
406
- # -- The selected leaderboard, under a heading that says what the selection covers. "I care
407
- # about" and "Measured by" are not inputs: the figure grid follows them in the browser.
408
- @gr.render(
409
- inputs=[
410
- entrants_state,
411
- impute_state,
412
- splits_state,
413
- tasks_state,
414
- datasets_state,
415
- commercial_state,
416
- ]
417
- )
418
- def _render_subset(ent, imp, spl, tsk, dst, noncommercial):
419
- subset = Subset(entrants=ent, imputation=imp, splits=spl, tasks=tsk, datasets=dst)
420
- lb = LBContainer(
421
- data_root=page.data_root, subset=subset, name=subset_name(subset), include_noncommercial=noncommercial
422
- )
423
- # The table is the one file every published cell has; a folder alone can be an empty
424
- # shell left by a skipped evaluation (a subset with no dataset of that task type).
425
- if not (lb.base_path / "website_leaderboard.csv").is_file():
426
- gr.HTML(section_head(website_texts.LB_TITLE, anchor="lb-detailed", hint=website_texts.LB_HINT))
427
- gr.Markdown(website_texts.SUBSET_MISSING, elem_classes="ta-warnbox", container=False)
428
- return
429
  line, notes = lb_spec(lb)
430
- # The Follow row sits under the heading, before the figures it marks.
431
- gr.HTML(
432
- section_head(
433
- website_texts.LB_TITLE, line, anchor="lb-detailed", hint=f"{website_texts.LB_HINT} {notes}".strip()
434
- )
435
- + follow_picker(lb)
436
  )
437
- # The table closes the section as its detailed reference, collapsed because that is what a
438
- # reference is for.
439
- make_figure_grid(lb)
440
 
441
- @gr.render(
442
- inputs=[
443
- entrants_state,
444
- impute_state,
445
- splits_state,
446
- tasks_state,
447
- datasets_state,
448
- commercial_state,
449
- ]
450
- )
451
- def _render_leaderboard_table(ent, imp, spl, tsk, dst, noncommercial):
452
- subset = Subset(entrants=ent, imputation=imp, splits=spl, tasks=tsk, datasets=dst)
453
- lb = LBContainer(
454
- data_root=page.data_root, subset=subset, name=subset_name(subset), include_noncommercial=noncommercial
455
- )
456
- if not (lb.base_path / "website_leaderboard.csv").is_file():
457
- return
458
- make_leaderboard(lb)
459
 
460
 
461
  def render_beyondarena_page(page: LeaderboardPage) -> None:
462
  """The BeyondArena leaderboard: four crossed filter axes, always on the ``core2k`` protocol.
463
 
464
- The cross-subset overview stays the two ``plot_subset_results`` images rather than TabArena's
465
- HTML heatmap. Below the control card everything is TabArena's: the figure grid ordered by
466
- "I care about", the full table and the per-dataset browser, read from
467
- the selected cell's ``data_beyondarena/subsets/split_*/size_*/features_*/tasks_*/``.
468
  """
469
  # Graceful fallback until the artifacts are generated (see
470
  # scripts/run_generate_beyondarena_website_artifacts.py in the tabarena repo).
@@ -485,31 +451,15 @@ def render_beyondarena_page(page: LeaderboardPage) -> None:
485
 
486
  make_beyond_hero(page.data_root, _beyond_docs())
487
 
488
- # -- Cross-subset overview (image, not the TabArena HTML heatmap)
489
- gr.HTML(
490
- section_head(
491
- website_texts.OVERVIEW_TITLE,
492
- html.escape(website_texts.BEYOND_OVERVIEW_LINE),
493
- hint=website_texts.BEYOND_OVERVIEW_HINT,
494
- actions=(
495
- '<span class="ta-section-links">'
496
- f'<button type="button" class="ta-jump" onclick="{open_doc("beyondarena", "subsets")}">'
497
- f"{website_texts.BEYOND_SUBSETS_LINK}</button>"
498
- f'<a href="#beyond-lb-detailed" class="ta-jump">{website_texts.BEYOND_JUMP_DETAILS}</a></span>'
499
- ),
500
- )
501
- )
502
- make_beyond_overview_figure(page.data_root)
503
-
504
- # One state per filter axis, in BEYOND_AXES order, each starting on its "all" chip. Made here,
505
- # above the control card that sets them, because the dataset browser under the overview reads
506
- # them too.
507
  axis_states = [gr.State("all") for _ in BEYOND_AXES]
508
  care_state = gr.State("quality")
509
  metric_state = gr.State("elo")
510
  commercial_state = gr.State(True)
 
511
 
512
- def _lb(keys: tuple[str, ...], noncommercial: bool) -> LBContainer:
 
513
  subset = BeyondSubset(**{field: key for (field, _caption, _labels), key in zip(BEYOND_AXES, keys, strict=True)})
514
  return LBContainer(
515
  data_root=page.data_root,
@@ -518,59 +468,51 @@ def render_beyondarena_page(page: LeaderboardPage) -> None:
518
  include_noncommercial=noncommercial,
519
  )
520
 
521
- # -- Dataset by dataset, under the overview: read from the "full" subset and told which slice is
522
- # selected.
523
- per_dataset_open = gr.State(False)
 
 
524
 
525
- @gr.render(inputs=[per_dataset_open, *axis_states, commercial_state])
526
- def _render_beyond_per_dataset(opened, *args):
527
- *keys, noncommercial = args
528
- lb = _lb(tuple(keys), noncommercial)
529
- toggle = make_per_dataset_block(
530
- lb,
531
- opened=opened,
532
- source_subset=BeyondSubset(),
533
- filter_attrs=beyond_per_dataset_attrs(lb.subset),
534
- anchor="beyond-perdataset-section",
535
- )
536
- toggle.click(lambda now: not now, per_dataset_open, per_dataset_open, api_visibility="private")
537
 
538
  _render_beyond_control_band(page.data_root, axis_states, care_state, metric_state, commercial_state)
539
 
540
- # -- The selected leaderboard: the TabArena figure grid for the selected subset, under a heading
541
- # that says what the selection covers.
542
-
543
- @gr.render(inputs=[*axis_states, commercial_state])
544
- def _render_beyond_subset(*args):
545
- *keys, noncommercial = args
546
- lb = _lb(tuple(keys), noncommercial)
547
- if not (lb.base_path / "website_leaderboard.csv").is_file():
548
- # The generator skips exactly the cells no dataset falls into, so a missing folder
549
- # with filters set means an empty intersection, not a deployment gap.
550
- missing = website_texts.BEYOND_CELL_EMPTY if lb.subset.filters else website_texts.SUBSET_MISSING
551
- gr.HTML(
552
- section_head(website_texts.LB_TITLE, anchor="beyond-lb-detailed", hint=website_texts.BEYOND_LB_HINT)
553
- )
554
- gr.Markdown(missing, elem_classes="ta-warnbox", container=False)
555
- return
556
- line, notes = lb_spec(lb)
557
- gr.HTML(
558
- section_head(
559
- website_texts.LB_TITLE,
560
- line,
561
- anchor="beyond-lb-detailed",
562
- hint=f"{website_texts.BEYOND_LB_HINT} {notes}".strip(),
563
- )
564
- + follow_picker(lb)
565
- )
566
- make_figure_grid(lb)
567
-
568
- @gr.render(inputs=[*axis_states, commercial_state])
569
- def _render_beyond_table(*args):
570
- *keys, noncommercial = args
571
- lb = _lb(tuple(keys), noncommercial)
572
- if (lb.base_path / "website_leaderboard.csv").is_file():
573
- make_leaderboard(lb)
574
 
575
 
576
  def _render_beyond_control_band(
 
8
 
9
  from __future__ import annotations
10
 
 
11
  import itertools
12
+ import re
13
  from dataclasses import dataclass
14
  from pathlib import Path
15
 
 
46
  METRIC_NOTES,
47
  Choice,
48
  ChoiceRow,
 
49
  METRIC_LABELS,
50
  make_beyond_hero,
51
  make_beyond_overview_figure,
 
53
  has_end_to_end,
54
  make_tabarena_hero,
55
  lb_spec,
 
56
  follow_picker,
57
+ VIEW_TILES,
58
  make_figure_grid,
59
  make_leaderboard,
60
  make_per_dataset_block,
61
  metric_definitions_html,
62
  picked,
63
  render_markdown,
 
64
  selector_card,
65
+ view_bar,
66
  )
67
 
68
 
 
315
  api_visibility="private",
316
  )
317
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
 
319
  def render_internal_page(page: LeaderboardPage) -> None:
320
  make_tabarena_hero(page.data_root, _tabarena_docs())
 
327
  tasks_state = gr.State("all")
328
  datasets_state = gr.State("all")
329
  commercial_state = gr.State(True)
330
+ selection = [entrants_state, impute_state, splits_state, tasks_state, datasets_state, commercial_state]
331
+
332
+ def _lb(ent, imp, spl, tsk, dst, noncommercial) -> LBContainer:
333
+ subset = Subset(entrants=ent, imputation=imp, splits=spl, tasks=tsk, datasets=dst)
334
+ return LBContainer(
335
+ data_root=page.data_root, subset=subset, name=subset_name(subset), include_noncommercial=noncommercial
336
+ )
337
+
338
+ # -- The views' tabs, then the selector card joined under them, then the views. Every view is on
339
+ # the page from the start and the browser shows one (main.taShowView), so a tab switch costs no
340
+ # round trip; each view's content renders from the selection it follows. The tab bar renders
341
+ # with the selection too, for the Follow picker's list.
342
+ @gr.render(inputs=selection)
343
+ def _render_view_bar(*args):
344
+ _view_bar_block("tabarena", _lb(*args), website_texts.LB_HINT)
345
 
346
  _render_control_band(
347
  page.data_root,
 
355
  commercial_state,
356
  )
357
 
358
+ with gr.Column(elem_classes=["ta-view", "ta-view-leaderboard"]):
359
+
360
+ @gr.render(inputs=selection)
361
+ def _render_leaderboard(*args):
362
+ lb = _lb(*args)
363
+ if not _published(lb):
364
+ gr.Markdown(website_texts.SUBSET_MISSING, elem_classes="ta-warnbox", container=False)
365
+ return
366
+ make_figure_grid(lb, VIEW_TILES["leaderboard"])
367
+ make_leaderboard(lb, opened=True)
368
+
369
+ # The Summary across all subsets follows the selectors too: the column each one points at leads
370
+ # its group and is tinted, so the reader's own view is where their eye already is.
371
+ with gr.Column(elem_classes=["ta-view", "ta-view-summary"]):
372
+
373
+ @gr.render(inputs=[entrants_state, tasks_state, datasets_state, care_state, commercial_state])
374
+ def _render_overview(entrants, tasks, datasets, care, noncommercial):
375
+ make_cross_subset_overview(
376
+ page.data_root,
377
+ entrants=entrants,
378
+ tasks=tasks,
379
+ datasets=datasets,
380
+ care=care,
381
+ include_noncommercial=noncommercial,
382
+ )
383
 
384
+ # "I care about" and "Measured by" are not inputs of the figure views: their grids follow them
385
+ # in the browser.
386
+ for view in ("time", "h2h"):
387
+ with gr.Column(elem_classes=["ta-view", f"ta-view-{view}"]):
388
 
389
+ @gr.render(inputs=selection)
390
+ def _render_figures(*args, view=view):
391
+ lb = _lb(*args)
392
+ if not _published(lb):
393
+ gr.Markdown(website_texts.SUBSET_MISSING, elem_classes="ta-warnbox", container=False)
394
+ return
395
+ make_figure_grid(lb, VIEW_TILES[view])
 
 
 
 
 
 
 
 
 
 
 
396
 
397
+ with gr.Column(elem_classes=["ta-view", "ta-view-datasets"]):
398
+
399
+ @gr.render(inputs=selection)
400
+ def _render_per_dataset(*args):
401
+ make_per_dataset_block(_lb(*args))
402
+
403
+
404
+ def _view_bar_block(slug: str, lb: LBContainer, hint: str) -> None:
405
+ """A page's view tabs with Follow (views.view_bar), for the selection ``lb``; each tab's hover text
406
+ ends with what the selection covers, and a warning follows the tabs where it has one."""
407
+ if not _published(lb):
408
+ bar = view_bar(slug)
409
+ else:
 
 
 
 
 
 
 
 
 
 
410
  line, notes = lb_spec(lb)
411
+ warn = re.search(r'<span class="ta-head-warn".*?</span>', line)
412
+ bar = view_bar(
413
+ slug,
414
+ follow=follow_picker(lb),
415
+ hint=f"{re.sub(r'<[^>]+>', '', line)}. {hint} {notes}".strip(),
416
+ extra=warn.group(0) if warn else "",
417
  )
418
+ gr.HTML(bar, js_on_load="taSyncViewTabs(element);", apply_default_css=False, elem_classes="ta-viewbar-block")
 
 
419
 
420
+
421
+ def _published(lb: LBContainer) -> bool:
422
+ """Whether the selected cell has a leaderboard. The table is the one file every published cell
423
+ has; a folder alone can be an empty shell left by a skipped evaluation (a subset with no
424
+ dataset of that task type)."""
425
+ return (lb.base_path / "website_leaderboard.csv").is_file()
 
 
 
 
 
 
 
 
 
 
 
 
426
 
427
 
428
  def render_beyondarena_page(page: LeaderboardPage) -> None:
429
  """The BeyondArena leaderboard: four crossed filter axes, always on the ``core2k`` protocol.
430
 
431
+ The views are TabArena's, under the same tabs, except the Summary, which stays the two
432
+ ``plot_subset_results`` images rather than TabArena's HTML table. The others read the selected
433
+ cell's ``data_beyondarena/subsets/split_*/size_*/features_*/tasks_*/``.
 
434
  """
435
  # Graceful fallback until the artifacts are generated (see
436
  # scripts/run_generate_beyondarena_website_artifacts.py in the tabarena repo).
 
451
 
452
  make_beyond_hero(page.data_root, _beyond_docs())
453
 
454
+ # One state per filter axis, in BEYOND_AXES order, each starting on its "all" chip.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  axis_states = [gr.State("all") for _ in BEYOND_AXES]
456
  care_state = gr.State("quality")
457
  metric_state = gr.State("elo")
458
  commercial_state = gr.State(True)
459
+ selection = [*axis_states, commercial_state]
460
 
461
+ def _lb(*args) -> LBContainer:
462
+ *keys, noncommercial = args
463
  subset = BeyondSubset(**{field: key for (field, _caption, _labels), key in zip(BEYOND_AXES, keys, strict=True)})
464
  return LBContainer(
465
  data_root=page.data_root,
 
468
  include_noncommercial=noncommercial,
469
  )
470
 
471
+ def _missing(lb: LBContainer) -> None:
472
+ # The generator skips exactly the cells no dataset falls into, so a missing folder with
473
+ # filters set means an empty intersection, not a deployment gap.
474
+ missing = website_texts.BEYOND_CELL_EMPTY if lb.subset.filters else website_texts.SUBSET_MISSING
475
+ gr.Markdown(missing, elem_classes="ta-warnbox", container=False)
476
 
477
+ @gr.render(inputs=selection)
478
+ def _render_beyond_view_bar(*args):
479
+ _view_bar_block("beyondarena", _lb(*args), website_texts.BEYOND_LB_HINT)
 
 
 
 
 
 
 
 
 
480
 
481
  _render_beyond_control_band(page.data_root, axis_states, care_state, metric_state, commercial_state)
482
 
483
+ with gr.Column(elem_classes=["ta-view", "ta-view-leaderboard"]):
484
+
485
+ @gr.render(inputs=selection)
486
+ def _render_beyond_leaderboard(*args):
487
+ lb = _lb(*args)
488
+ if not _published(lb):
489
+ _missing(lb)
490
+ return
491
+ make_figure_grid(lb, VIEW_TILES["leaderboard"])
492
+ make_leaderboard(lb, opened=True)
493
+
494
+ # The Summary: the overview images, which hold every subset whatever is selected.
495
+ with gr.Column(elem_classes=["ta-view", "ta-view-summary"]):
496
+ make_beyond_overview_figure(page.data_root)
497
+
498
+ for view in ("time", "h2h"):
499
+ with gr.Column(elem_classes=["ta-view", f"ta-view-{view}"]):
500
+
501
+ @gr.render(inputs=selection)
502
+ def _render_beyond_figures(*args, view=view):
503
+ lb = _lb(*args)
504
+ if not _published(lb):
505
+ _missing(lb)
506
+ return
507
+ make_figure_grid(lb, VIEW_TILES[view])
508
+
509
+ # Per dataset: read from the "full" subset and told which slice is selected.
510
+ with gr.Column(elem_classes=["ta-view", "ta-view-datasets"]):
511
+
512
+ @gr.render(inputs=selection)
513
+ def _render_beyond_per_dataset(*args):
514
+ lb = _lb(*args)
515
+ make_per_dataset_block(lb, source_subset=BeyondSubset(), filter_attrs=beyond_per_dataset_attrs(lb.subset))
 
516
 
517
 
518
  def _render_beyond_control_band(
views.py CHANGED
@@ -419,8 +419,9 @@ def leaderboard_table_html(df: pd.DataFrame, columns: list[str], table_id: str)
419
  )
420
 
421
 
422
- def make_leaderboard(lb: LBContainer) -> None:
423
- """The full leaderboard table for one subset, as a card like the figure tiles.
 
424
 
425
  The table is a generated artifact (`leaderboard_table.html`, built by
426
  `tabarena.plot.interactive.leaderboard_table`), embedded the same way as the
@@ -428,8 +429,8 @@ def make_leaderboard(lb: LBContainer) -> None:
428
  family and variant colours, chip components and imputation markers rather than
429
  reimplementing them here, where they drifted.
430
 
431
- It closes the section as its reference, so it opens on request (``main.taToggleTable``, in the
432
- browser); its frame loads only then. The download sits in the card's header but is the
433
  frame's own (``main.taLeaderboardCsv``): only the frame knows the current filters, columns and
434
  sort order. On TabArena the frame is also told which methods the latest releases added, for
435
  its "new" badges (``data-new``, ``main.taSendNew``).
@@ -452,12 +453,13 @@ def make_leaderboard(lb: LBContainer) -> None:
452
  f'<section class="ta-tile ta-lbtile" id="{uid}" data-role="table"><div class="ta-figbar">'
453
  f'<span class="ta-figbar-title" title="{caption}">{website_texts.TABLE_TITLE}</span>'
454
  f'<span class="ta-figbar-sub" title="{caption}">{caption}</span><span class="ta-figbar-actions">'
455
- '<button type="button" class="ta-viewbtn ta-csvbtn" hidden onclick="taLeaderboardCsv(this)" '
456
  'title="Download the rows and columns shown, in the current sort order">Download CSV</button>'
457
- '<button type="button" class="ta-viewbtn ta-tabletoggle" aria-expanded="false" '
458
  f'data-show="{website_texts.TABLE_SHOW}" data-hide="{website_texts.TABLE_HIDE}" '
459
- f'onclick="taToggleTable(this)">{website_texts.TABLE_SHOW}</button></span></div>'
460
- f'<div class="ta-figview ta-lbtile-body" hidden>{frame}</div></section>',
 
461
  padding=False,
462
  apply_default_css=False,
463
  elem_classes="ta-flush",
@@ -787,6 +789,14 @@ _TILES = (
787
  )
788
 
789
 
 
 
 
 
 
 
 
 
790
  def _figure_tiles(lb: LBContainer) -> list[_Tile]:
791
  """The tiles this subset has a figure for.
792
 
@@ -865,16 +875,17 @@ def _tile_html(lb: LBContainer, tile: _Tile) -> str:
865
  )
866
 
867
 
868
- def make_figure_grid(lb: LBContainer) -> None:
869
- """The subset's figures as one grid: the overview and the win rates span the width, the four
870
- scatter plots (the three Pareto fronts and the tuning trajectories) sit two to a row.
 
871
 
872
  One HTML block, so "I care about" and "Measured by" reach it without a render: the page
873
  carries the reader's choice as ``data-care`` / ``data-metric`` (``main.taSelector`` writes
874
  them), the stylesheet orders the tiles from those, and ``main.taSendMetric`` tells each
875
  explorer which metric to open on.
876
  """
877
- tiles = _figure_tiles(lb)
878
  no_total = "" if any(tile.key == "total" for tile in tiles) else " ta-no-total"
879
  gr.HTML(
880
  f'<div class="ta-figgrid{no_total}">' + "".join(_tile_html(lb, tile) for tile in tiles) + "</div>",
@@ -896,27 +907,17 @@ _PD_ICON = (
896
  def make_per_dataset_block(
897
  lb: LBContainer,
898
  *,
899
- opened: bool,
900
  source_subset: Subset | BeyondSubset | None = None,
901
  filter_attrs: str | None = None,
902
- anchor: str = "",
903
- ) -> gr.Button:
904
- """The per-dataset browser panel, returned so the caller can wire its show/hide button.
905
-
906
- It hangs under the cross-subset overview, collapsed: both look across the whole benchmark.
907
- ``anchor`` is the id its header carries for the Jump to menu.
908
 
909
  The artifact is published once per (entrants, imputation, splits) rather than for all 60
910
  task/dataset cells: a dataset's own numbers do not depend on which *other* datasets share
911
  its leaderboard, so the browser is read from the unrestricted cell and told which slice the
912
  reader currently has selected. It filters itself from there, and the reader can widen it
913
- again from inside without leaving the page.
914
-
915
- The show/hide control lives in the panel's own title bar rather than in an accordion around
916
- it: an accordion would wrap this panel in a second box with its own padding and its own
917
- title, for one button. That button is HTML inside the bar, so it needs a real Gradio button
918
- to reach the server (`main.taTogglePerDataset` clicks the hidden one) — the frame is only
919
- built when ``opened``, since the page it embeds carries every dataset's results and is the
920
  largest artifact the site ships.
921
 
922
  ``source_subset`` and ``filter_attrs`` are for BeyondArena, whose cells have no
@@ -937,44 +938,33 @@ def make_per_dataset_block(
937
  f'data-size="{lb.subset.datasets if lb.subset.datasets in DATASET_SIZE_LABELS else "all"}" '
938
  f'data-balance="{lb.subset.datasets if lb.subset.datasets in DATASET_DISTRIBUTION_LABELS else "all"}" '
939
  )
940
- url = source.html_url("per_dataset_explorer") if opened else ""
941
- anchor = f' id="{anchor}"' if anchor else ""
942
  teaser = html.escape(website_texts.PER_DATASET_TEASER, quote=True)
943
  n = source.n_datasets
944
  caption = website_texts.PER_DATASET_CAPTION.format(n=n) if n else website_texts.FIGURE_CAPTIONS["per_dataset"]
945
- label = website_texts.PER_DATASET_HIDE if opened else website_texts.PER_DATASET_OPEN.format(n=n or "the")
946
- with gr.Column(elem_classes=["ta-figpanel", "ta-pdrawer"], elem_id=uid):
947
- # The whole bar opens it (main.taPdBarClick); the button inside is what a keyboard reaches.
948
  gr.HTML(
949
- f'<div class="ta-figbar ta-pdbar{" is-open" if opened else ""}"{anchor} '
950
- f"onclick=\"taPdBarClick(event, '{uid}')\">"
951
- f'<span class="ta-pdicon" aria-hidden="true">{_PD_ICON}</span>'
952
- f'<span class="ta-pdtext"><span class="ta-figbar-title" title="{teaser}">'
953
  f"{website_texts.PER_DATASET_TITLE}</span>"
954
- f'<span class="ta-figbar-sub">{html.escape(caption)}</span></span>'
955
- '<span class="ta-figbar-actions">'
956
- + (_download_menu(uid, "Download the chart") if opened and url else "")
957
- + f'<button type="button" class="ta-pdtoggle" aria-expanded="{str(opened).lower()}">{label}</button>'
958
- + "</span></div>"
959
  )
960
- toggle = gr.Button("toggle", elem_id=f"{uid}-toggle", elem_classes="ta-offscreen-btn")
961
- if opened and url is None:
962
  gr.HTML(f'<p class="ta-pd-teaser">{website_texts.PER_DATASET_MISSING}</p>')
963
- elif opened:
964
- # `-i` matches what `taExport` and `taPaperView` look for on the other panels.
965
- with gr.Column(elem_id=f"{uid}-i", elem_classes=["ta-figview", "ta-figview-flush"]):
966
- gr.HTML(
967
- _explorer_frame(
968
- url,
969
- f"Per-dataset results, {lb.name}",
970
- height=1020,
971
- # Read back by `main.taSendPerDatasetFilter` once the frame reports its
972
- # height, so the browser opens on the slice selected above it.
973
- extra_attrs=filter_attrs,
974
- exclude=lb.excluded_methods(),
975
- )
976
  )
977
- return toggle
978
 
979
 
980
  # --------------------------------------------------------------------------- #
@@ -1441,12 +1431,11 @@ def selector_card(
1441
  ) -> gr.HTML:
1442
  """Every selector of a page, as one card of chips that answers a click at once.
1443
 
1444
- The card starts closed, as a bar like the per-dataset browser's: its ``title``, which is the
1445
- button that opens it, and the ``examples`` (see :func:`_selector_examples`), shortcuts that pick
1446
- a chip and open the card on it. Open, the ``lead`` joins the title. Each of the ``lines`` holds
1447
- one row, or several that read as one choice, the later ones with their caption inline. Beside
1448
- the bar and the lines (under them on narrow screens) the card says how many datasets the
1449
- selection covers, open or closed. A click updates the card in the browser
1450
  (main.taSelector): the chip, the dataset counts and the ``warnings``, as (selection
1451
  field, value, headline, details in Markdown), each shown while that value is selected. The whole
1452
  selection then goes to Python as one ``pick`` event, whose listener writes the page's states.
@@ -1462,7 +1451,7 @@ def selector_card(
1462
  shortcuts = _selector_examples(rows, examples)
1463
  if shortcuts:
1464
  shortcuts = (
1465
- f'<span class="ta-sel-try"><span class="ta-sel-try-lead">{html.escape(website_texts.SELECTOR_TRY)}</span>'
1466
  f"{shortcuts}</span>"
1467
  )
1468
  # The whole bar opens and closes the card (main.taSelector); the title is the button a keyboard
@@ -1470,10 +1459,9 @@ def selector_card(
1470
  bar = (
1471
  '<div class="ta-figbar ta-pdbar ta-sel-bar">'
1472
  f'<span class="ta-pdicon" aria-hidden="true">{_SELECTOR_ICON}</span>'
1473
- '<span class="ta-pdtext"><span class="ta-sel-head">'
1474
  '<h2 class="ta-figbar-title ta-sel-title"><button type="button" class="ta-sel-toggle" aria-expanded="false">'
1475
  f'{html.escape(title)}<span class="ta-sel-chev" aria-hidden="true"></span></button></h2>'
1476
- f'<span class="ta-figbar-sub ta-sel-lead">{html.escape(lead)}</span></span>{shortcuts}</span></div>'
1477
  )
1478
  warns = "".join(
1479
  f'<details class="ta-sel-warn" data-field="{field}" data-when="{value}" hidden>'
@@ -1492,7 +1480,7 @@ def selector_card(
1492
  }
1493
  return gr.HTML(
1494
  f'<div class="ta-sel" data-page="{page}" data-open="false" data-config="{html.escape(json.dumps(data))}">'
1495
- f'{bar}<div class="ta-sel-main">{body}</div>'
1496
  '<div class="ta-sel-result" aria-live="polite">'
1497
  '<p class="ta-sel-total"><b class="ta-sel-num"></b><span class="ta-sel-unit"></span></p></div>'
1498
  f'<div class="ta-sel-warns">{warns}</div></div>',
@@ -1503,81 +1491,54 @@ def selector_card(
1503
  )
1504
 
1505
 
1506
- def picked(value: object, labels: dict[str, str]) -> str:
1507
- """``value`` when it is one of the ``labels`` keys, else the first key (the row's default).
1508
 
1509
- The selection comes from the browser, so nothing in it is used before it is checked.
1510
- """
1511
- return value if isinstance(value, str) and value in labels else next(iter(labels))
1512
 
 
 
 
1513
 
1514
- #: Anchor on the control card; the sticky selection bar watches it and scrolls back to it.
1515
- CONTROLS_ANCHOR = "ta-controls-anchor"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1516
 
1517
 
1518
- def make_selection_bar(
1519
- *,
1520
- entrants: str,
1521
- care: str,
1522
- metric: str,
1523
- tasks: str,
1524
- datasets: str,
1525
- imputation: str,
1526
- splits: str,
1527
- include_noncommercial: bool = True,
1528
- ) -> None:
1529
- """A fixed bar summarising the current selection, shown once the controls scroll away.
1530
 
1531
- The controls decide every number on a long page, so once they are off screen there is
1532
- nothing left saying which leaderboard you are reading. This restates it and links back up
1533
- to the card. Read-only on purpose: a second set of live controls means two widgets per axis
1534
- to keep in step, and the card is one scroll away. It also carries the page's "Jump to"
1535
- menu, so the bar is the one element that floats over the page.
1536
  """
1537
- parts = [
1538
- TASK_LABELS[tasks],
1539
- DATASET_LABELS[datasets],
1540
- _entrants_short(entrants),
1541
- CARE_LABELS[care],
1542
- METRIC_LABELS[metric],
1543
- ]
1544
- if imputation == "no":
1545
- parts.append("no imputed")
1546
- if splits == "lite":
1547
- parts.append("Lite")
1548
- if not include_noncommercial:
1549
- parts.append("commercial use only")
1550
- chips = "".join(f'<span class="ta-selchip">{html.escape(p)}</span>' for p in parts)
1551
- jumps = "".join(f'<a href="#{anchor}">{html.escape(label)}</a>' for anchor, label in website_texts.JUMP_LINKS)
1552
- # The bar is TabArena's, as are its anchors.
1553
- jumps += (
1554
- f'<button type="button" onclick="{open_doc("tabarena", website_texts.AGENTS_TOPIC[0])}">'
1555
- f"{html.escape(website_texts.JUMP_AGENTS)}</button>"
1556
- f'<button type="button" onclick="taOpenDialog(\'{VERSION_HISTORY_TOGGLE}\')">'
1557
- f"{html.escape(website_texts.VERSION_HISTORY_TITLE)}</button>"
1558
- )
1559
- # A link rather than a scripted scroll: inside the Space only `taJumpToAnchor` can move the
1560
- # page the reader is looking at, and it acts on anchors.
1561
- gr.HTML(
1562
- '<div class="ta-selbar">'
1563
- f'<a class="ta-selbar-view" href="#{CONTROLS_ANCHOR}" title="Back to the selectors">'
1564
- '<span class="ta-selbar-lead">Viewing</span>'
1565
- f"{chips}"
1566
- '<span class="ta-selbar-go">change</span>'
1567
- "</a>"
1568
- f'<details class="ta-jumpmenu"><summary>Jump to</summary><div class="ta-jumpmenu-list">{jumps}</div></details>'
1569
- "</div>",
1570
- apply_default_css=False,
1571
- elem_classes="ta-selbar-host",
1572
- )
1573
 
1574
 
1575
- def _entrants_short(key: str) -> str:
1576
- """A pool name short enough for the bar: the categories, not their full sentences."""
1577
- selected = entrants_categories(key)
1578
- if not selected:
1579
- return "🤖 Models only"
1580
- return "🤖 + " + " + ".join(SYSTEM_CATEGORY_LABELS[k].split(" ", 1)[0] for k in selected)
1581
 
1582
 
1583
  def make_cross_subset_overview(
@@ -1588,7 +1549,7 @@ def make_cross_subset_overview(
1588
  care: str = "quality",
1589
  include_noncommercial: bool = True,
1590
  ) -> gr.HTML:
1591
- """The Summary: every entrant (rows) on every subset (columns), within one pool, under its heading.
1592
 
1593
  A row is one model *variant* — default, tuned, or tuned + ensembled — so its quality and its
1594
  cost are always the same run's.
@@ -1879,9 +1840,8 @@ def make_cross_subset_overview(
1879
  )
1880
 
1881
  table = f'<table class="ta-xo" data-auto="{auto_open}">{header}<tbody>{"".join(body)}</tbody></table>'
1882
- head = section_head(website_texts.OVERVIEW_TITLE, anchor="ta-overview-section", hint=website_texts.OVERVIEW_SUB)
1883
  return gr.HTML(
1884
- f'{head}{_OVERVIEW_CSS}<div class="ta-xo-scroll">{table}</div>',
1885
  js_on_load="taOverviewTable(element);",
1886
  apply_default_css=False,
1887
  elem_classes="ta-overview-block",
@@ -1889,9 +1849,8 @@ def make_cross_subset_overview(
1889
 
1890
 
1891
  def _no_overview() -> gr.HTML:
1892
- """The overview's heading over a note, when its pool has no data."""
1893
- head = section_head(website_texts.OVERVIEW_TITLE, anchor="ta-overview-section", hint=website_texts.OVERVIEW_SUB)
1894
- return gr.HTML(f"{head}<p>No overview data available.</p>", elem_classes="ta-overview-block")
1895
 
1896
 
1897
  #: An overview column as its hover text names it.
@@ -2108,12 +2067,6 @@ def _added_entrants(releases: Sequence[Release]) -> list[tuple[str, Release]]:
2108
  return added
2109
 
2110
 
2111
- def _newest_entrant(releases: Sequence[Release]) -> str:
2112
- """The entrant the newest release that adds one names, or "" when none does."""
2113
- added = _added_entrants(releases)
2114
- return added[0][0] if added else ""
2115
-
2116
-
2117
  def _entrant_facts(df: pd.DataFrame) -> dict[str, str]:
2118
  """How many models and systems a leaderboard lists, a method counting once whatever its variants."""
2119
  method = df["Model"].map(lambda m: parse_model(m)[0])
@@ -2137,27 +2090,13 @@ def _entrant_facts(df: pd.DataFrame) -> dict[str, str]:
2137
 
2138
 
2139
  def hero_facts(data_root: Path) -> dict[str, str]:
2140
- """The numbers the TabArena hero quotes, read from the published data and the version history.
2141
 
2142
  Methods are counted in the widest entrant pool, so the hero names everything TabArena covers
2143
  rather than whichever pool the page opens on.
2144
  """
2145
  df = LBContainer(data_root, Subset(entrants=widest_entrants_key()), "").load_df()
2146
- releases, _ = parse_version_history(website_texts.VERSION_HISTORY_BUTTON_TEXT)
2147
- updated = releases[0].when if releases else "—"
2148
- newest = _newest_entrant(releases)
2149
- if newest:
2150
- fresh = website_texts.HERO_FRESH_NEWEST.format(newest=newest)
2151
- else:
2152
- fresh = website_texts.HERO_FRESH_UPDATED.format(updated=updated)
2153
- return {
2154
- "datasets": str(LBContainer(data_root, Subset(), "").n_datasets or "—"),
2155
- **_entrant_facts(df),
2156
- "releases": str(len(releases)),
2157
- "since": f"{releases[-1].date:%b %Y}" if releases else "—",
2158
- "updated": updated,
2159
- "fresh": fresh,
2160
- }
2161
 
2162
 
2163
  def _dialog(toggle: str, label: str, body: str, *, cls: str, head: str = "") -> str:
@@ -2197,20 +2136,28 @@ def metric_definitions_html() -> str:
2197
 
2198
 
2199
  def learn_more(
2200
- toggle: str, label: str, topics: Sequence[tuple[str, str, str]], cta: tuple[str, str] | None = None
 
 
 
 
2201
  ) -> tuple[str, str]:
2202
  """A page's Learn more row and the dialog it opens, for (key, label, HTML) topics.
2203
 
2204
  Every topic is in the page from the start, so a pill opens at once without a round trip to the
2205
  server, and the tabs at the top of the dialog switch topic without closing it. Returned apart,
2206
- so the row can sit inside a layout the dialog would otherwise add a gap to. ``cta``, as (label,
2207
- link), ends the row as a highlighted link out, which also draws the eye to the pills.
 
2208
  """
2209
- pills = "".join(
2210
- f'<button type="button" class="ta-learn-pill" onclick="taOpenDoc(\'{toggle}\', \'{key}\')">'
2211
- f"{html.escape(name)}</button>"
2212
- for key, name, _ in topics
2213
- )
 
 
 
2214
  if cta:
2215
  cta_label, url = cta
2216
  pills += (
@@ -2257,56 +2204,25 @@ def _team_dialog(toggle: str, title: str, team: Sequence[tuple[str, str]]) -> st
2257
  return _dialog(toggle, title, body, cls="ta-dialog-team")
2258
 
2259
 
2260
- def _hero_stat(
2261
- value: str, label: str, line: str, hint: str, target: str, *, facts: dict[str, str], docs_toggle: str
2262
- ) -> str:
2263
- """One selling point: a big value, what it counts and a short line, opening its target.
2264
-
2265
- A numeric value counts up when the hero first shows (the ``ta-count-up`` animation in main.py).
2266
- """
2267
- value, label, line, hint = (part.format(**facts) for part in (value, label, line, hint))
2268
- if value.isdigit():
2269
- num = (
2270
- f'<span class="ta-stat-num" data-count style="--ta-count-to: {value}">'
2271
- f'<span class="ta-sr-only">{value}</span></span>'
2272
- )
2273
- else:
2274
- num = f'<span class="ta-stat-num">{html.escape(value)}</span>'
2275
- inner = (
2276
- f'{num}<span class="ta-stat-text"><span class="ta-stat-lbl">{html.escape(label)}</span>'
2277
- f'<span class="ta-stat-line">{html.escape(line)}</span></span>'
2278
- )
2279
- title = html.escape(hint)
2280
- if target.startswith("doc:"):
2281
- action = f"taOpenDoc('{docs_toggle}', '{target[4:]}')"
2282
- return f'<button type="button" class="ta-stat" title="{title}" onclick="{action}">{inner}</button>'
2283
- if target == "history":
2284
- # The version history is the proof that the benchmark is alive, so its line has a live dot.
2285
- action = f"taOpenDialog('{VERSION_HISTORY_TOGGLE}')"
2286
- return f'<button type="button" class="ta-stat" data-live title="{title}" onclick="{action}">{inner}</button>'
2287
- return f'<a class="ta-stat" href="{target}" target="_blank" rel="noopener noreferrer" title="{title}">{inner}</a>'
2288
-
2289
-
2290
  def _page_hero(
2291
  slug: str,
2292
  facts: dict[str, str],
2293
  *,
2294
- eyebrow: str,
2295
  lead: str,
2296
  paper_url: str,
2297
  citation: tuple[str, str],
2298
  cta: tuple[str, str],
2299
  team: tuple[str, Sequence[tuple[str, str]]],
2300
- stats: Sequence[tuple[str, str, str, str, str]],
2301
  docs: Sequence[tuple[str, str, str]],
2302
  ) -> gr.HTML:
2303
- """The top of a benchmark's page: what it is, its links, and why it is worth trusting.
2304
 
2305
- The selling points quote ``facts`` read from the data, so they cannot drift from what the page
2306
- shows, and each opens the details behind it. The citation (``citation`` is its label and BibTeX),
2307
- the team (``team`` is its title and members) and the ``docs`` topics of the Learn more row open
2308
- as dialogs rather than panels on the page, and ``cta`` ends that row. The hero takes its name
2309
- from the page's link in the top bar and its colour from ``slug``.
2310
  """
2311
  name = website_texts.TOPBAR_NAV[slug][1]
2312
  cite_toggle, team_toggle, docs_toggle = (_hero_toggle(slug, kind) for kind in ("cite", "team", "docs"))
@@ -2326,17 +2242,15 @@ def _page_hero(
2326
  f'<span class="ta-avatars" aria-hidden="true">{avatars}</span>{html.escape(website_texts.HERO_TEAM_LABEL)}'
2327
  "</button>"
2328
  )
2329
- band = "".join(_hero_stat(*stat, facts=facts, docs_toggle=docs_toggle) for stat in stats)
2330
- learn_row, docs_dialog = learn_more(docs_toggle, f"Learn more about {name}", docs, cta=cta)
2331
  cite = f"<h2>Cite {html.escape(name)}</h2>" + citation_html(f"{cite_toggle}-bibtex", *citation)
2332
  dialogs = _dialog(cite_toggle, f"Cite {name}", cite, cls="ta-dialog-cite")
2333
  dialogs += _team_dialog(team_toggle, *team) + docs_dialog
2334
  return gr.HTML(
2335
  f'<section class="ta-phero" data-page="{slug}">'
2336
- f'<span class="ta-phero-eyebrow">{html.escape(eyebrow)}</span>'
2337
  f'<div class="ta-phero-head"><p class="ta-phero-lead">{_MARKDOWN.renderInline(lead)}</p>'
2338
  f'<div class="ta-phero-links">{head}</div></div>'
2339
- f'<div class="ta-stats">{band}</div>'
2340
  f"{learn_row}"
2341
  f"</section>{dialogs}",
2342
  apply_default_css=False,
@@ -2349,13 +2263,12 @@ def make_tabarena_hero(data_root: Path, docs: Sequence[tuple[str, str, str]]) ->
2349
  return _page_hero(
2350
  "tabarena",
2351
  hero_facts(data_root),
2352
- eyebrow=website_texts.HERO_EYEBROW,
2353
  lead=website_texts.HERO_LEAD,
2354
  paper_url=website_texts.HERO_PAPER_URL,
2355
  citation=(website_texts.CITATION_BUTTON_LABEL, website_texts.CITATION_BUTTON_TEXT),
2356
  cta=website_texts.HERO_CTA,
2357
  team=(website_texts.TEAM_TITLE, website_texts.TEAM),
2358
- stats=website_texts.HERO_STATS,
2359
  docs=docs,
2360
  )
2361
 
@@ -2587,13 +2500,12 @@ def make_beyond_hero(data_root: Path, docs: Sequence[tuple[str, str, str]]) -> g
2587
  return _page_hero(
2588
  "beyondarena",
2589
  beyond_hero_facts(data_root),
2590
- eyebrow=website_texts.BEYOND_HERO_EYEBROW,
2591
  lead=website_texts.BEYOND_HERO_LEAD,
2592
  paper_url=website_texts.BEYOND_HERO_PAPER_URL,
2593
  citation=(website_texts.BEYOND_CITATION_BUTTON_LABEL, website_texts.BEYOND_CITATION_BUTTON_TEXT),
2594
  cta=website_texts.BEYOND_HERO_CTA,
2595
  team=(website_texts.BEYOND_TEAM_TITLE, website_texts.BEYOND_TEAM),
2596
- stats=website_texts.BEYOND_HERO_STATS,
2597
  docs=docs,
2598
  )
2599
 
@@ -2617,6 +2529,7 @@ def make_beyond_overview_figure(data_root: Path) -> None:
2617
  unzip_png(result_dir, name),
2618
  label=label,
2619
  show_label=True,
 
2620
  )
2621
  shown = True
2622
  if not shown:
 
419
  )
420
 
421
 
422
+ def make_leaderboard(lb: LBContainer, *, opened: bool = False) -> None:
423
+ """The full leaderboard table for one subset, as a card like the figure tiles, ``opened`` or
424
+ closed.
425
 
426
  The table is a generated artifact (`leaderboard_table.html`, built by
427
  `tabarena.plot.interactive.leaderboard_table`), embedded the same way as the
 
429
  family and variant colours, chip components and imputation markers rather than
430
  reimplementing them here, where they drifted.
431
 
432
+ Its button shows and hides it in the browser (``main.taToggleTable``); its frame loads only
433
+ while it is shown. The download sits in the card's header but is the
434
  frame's own (``main.taLeaderboardCsv``): only the frame knows the current filters, columns and
435
  sort order. On TabArena the frame is also told which methods the latest releases added, for
436
  its "new" badges (``data-new``, ``main.taSendNew``).
 
453
  f'<section class="ta-tile ta-lbtile" id="{uid}" data-role="table"><div class="ta-figbar">'
454
  f'<span class="ta-figbar-title" title="{caption}">{website_texts.TABLE_TITLE}</span>'
455
  f'<span class="ta-figbar-sub" title="{caption}">{caption}</span><span class="ta-figbar-actions">'
456
+ f'<button type="button" class="ta-viewbtn ta-csvbtn"{"" if opened else " hidden"} onclick="taLeaderboardCsv(this)" '
457
  'title="Download the rows and columns shown, in the current sort order">Download CSV</button>'
458
+ f'<button type="button" class="ta-viewbtn ta-tabletoggle" aria-expanded="{str(opened).lower()}" '
459
  f'data-show="{website_texts.TABLE_SHOW}" data-hide="{website_texts.TABLE_HIDE}" '
460
+ f'onclick="taToggleTable(this)">{website_texts.TABLE_HIDE if opened else website_texts.TABLE_SHOW}</button>'
461
+ "</span></div>"
462
+ f'<div class="ta-figview ta-lbtile-body"{"" if opened else " hidden"}>{frame}</div></section>',
463
  padding=False,
464
  apply_default_css=False,
465
  elem_classes="ta-flush",
 
789
  )
790
 
791
 
792
+ #: The figures of each view that shows them, by their ``_TILES`` keys.
793
+ VIEW_TILES = {
794
+ "leaderboard": ("overview",),
795
+ "time": ("infer", "total", "train", "trajectories"),
796
+ "h2h": ("winrate",),
797
+ }
798
+
799
+
800
  def _figure_tiles(lb: LBContainer) -> list[_Tile]:
801
  """The tiles this subset has a figure for.
802
 
 
875
  )
876
 
877
 
878
+ def make_figure_grid(lb: LBContainer, keys: Sequence[str]) -> None:
879
+ """The subset's figures of one view, by their ``_TILES`` keys, as one grid: the overview and
880
+ the win rates span the width, the scatter plots (the three Pareto fronts and the tuning
881
+ trajectories) sit two to a row.
882
 
883
  One HTML block, so "I care about" and "Measured by" reach it without a render: the page
884
  carries the reader's choice as ``data-care`` / ``data-metric`` (``main.taSelector`` writes
885
  them), the stylesheet orders the tiles from those, and ``main.taSendMetric`` tells each
886
  explorer which metric to open on.
887
  """
888
+ tiles = [tile for tile in _figure_tiles(lb) if tile.key in keys]
889
  no_total = "" if any(tile.key == "total" for tile in tiles) else " ta-no-total"
890
  gr.HTML(
891
  f'<div class="ta-figgrid{no_total}">' + "".join(_tile_html(lb, tile) for tile in tiles) + "</div>",
 
907
  def make_per_dataset_block(
908
  lb: LBContainer,
909
  *,
 
910
  source_subset: Subset | BeyondSubset | None = None,
911
  filter_attrs: str | None = None,
912
+ ) -> None:
913
+ """The per-dataset browser, the page's Datasets view: a panel like the figure tiles, open.
 
 
 
 
914
 
915
  The artifact is published once per (entrants, imputation, splits) rather than for all 60
916
  task/dataset cells: a dataset's own numbers do not depend on which *other* datasets share
917
  its leaderboard, so the browser is read from the unrestricted cell and told which slice the
918
  reader currently has selected. It filters itself from there, and the reader can widen it
919
+ again from inside without leaving the page. Its frame loads only once the view is shown
920
+ (main.taLoadFrames), since the page it embeds carries every dataset's results and is the
 
 
 
 
 
921
  largest artifact the site ships.
922
 
923
  ``source_subset`` and ``filter_attrs`` are for BeyondArena, whose cells have no
 
938
  f'data-size="{lb.subset.datasets if lb.subset.datasets in DATASET_SIZE_LABELS else "all"}" '
939
  f'data-balance="{lb.subset.datasets if lb.subset.datasets in DATASET_DISTRIBUTION_LABELS else "all"}" '
940
  )
941
+ url = source.html_url("per_dataset_explorer")
 
942
  teaser = html.escape(website_texts.PER_DATASET_TEASER, quote=True)
943
  n = source.n_datasets
944
  caption = website_texts.PER_DATASET_CAPTION.format(n=n) if n else website_texts.FIGURE_CAPTIONS["per_dataset"]
945
+ with gr.Column(elem_classes="ta-figpanel", elem_id=uid):
 
 
946
  gr.HTML(
947
+ f'<div class="ta-figbar"><span class="ta-figbar-title" title="{teaser}">'
 
 
 
948
  f"{website_texts.PER_DATASET_TITLE}</span>"
949
+ f'<span class="ta-figbar-sub">{html.escape(caption)}</span>'
950
+ f'<span class="ta-figbar-actions">{_download_menu(uid, "Download the chart") if url else ""}</span></div>'
 
 
 
951
  )
952
+ if url is None:
 
953
  gr.HTML(f'<p class="ta-pd-teaser">{website_texts.PER_DATASET_MISSING}</p>')
954
+ return
955
+ # `-i` matches what `taExport` and `taPaperView` look for on the other panels.
956
+ with gr.Column(elem_id=f"{uid}-i", elem_classes=["ta-figview", "ta-figview-flush"]):
957
+ gr.HTML(
958
+ _explorer_frame(
959
+ url,
960
+ f"Per-dataset results, {lb.name}",
961
+ height=1020,
962
+ # Read back by `main.taSendPerDatasetFilter` once the frame reports its
963
+ # height, so the browser opens on the slice selected above it.
964
+ extra_attrs=filter_attrs,
965
+ exclude=lb.excluded_methods(),
 
966
  )
967
+ )
968
 
969
 
970
  # --------------------------------------------------------------------------- #
 
1431
  ) -> gr.HTML:
1432
  """Every selector of a page, as one card of chips that answers a click at once.
1433
 
1434
+ The card starts closed, as one row like the per-dataset browser's bar: its ``title``, which is the
1435
+ button that opens it, the ``examples`` (see :func:`_selector_examples`), shortcuts that pick a
1436
+ chip and open the card on it, and at its end how many datasets the selection covers, open or
1437
+ closed. Open, the ``lead`` heads the ``lines`` under the bar. Each line holds one row, or several
1438
+ that read as one choice, the later ones with their caption inline. A click updates the card in the browser
 
1439
  (main.taSelector): the chip, the dataset counts and the ``warnings``, as (selection
1440
  field, value, headline, details in Markdown), each shown while that value is selected. The whole
1441
  selection then goes to Python as one ``pick`` event, whose listener writes the page's states.
 
1451
  shortcuts = _selector_examples(rows, examples)
1452
  if shortcuts:
1453
  shortcuts = (
1454
+ f'<span class="ta-sel-try" role="group" aria-label="{html.escape(website_texts.SELECTOR_TRY)}">'
1455
  f"{shortcuts}</span>"
1456
  )
1457
  # The whole bar opens and closes the card (main.taSelector); the title is the button a keyboard
 
1459
  bar = (
1460
  '<div class="ta-figbar ta-pdbar ta-sel-bar">'
1461
  f'<span class="ta-pdicon" aria-hidden="true">{_SELECTOR_ICON}</span>'
 
1462
  '<h2 class="ta-figbar-title ta-sel-title"><button type="button" class="ta-sel-toggle" aria-expanded="false">'
1463
  f'{html.escape(title)}<span class="ta-sel-chev" aria-hidden="true"></span></button></h2>'
1464
+ f"{shortcuts}</div>"
1465
  )
1466
  warns = "".join(
1467
  f'<details class="ta-sel-warn" data-field="{field}" data-when="{value}" hidden>'
 
1480
  }
1481
  return gr.HTML(
1482
  f'<div class="ta-sel" data-page="{page}" data-open="false" data-config="{html.escape(json.dumps(data))}">'
1483
+ f'{bar}<div class="ta-sel-main"><p class="ta-sel-lead">{html.escape(lead)}</p>{body}</div>'
1484
  '<div class="ta-sel-result" aria-live="polite">'
1485
  '<p class="ta-sel-total"><b class="ta-sel-num"></b><span class="ta-sel-unit"></span></p></div>'
1486
  f'<div class="ta-sel-warns">{warns}</div></div>',
 
1491
  )
1492
 
1493
 
1494
+ #: The view bar's anchor per page.
1495
+ VIEW_ANCHORS = {"tabarena": "ta-views-anchor", "beyondarena": "beyond-views-anchor"}
1496
 
 
 
 
1497
 
1498
+ def view_bar(slug: str, *, follow: str = "", hint: str = "", extra: str = "") -> str:
1499
+ """A page's view tabs (``website_texts.VIEW_TABS``), the top of its controls, with ``follow`` (the
1500
+ Follow picker, HTML) at the right.
1501
 
1502
+ Plain buttons: the browser shows the view a tab names (``main.taShowView``), every view being on
1503
+ the page from the start, so a switch costs no round trip, and marks the page's shown one
1504
+ whenever the bar is drawn again (``main.taSyncViewTabs``). A tab's hover text says what its view
1505
+ shows, ending with ``hint``, what the selection covers; ``extra`` (HTML) follows the tabs, such as
1506
+ a warning, and so does a note that only one view has, shown with it.
1507
+ """
1508
+ hints = dict(website_texts.VIEW_HINTS)
1509
+ notes = {}
1510
+ if slug == "beyondarena":
1511
+ hints.update(website_texts.BEYOND_VIEW_HINTS)
1512
+ # Its Summary charts the subsets, whose meaning is a Learn more topic.
1513
+ notes["summary"] = (
1514
+ f'<button type="button" class="ta-viewnote-link" onclick="{open_doc("beyondarena", "subsets")}">'
1515
+ f"{html.escape(website_texts.BEYOND_SUBSETS_LINK)}</button>"
1516
+ )
1517
+ buttons = "".join(
1518
+ f'<button type="button" class="ta-viewtab" role="tab" data-view="{key}" '
1519
+ f'aria-selected="{str(i == 0).lower()}" '
1520
+ f'title="{html.escape(" ".join(part for part in (hints.get(key, ""), hint) if part), quote=True)}">'
1521
+ f"{html.escape(label)}</button>"
1522
+ for i, (key, label) in enumerate(website_texts.VIEW_TABS)
1523
+ )
1524
+ shown = "".join(f'<span class="ta-viewnote" data-view="{key}">{note}</span>' for key, note in notes.items())
1525
+ return (
1526
+ f'<div class="ta-viewbar" id="{VIEW_ANCHORS[slug]}" data-page="{slug}">'
1527
+ f'<nav class="ta-viewtabs" role="tablist" aria-label="Views">{buttons}</nav>'
1528
+ f'<p class="ta-viewnotes">{shown}{extra}</p>{follow}</div>'
1529
+ )
1530
 
1531
 
1532
+ def picked(value: object, labels: dict[str, str]) -> str:
1533
+ """``value`` when it is one of the ``labels`` keys, else the first key (the row's default).
 
 
 
 
 
 
 
 
 
 
1534
 
1535
+ The selection comes from the browser, so nothing in it is used before it is checked.
 
 
 
 
1536
  """
1537
+ return value if isinstance(value, str) and value in labels else next(iter(labels))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1538
 
1539
 
1540
+ #: Anchor on TabArena's selector card.
1541
+ CONTROLS_ANCHOR = "ta-controls-anchor"
 
 
 
 
1542
 
1543
 
1544
  def make_cross_subset_overview(
 
1549
  care: str = "quality",
1550
  include_noncommercial: bool = True,
1551
  ) -> gr.HTML:
1552
+ """The Summary: every entrant (rows) on every subset (columns), within one pool, as one table.
1553
 
1554
  A row is one model *variant* — default, tuned, or tuned + ensembled — so its quality and its
1555
  cost are always the same run's.
 
1840
  )
1841
 
1842
  table = f'<table class="ta-xo" data-auto="{auto_open}">{header}<tbody>{"".join(body)}</tbody></table>'
 
1843
  return gr.HTML(
1844
+ f'{_OVERVIEW_CSS}<div class="ta-xo-scroll">{table}</div>',
1845
  js_on_load="taOverviewTable(element);",
1846
  apply_default_css=False,
1847
  elem_classes="ta-overview-block",
 
1849
 
1850
 
1851
  def _no_overview() -> gr.HTML:
1852
+ """A note in place of the overview, when its pool has no data."""
1853
+ return gr.HTML("<p>No overview data available.</p>", elem_classes="ta-overview-block")
 
1854
 
1855
 
1856
  #: An overview column as its hover text names it.
 
2067
  return added
2068
 
2069
 
 
 
 
 
 
 
2070
  def _entrant_facts(df: pd.DataFrame) -> dict[str, str]:
2071
  """How many models and systems a leaderboard lists, a method counting once whatever its variants."""
2072
  method = df["Model"].map(lambda m: parse_model(m)[0])
 
2090
 
2091
 
2092
  def hero_facts(data_root: Path) -> dict[str, str]:
2093
+ """The numbers the TabArena hero quotes, read from the published data.
2094
 
2095
  Methods are counted in the widest entrant pool, so the hero names everything TabArena covers
2096
  rather than whichever pool the page opens on.
2097
  """
2098
  df = LBContainer(data_root, Subset(entrants=widest_entrants_key()), "").load_df()
2099
+ return {"datasets": str(LBContainer(data_root, Subset(), "").n_datasets or "—"), **_entrant_facts(df)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2100
 
2101
 
2102
  def _dialog(toggle: str, label: str, body: str, *, cls: str, head: str = "") -> str:
 
2136
 
2137
 
2138
  def learn_more(
2139
+ toggle: str,
2140
+ label: str,
2141
+ topics: Sequence[tuple[str, str, str]],
2142
+ cta: tuple[str, str] | None = None,
2143
+ hints: dict[str, str] | None = None,
2144
  ) -> tuple[str, str]:
2145
  """A page's Learn more row and the dialog it opens, for (key, label, HTML) topics.
2146
 
2147
  Every topic is in the page from the start, so a pill opens at once without a round trip to the
2148
  server, and the tabs at the top of the dialog switch topic without closing it. Returned apart,
2149
+ so the row can sit inside a layout the dialog would otherwise add a gap to. ``hints`` maps a
2150
+ topic to its pill's hover text. ``cta``, as (label, link), ends the row as a highlighted link out,
2151
+ which also draws the eye to the pills.
2152
  """
2153
+ pills = ""
2154
+ for key, name, _ in topics:
2155
+ hint = (hints or {}).get(key)
2156
+ title = f' title="{html.escape(hint)}"' if hint else ""
2157
+ pills += (
2158
+ f'<button type="button" class="ta-learn-pill"{title} onclick="taOpenDoc(\'{toggle}\', \'{key}\')">'
2159
+ f"{html.escape(name)}</button>"
2160
+ )
2161
  if cta:
2162
  cta_label, url = cta
2163
  pills += (
 
2204
  return _dialog(toggle, title, body, cls="ta-dialog-team")
2205
 
2206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2207
  def _page_hero(
2208
  slug: str,
2209
  facts: dict[str, str],
2210
  *,
 
2211
  lead: str,
2212
  paper_url: str,
2213
  citation: tuple[str, str],
2214
  cta: tuple[str, str],
2215
  team: tuple[str, Sequence[tuple[str, str]]],
2216
+ hints: dict[str, str],
2217
  docs: Sequence[tuple[str, str, str]],
2218
  ) -> gr.HTML:
2219
+ """The top of a benchmark's page: what it is, its links, and the topics to learn more.
2220
 
2221
+ The Learn more pills' ``hints`` quote ``facts`` read from the data, so the numbers in them cannot
2222
+ drift from what the page shows. The citation (``citation`` is its label and BibTeX), the
2223
+ team (``team`` is its title and members) and the ``docs`` topics open as dialogs rather than
2224
+ panels on the page, and ``cta`` ends the Learn more row. The hero takes its name from the page's
2225
+ link in the top bar and its colour from ``slug``.
2226
  """
2227
  name = website_texts.TOPBAR_NAV[slug][1]
2228
  cite_toggle, team_toggle, docs_toggle = (_hero_toggle(slug, kind) for kind in ("cite", "team", "docs"))
 
2242
  f'<span class="ta-avatars" aria-hidden="true">{avatars}</span>{html.escape(website_texts.HERO_TEAM_LABEL)}'
2243
  "</button>"
2244
  )
2245
+ hints = {key: hint.format(**facts) for key, hint in hints.items()}
2246
+ learn_row, docs_dialog = learn_more(docs_toggle, f"Learn more about {name}", docs, cta=cta, hints=hints)
2247
  cite = f"<h2>Cite {html.escape(name)}</h2>" + citation_html(f"{cite_toggle}-bibtex", *citation)
2248
  dialogs = _dialog(cite_toggle, f"Cite {name}", cite, cls="ta-dialog-cite")
2249
  dialogs += _team_dialog(team_toggle, *team) + docs_dialog
2250
  return gr.HTML(
2251
  f'<section class="ta-phero" data-page="{slug}">'
 
2252
  f'<div class="ta-phero-head"><p class="ta-phero-lead">{_MARKDOWN.renderInline(lead)}</p>'
2253
  f'<div class="ta-phero-links">{head}</div></div>'
 
2254
  f"{learn_row}"
2255
  f"</section>{dialogs}",
2256
  apply_default_css=False,
 
2263
  return _page_hero(
2264
  "tabarena",
2265
  hero_facts(data_root),
 
2266
  lead=website_texts.HERO_LEAD,
2267
  paper_url=website_texts.HERO_PAPER_URL,
2268
  citation=(website_texts.CITATION_BUTTON_LABEL, website_texts.CITATION_BUTTON_TEXT),
2269
  cta=website_texts.HERO_CTA,
2270
  team=(website_texts.TEAM_TITLE, website_texts.TEAM),
2271
+ hints=website_texts.HERO_HINTS,
2272
  docs=docs,
2273
  )
2274
 
 
2500
  return _page_hero(
2501
  "beyondarena",
2502
  beyond_hero_facts(data_root),
 
2503
  lead=website_texts.BEYOND_HERO_LEAD,
2504
  paper_url=website_texts.BEYOND_HERO_PAPER_URL,
2505
  citation=(website_texts.BEYOND_CITATION_BUTTON_LABEL, website_texts.BEYOND_CITATION_BUTTON_TEXT),
2506
  cta=website_texts.BEYOND_HERO_CTA,
2507
  team=(website_texts.BEYOND_TEAM_TITLE, website_texts.BEYOND_TEAM),
2508
+ hints=website_texts.BEYOND_HERO_HINTS,
2509
  docs=docs,
2510
  )
2511
 
 
2529
  unzip_png(result_dir, name),
2530
  label=label,
2531
  show_label=True,
2532
+ elem_classes="ta-view-card",
2533
  )
2534
  shown = True
2535
  if not shown:
website_texts.py CHANGED
@@ -29,20 +29,32 @@ VERSION_HISTORY_TITLE = "Version history"
29
  VERSION_HISTORY_HINT = "Version history: what changed in each release"
30
  VERSION_HISTORY_SUB = "Every change to the leaderboard, newest first."
31
 
32
- # The "Jump to" menu of the bottom selection bar, as (anchor id, label). The anchors are set in
33
- # pages.render_internal_page; the MCP & API topic and the version history open as dialogs after them.
34
- JUMP_LINKS = [
35
- ("ta-controls-anchor", "Customize your leaderboard"),
36
- ("ta-overview-section", "Summary"),
37
- ("ta-perdataset-section", "Dataset by dataset"),
38
- ("lb-detailed", "Your leaderboard"),
 
39
  ]
40
- JUMP_AGENTS = "MCP & API"
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  # --- The page heroes (views.make_tabarena_hero, views.make_beyond_hero) -------
43
- # What the benchmark is, in one sentence (`**…**` is bold).
44
- # Heads the hero, which is the page's background: what the benchmark is, before its results.
45
- HERO_EYEBROW = "Meet our benchmark"
46
  HERO_LEAD = (
47
  "**TabArena-v0.1** is a living benchmark for predictive machine learning on IID tabular data, built to "
48
  "measure the peak performance of model-specific pipelines."
@@ -54,49 +66,16 @@ HERO_TEAM_LABEL = "Team"
54
  HERO_PAPER_URL = "https://tabarena.ai/paper-tabular-ml-iid-study"
55
  # The call to submit, as (label, link): the highlighted pill that ends the Learn more row.
56
  HERO_CTA = ("Benchmark your model", "https://tabarena.ai/code-examples")
57
- # The selling points, as (value, label, line, hover text, target). The fields in braces are read
58
- # from the data and the version history when the page is built (views.hero_facts). A click opens
59
- # the target: a Learn more topic ("doc:<topic>"), the version history ("history") or a link.
60
- HERO_STATS = [
61
- (
62
- "{datasets}",
63
- "curated datasets",
64
- "from 1,053 candidates",
65
- "Hand-picked from 1,053 real-world candidates for IID tabular data",
66
- "doc:datasets",
67
  ),
68
- (
69
- "{methods}",
70
- "{methods_label}",
71
- "each tuned to its peak",
72
- (
73
- "{entrants}, run on the same protocol and budget. Every model is scored at its default, "
74
- "tuned, and as an ensemble of tuned configurations"
75
- ),
76
- "doc:models",
77
- ),
78
- (
79
- "{releases}",
80
- "live updates",
81
- "{fresh}",
82
- "{releases} releases since {since}, as new models join. Last update: {updated}",
83
- "history",
84
- ),
85
- (
86
- "Open",
87
- "source & data",
88
- "verified implementations",
89
- (
90
- "The code, the datasets and every prediction are public, and {verified} of the {models} "
91
- "models are verified by their authors or the maintainers"
92
- ),
93
- TOPBAR_CODE_URL,
94
- ),
95
- ]
96
- # The third selling point's line on TabArena: the newest entrant in the version history, or the date of the
97
- # last update when no release names one.
98
- HERO_FRESH_NEWEST = "newest: {newest}"
99
- HERO_FRESH_UPDATED = "last update {updated}"
100
 
101
  # The "Learn more" row: each topic opens one dialog, which switches between them.
102
  LEARN_MORE_LABEL = "Learn more"
@@ -234,14 +213,7 @@ benchmark should not rank as if it covered all of it.
234
  **Repeats:** Toggle *TabArena-Lite* to score each experiment on a single repeat instead of several. It is
235
  noisier than the full setting, but much cheaper to compute and usually close enough to be worth it.
236
  """
237
- # --- The summary table (views.make_cross_subset_overview) ----------------------
238
- OVERVIEW_TITLE = "Summary"
239
- # The title's hover text.
240
- OVERVIEW_SUB = (
241
- "Every method variant on every task and dataset subset: the quickest way to tell the ones that are "
242
- "strong everywhere from those that shine on one subset, and to see what tuning buys. Per subset, "
243
- "with imputation, over all repeats."
244
- )
245
  # The search at the heading's right, which keeps the rows whose name holds its text.
246
  OVERVIEW_FIND = "Find a method"
247
  # The table's two metrics, as (headline column name, the name a breakdown's switch shows).
@@ -253,14 +225,14 @@ OVERVIEW_STUB = "{n} subsets"
253
  OVERVIEW_FOLD = "Fold these columns"
254
 
255
  # --- The selector card (views.selector_card) ---------------------------------
256
- SELECTOR_TITLE = "Customize your leaderboard"
257
  SELECTOR_SUB = "Every number below is relative to who competes and which datasets count."
258
  BEYOND_SELECTOR_SUB = "Every number below is relative to the datasets compared, so each subset is its own evaluation."
259
- # The bar's shortcuts into the card, after SELECTOR_TRY, as (label, hover text, row, value). A click
260
- # picks that chip of the row and opens the card on it, a second click undoes it. In a row of toggles
261
- # the value can be a tuple of chips turned on together; a switch names itself as the row and the
262
- # state the shortcut puts it in as the value. Chips that cannot be picked yet are left out.
263
- SELECTOR_TRY = "Make it yours:"
264
  SELECTOR_EXAMPLES = [
265
  (
266
  "Add systems",
@@ -363,24 +335,12 @@ SYSTEM_WARNINGS = {
363
  }
364
 
365
  # --- Section headings (views.section_head) ----------------------------------
366
- # The selected leaderboard's section: its title, beside it what the selection covers (views.lb_spec),
367
- # and what the section holds as the title's hover text.
368
- LB_TITLE = "Your leaderboard"
369
- LB_HINT = (
370
- "The full picture for the leaderboard selected above: how tuning moves each method, the "
371
- "accuracy-versus-time trade-off, and the complete table."
372
- )
373
  BEYOND_LB_HINT = LB_HINT + " Every BeyondArena leaderboard is computed on the core2k protocol."
374
  PER_DATASET_TITLE = "Dataset by dataset"
375
- # Its bar under the overview table: what opening it shows, and the button. `{n}` is the number of
376
- # datasets the browser covers.
377
  PER_DATASET_CAPTION = "Where each method wins and loses, on each of the {n} datasets"
378
- PER_DATASET_OPEN = "Explore all {n} datasets ▾"
379
- PER_DATASET_HIDE = "Hide ▴"
380
- # BeyondArena's overview figures, above its selector.
381
- BEYOND_OVERVIEW_LINE = "Best result per model and per model family on every BeyondArena subset"
382
- BEYOND_OVERVIEW_HINT = "The quickest way to see which methods hold up beyond the IID assumption."
383
- BEYOND_JUMP_DETAILS = "Jump to your leaderboard"
384
  # What each figure panel shows, in its header beside the title.
385
  FIGURE_CAPTIONS = {
386
  "overview": "Each method at its default, tuned, and tuned + ensembled settings",
@@ -624,7 +584,6 @@ under 0.5 s per 1000 rows?"*
624
  # BeyondArena is the second internal leaderboard: the first unified benchmark for
625
  # tabular data that goes beyond the IID assumption. Copy mirrors the TabArena
626
  # HERO_* / OVERVIEW_* / CITATION_* structure, plus a Subsets topic, with BeyondArena specifics.
627
- BEYOND_HERO_EYEBROW = "Meet our benchmark"
628
  BEYOND_HERO_LEAD = (
629
  "**BeyondArena** is the first unified benchmark for tabular data beyond the IID assumption, with "
630
  "IID, temporal and grouped splits across dataset sizes and feature types."
@@ -641,44 +600,18 @@ BEYOND_TEAM = [
641
  ("Alan Arazi", "alanarazi7"),
642
  ]
643
  BEYOND_HERO_CTA = ("Benchmark your model", "https://github.com/autogluon/tabarena/tree/main/examples/beyondarena")
644
- # As HERO_STATS; the fields in braces come from views.beyond_hero_facts.
645
- BEYOND_HERO_STATS = [
646
- (
647
- "{datasets}",
648
- "curated datasets",
649
- "IID, temporal & grouped splits",
650
- (
651
- "Split at random, by time or by group, from tiny to large and with text or high-cardinality "
652
- "columns. Every leaderboard uses the core2k protocol: 2,000 splits over all {datasets} datasets"
653
- ),
654
- "doc:datasets",
655
- ),
656
- (
657
- "{subsets}",
658
- "subsets to combine",
659
- "by split, size, features & task",
660
- "Pick one chip per row in the selector below, and each combination is its own leaderboard",
661
- "doc:subsets",
662
  ),
663
- (
664
- "{methods}",
665
- "{methods_label}",
666
- "each tuned to its peak",
667
- "{entrants}, each scored at its default, tuned, and as an ensemble of tuned configurations",
668
- "doc:models",
669
- ),
670
- (
671
- "Open",
672
- "source & data",
673
- "built on TabArena's code",
674
- (
675
- "The code, the datasets and every prediction are public, BeyondArena runs on TabArena's "
676
- "experiment and evaluation code, and {verified} of the {models} models are verified by their "
677
- "authors or the maintainers"
678
- ),
679
- TOPBAR_CODE_URL,
680
  ),
681
- ]
682
 
683
  BEYOND_OVERVIEW_DATASETS = """
684
  BeyondArena is built on a curated collection of **142 datasets** that deliberately go **beyond the IID
@@ -730,7 +663,7 @@ everywhere. A `[X% IMPUTED]` tag marks affected models.
730
 
731
  # The Subsets topic of the Learn more dialog, also opened from the overview figures (whose axes name
732
  # the subsets) by this link.
733
- BEYOND_SUBSETS_LINK = "🧩 What do the subsets mean?"
734
  BEYOND_SUBSETS_EXPLAINER = """
735
  BeyondArena is sliced into **curated subsets** so you can see how methods hold up *beyond* the IID
736
  assumption, and specifically on the kinds of data where an average hides big differences.
 
29
  VERSION_HISTORY_HINT = "Version history: what changed in each release"
30
  VERSION_HISTORY_SUB = "Every change to the leaderboard, newest first."
31
 
32
+ # The views of a benchmark page, as tabs at the top of its controls (views.view_bar), as (key, tab
33
+ # label). The first, the Summary, is the default, and `?view=<key>` opens another.
34
+ VIEW_TABS = [
35
+ ("summary", "Summary"),
36
+ ("leaderboard", "Leaderboard"),
37
+ ("time", "Quality vs time"),
38
+ ("h2h", "Head to head"),
39
+ ("datasets", "Per dataset"),
40
  ]
41
+ # A tab's hover text: what its view shows.
42
+ VIEW_HINTS = {
43
+ "summary": "Every method on every task and dataset subset at once, with its cost: who is strong "
44
+ "everywhere and who shines on one subset. Per subset, with imputation, over all repeats.",
45
+ "leaderboard": "The Elo ratings, then every number in the full table, on the datasets selected below.",
46
+ "time": "The quality against training and prediction time, and what tuning buys.",
47
+ "h2h": "The win rate of every pair of methods, on the datasets selected below.",
48
+ "datasets": "Each method's rank and gap to the best on each dataset, as the chart and its ranking.",
49
+ }
50
+ # BeyondArena's hover texts where its views differ from TabArena's.
51
+ BEYOND_VIEW_HINTS = {
52
+ "summary": "Best result per model and per model family on every BeyondArena subset: which methods "
53
+ "hold up beyond the IID assumption.",
54
+ }
55
 
56
  # --- The page heroes (views.make_tabarena_hero, views.make_beyond_hero) -------
57
+ # What the benchmark is, in one sentence (`**…**` is bold), heading the hero.
 
 
58
  HERO_LEAD = (
59
  "**TabArena-v0.1** is a living benchmark for predictive machine learning on IID tabular data, built to "
60
  "measure the peak performance of model-specific pipelines."
 
66
  HERO_PAPER_URL = "https://tabarena.ai/paper-tabular-ml-iid-study"
67
  # The call to submit, as (label, link): the highlighted pill that ends the Learn more row.
68
  HERO_CTA = ("Benchmark your model", "https://tabarena.ai/code-examples")
69
+ # The Learn more pills' hover texts, as {topic: text}. The fields in braces are read from the data when
70
+ # the page is built (views.hero_facts).
71
+ HERO_HINTS = {
72
+ "datasets": "{datasets} datasets, hand-picked from 1,053 real-world candidates for IID tabular data",
73
+ "models": (
74
+ "{entrants}, run on the same protocol and budget, each scored at its default, tuned, and as an "
75
+ "ensemble of tuned configurations. {verified} of the {models} models are verified by their authors "
76
+ "or the maintainers"
 
 
77
  ),
78
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
  # The "Learn more" row: each topic opens one dialog, which switches between them.
81
  LEARN_MORE_LABEL = "Learn more"
 
213
  **Repeats:** Toggle *TabArena-Lite* to score each experiment on a single repeat instead of several. It is
214
  noisier than the full setting, but much cheaper to compute and usually close enough to be worth it.
215
  """
216
+ # --- The summary table, the Summary view (views.make_cross_subset_overview) ----------
 
 
 
 
 
 
 
217
  # The search at the heading's right, which keeps the rows whose name holds its text.
218
  OVERVIEW_FIND = "Find a method"
219
  # The table's two metrics, as (headline column name, the name a breakdown's switch shows).
 
225
  OVERVIEW_FOLD = "Fold these columns"
226
 
227
  # --- The selector card (views.selector_card) ---------------------------------
228
+ SELECTOR_TITLE = "Customize"
229
  SELECTOR_SUB = "Every number below is relative to who competes and which datasets count."
230
  BEYOND_SELECTOR_SUB = "Every number below is relative to the datasets compared, so each subset is its own evaluation."
231
+ # The bar's shortcuts into the card (SELECTOR_TRY names the group for screen readers), as (label, hover
232
+ # text, row, value). A click picks that chip of the row and opens the card on it, a second click undoes
233
+ # it. In a row of toggles the value can be a tuple of chips turned on together; a switch names itself as
234
+ # the row and the state the shortcut puts it in as the value. Chips that cannot be picked yet are left out.
235
+ SELECTOR_TRY = "Make it yours"
236
  SELECTOR_EXAMPLES = [
237
  (
238
  "Add systems",
 
335
  }
336
 
337
  # --- Section headings (views.section_head) ----------------------------------
338
+ # Ends the view bar's hover text after what the selection covers (views.lb_spec).
339
+ LB_HINT = "The leaderboard selected below: the datasets it covers, who competes and how it is computed."
 
 
 
 
 
340
  BEYOND_LB_HINT = LB_HINT + " Every BeyondArena leaderboard is computed on the core2k protocol."
341
  PER_DATASET_TITLE = "Dataset by dataset"
342
+ # The browser's header in the Datasets view. `{n}` is the number of datasets it covers.
 
343
  PER_DATASET_CAPTION = "Where each method wins and loses, on each of the {n} datasets"
 
 
 
 
 
 
344
  # What each figure panel shows, in its header beside the title.
345
  FIGURE_CAPTIONS = {
346
  "overview": "Each method at its default, tuned, and tuned + ensembled settings",
 
584
  # BeyondArena is the second internal leaderboard: the first unified benchmark for
585
  # tabular data that goes beyond the IID assumption. Copy mirrors the TabArena
586
  # HERO_* / OVERVIEW_* / CITATION_* structure, plus a Subsets topic, with BeyondArena specifics.
 
587
  BEYOND_HERO_LEAD = (
588
  "**BeyondArena** is the first unified benchmark for tabular data beyond the IID assumption, with "
589
  "IID, temporal and grouped splits across dataset sizes and feature types."
 
600
  ("Alan Arazi", "alanarazi7"),
601
  ]
602
  BEYOND_HERO_CTA = ("Benchmark your model", "https://github.com/autogluon/tabarena/tree/main/examples/beyondarena")
603
+ # As HERO_HINTS; the fields in braces come from views.beyond_hero_facts.
604
+ BEYOND_HERO_HINTS = {
605
+ "datasets": (
606
+ "{datasets} datasets, split at random, by time or by group, from tiny to large and with text or "
607
+ "high-cardinality columns. Every leaderboard uses the core2k protocol: 2,000 splits over all of them"
 
 
 
 
 
 
 
 
 
 
 
 
 
608
  ),
609
+ "subsets": "{subsets} subsets: pick one chip per row in the selector below, and each combination is its own leaderboard",
610
+ "models": (
611
+ "{entrants}, each scored at its default, tuned, and as an ensemble of tuned configurations. "
612
+ "{verified} of the {models} models are verified by their authors or the maintainers"
 
 
 
 
 
 
 
 
 
 
 
 
 
613
  ),
614
+ }
615
 
616
  BEYOND_OVERVIEW_DATASETS = """
617
  BeyondArena is built on a curated collection of **142 datasets** that deliberately go **beyond the IID
 
663
 
664
  # The Subsets topic of the Learn more dialog, also opened from the overview figures (whose axes name
665
  # the subsets) by this link.
666
+ BEYOND_SUBSETS_LINK = "What do the subsets mean?"
667
  BEYOND_SUBSETS_EXPLAINER = """
668
  BeyondArena is sliced into **curated subsets** so you can see how methods hold up *beyond* the IID
669
  assumption, and specifically on the kinds of data where an average hides big differences.