DesignTables
Tables
Full stat lines, with their game or season context.
| Game | Result | MIN | PTS | REB | AST | STL | BLK | TOV | FG | 3P | FT | +/- |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Average3 games | – | 25.8 | 18.0 | 3.7 | 4.0 | 0.3 | 0.0 | 1.7 | 6.7/14.047.6% | 4.0/9.044.4% | 0.7/1.066.7% | -1.0 |
| Nov 9vs LAL | L114-124 | 23 | 15 | 3 | 3 | 0 | 0 | 3 | 5-11 | 4-7 | 1-1 | -11 |
| Nov 7vs BKN | W124-121 | 26 | 20 | 4 | 6 | 1 | 0 | 0 | 7-19 | 5-13 | 1-2 | +12 |
| Nov 6vs WAS | W139-132 | 29 | 19 | 4 | 3 | 0 | 0 | 2 | 8-12 | 3-7 | 0-0 | -4 |
Game context
| Game | Min vs expected | FGA vs usual | Notes |
|---|---|---|---|
| Nov 9vs LAL | – | +1 | 1 teammate out |
| Nov 7vs BKN | – | +9 | 1 teammate out · Season high |
| Nov 6vs WAS | – | +2 | 1 teammate out |
Expected (minutes): what Gist's model expected before that game. Usual (shots): his average shot attempts before that game. Notes mark back-to-back games, teammates out, 8+ points off the model's projection and season highs.
Teammate availability: injury log from Oct 28.
Totals · FG 20-42 (47.6%) · 3P 12-27 (44.4%) · FT 2-3 (66.7%)
- Text
- 12px · tabular figures
- Values
- Right aligned
- Phone
- Scroll for the full line
Rules and code
Use a table to compare complete numeric lines. Keep a ranking row for a single-metric list.
Existing rules
- Use shared body size and tabular numbers. Table weights are contextual: Sheet forecasts and past-game results, and Projection forecasts, use 500; Sheet minutes/context and Model record use 400. The generic 600 value role does not override these tables. Keep labels and source notes readable.
- Projected and observed table values use the same body size. Projection retains its forecast-row label, weight and fill. Sheet uses an aligned forecast column and 500-weight values without a row fill.
- Retain full fields, exact player/game destinations and a named keyboard/touch scroll region.
- Distinguish observed games, season averages and model projections. Missing values stay unavailable.
Projection context
The log starts at the work-area edge. Factors sits in a 208px column to its right with twice the shared section gap; phone and narrow guide containers put Factors below the log. Expected minutes uses a contextual 20px figure; projected table cells remain body-sized.
Inspecting an observed game opens detail directly beneath its row, one at a time, with a 44px minimum target and focus retained on the opener. Last 3/5/10/Season changes the comparison only. Factors reads the position saved with the forecast; missing position or source cutoff stays explicit.
Blue raises and amber lowers the estimate, in the named statistic’s units. All bars share a symmetric scale within the panel. A missing term is Not used; a used term that rounds to zero is Near baseline. These are contributions, not allowance percentages or a defensive-strength rating.
Sheet comparison meaning
A projected stat is the player’s expected game total. Opponent allowance is a rate per 36 opponent player-minutes for the same G/F/C group. Its percentage compares that rate with the league position average; it is not a time trend or a percentage adjustment to the forecast. Rank 1 allows the most. Limited and missing data receive no directional color. The separate model contribution is in the detail; 3PM allowance is context only.
Projected belongs in its stat column header. Expanded history compares projected and actual game values with L3/L5/L10; Saved and Replay stay visible. Its Allowed column leads with vs expected, the schedule-adjusted strength (what the defense allowed against these players’ own norms), and keeps vs league in the grey line. Both use results before that game; neither is today’s rating or an individual defender rating. Replays use the current model and are excluded from Model record.
Keep contextual
Current detail and Stat sheet remain selected for their own tasks. The specimen demonstrates Current detail; it does not make its cell widths the default for every table.
Player and Team tables
Use the existing StatTableFrame for the hoops Recent form, Game log and silent-stat tables. Keep its native table, caption and named scroll region. Phone fades and direction hints follow measured hidden columns; a fitted table has no cue, and final values stay unfaded at the right edge.
Player puts Per game and the game count beside the headings while retaining accessible captions. This does not change other table families’ captions or widths.
Keep contextual
Current detail and Stat sheet remain selected for their own tasks. The specimen demonstrates Current detail; it does not make its cell widths the default for every table.
Adoption and open work
Current adoption
Current detail is shared by Search, Home’s game-log answers (since Nov 12, through search/ui/quiet/ObservedLog.tsx) and the existing guide/prototype consumers. Search and this specimen use a full average row with exact game count; per-game model context is in Game context. Other existing consumers retain their footer/column defaults. Stat sheet is shared by actual Home and Search. The separate StatTableFrame serves Player box score, silent stats and game log, plus Team recent form and game log. Its scroll cues are locally adopted; Player-only caption grouping and 11px percentile markers do not migrate Team typography.
Open · projection order on phones
Phone font parity (R09) and numeric roles (R10) were decided on Nov 12 and applied. The full projection page still puts factors before the stat line on phone (R72).
Done when: agreed shared rules and exceptions are documented, applied to named consumers and checked on desktop and phone.
Implementation and checks
<PlayerGameLog player="Aaron Wiggins" games={3} selection={currentSeasonGames} summary="row" context="disclosure" />Verify the displayed sample and averages match the requested games; preserve internal scrolling, row inspection, profile/game links and source dates after reload.
proto/hoops/shared/StatTable.tsxproto/hoops/player/PlayerSnapshot.tsxproto/boxlog/PlayerGameLog.tsxproto/players/projection/Factors.tsxcomponents/nba/true-shooting/TrueShootingLeaders.tsxproto/games/PlayerProjections.tsx
In Gist
| Game | Result | MIN | PTS | REB | AST | STL | BLK | TOV | FG | 3P | FT | +/- |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Average10 games | – | 25.6 | 14.4 | 4.0 | 2.8 | 0.7 | 0.4 | 1.3 | 5.6/12.943.4% | 3.0/7.639.5% | 0.2/0.450.0% | +1.3 |
| Nov 9vs LAL | L114-124 | 23 | 15 | 3 | 3 | 0 | 0 | 3 | 5-11 | 4-7 | 1-1 | -11 |
| Nov 7vs BKN | W124-121 | 26 | 20 | 4 | 6 | 1 | 0 | 0 | 7-19 | 5-13 | 1-2 | +12 |
| Nov 6vs WAS | W139-132 | 29 | 19 | 4 | 3 | 0 | 0 | 2 | 8-12 | 3-7 | 0-0 | -4 |
| Nov 4@ WAS | W120-105 | 29 | 15 | 4 | 2 | 2 | 1 | 2 | 6-14 | 3-7 | 0-0 | +15 |
| Nov 2@ IND | W108-104 | 24 | 7 | 3 | 2 | 1 | 0 | 2 | 3-12 | 1-6 | 0-1 | +7 |
| Oct 31@ NOP | L99-121 | 23 | 10 | 2 | 1 | 1 | 1 | 2 | 4-11 | 2-7 | 0-0 | -7 |
| Oct 29vs CLE | W113-111 | 24 | 20 | 7 | 3 | 0 | 1 | 1 | 8-14 | 4-7 | 0-0 | +17 |
| Oct 28vs MIA | L117-129 | 24 | 10 | 4 | 3 | 0 | 0 | 1 | 4-11 | 2-7 | 0-0 | -2 |
| Oct 26@ HOU | L110-120 | 27 | 12 | 4 | 3 | 1 | 0 | 0 | 5-12 | 2-6 | 0-0 | -12 |
| Oct 24vs HOU | L114-121 | 29 | 16 | 5 | 2 | 1 | 1 | 0 | 6-13 | 4-9 | 0-0 | -2 |
Game context
| Game | Min vs expected | FGA vs usual | Notes |
|---|---|---|---|
| Nov 9vs LAL | – | +1 | 1 teammate out |
| Nov 7vs BKN | – | +9 | 1 teammate out · Season high |
| Nov 6vs WAS | – | +2 | 1 teammate out |
| Nov 4@ WAS | – | +4 | 1 teammate out |
| Nov 2@ IND | – | +3 | 1 teammate out |
| Oct 31@ NOP | – | +2 | 1 teammate out |
| Oct 29vs CLE | – | +5 | 1 teammate out · Season high |
| Oct 28vs MIA | – | +2 | 1 teammate out |
| Oct 26@ HOU | – | +3 | – |
| Oct 24vs HOU | – | +5 | – |
Expected (minutes): what Gist's model expected before that game. Usual (shots): his average shot attempts before that game. Notes mark back-to-back games, teammates out, 8+ points off the model's projection and season highs.
Teammate availability: injury log from Oct 28.
Totals · FG 56-129 (43.4%) · 3P 30-76 (39.5%) · FT 2-4 (50.0%)
Controls
Ask, filter and switch views while keeping the context.
Search
- Focus
- Ink underline
- Input
- Body 12px · 16px touch · 44px target
- Submit
- Enter opens Search
Details & sources
Aaron Wiggins averaged 14.4 points, 4.0 rebounds, 2.8 assists over the last 10 games, Oct 24 to Nov 9 (2026-27).
Aggregate · balldontlie.io /v1/stats (player box scores; 0-minute DNP rows dropped). Regular season by balldontlie's own postseason flag, which excludes the playoffs but not the play-in tournament — a few play-in nights beyond the NBA.com game logs' cutoff may be included., 2026-27, retrieved 2026-11-11. Regular season.
Source notes: 240px on desktop, up to 320px stacked. In Search, the gap is 152px at 1920px and 72px at 1440px.
View the spacing in SearchQuadrants · section links
Make your own leads in ink; the other sections follow in grey; the current one is underlined. In the app, a section on another page is a real link.
Quadrants · data fields
Aaron Wiggins · 2026-27
| Game | Result | MIN | PTS | REB | AST | STL | BLK | TOV | FG | 3P | FT | +/- |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Average3 games | – | 25.8 | 18.0 | 3.7 | 4.0 | 0.3 | 0.0 | 1.7 | 6.7/14.047.6% | 4.0/9.044.4% | 0.7/1.066.7% | -1.0 |
| Nov 9vs LAL | L114-124 | 23 | 15 | 3 | 3 | 0 | 0 | 3 | 5-11 | 4-7 | 1-1 | -11 |
| Nov 7vs BKN | W124-121 | 26 | 20 | 4 | 6 | 1 | 0 | 0 | 7-19 | 5-13 | 1-2 | +12 |
| Nov 6vs WAS | W139-132 | 29 | 19 | 4 | 3 | 0 | 0 | 2 | 8-12 | 3-7 | 0-0 | -4 |
Game context
| Game | Min vs expected | FGA vs usual | Notes |
|---|---|---|---|
| Nov 9vs LAL | – | +1 | 1 teammate out |
| Nov 7vs BKN | – | +9 | 1 teammate out · Season high |
| Nov 6vs WAS | – | +2 | 1 teammate out |
Expected (minutes): what Gist's model expected before that game. Usual (shots): his average shot attempts before that game. Notes mark back-to-back games, teammates out, 8+ points off the model's projection and season highs.
Teammate availability: injury log from Oct 28.
Totals · FG 20-42 (47.6%) · 3P 12-27 (44.4%) · FT 2-3 (66.7%)
- Role
- Game window
- Target
- 44px minimum
- Selection
- One active underline
Rules and code
Use content tabs when the same subject changes view. Use choices for settings, native filters for data scope and links for another destination.
Every NBA dropdown is an OverlaySelect (R68, Nov 12): a 12px value, with a caret for filters and pickers or an underline for a word in a sentence, over a native select that keeps 16px on phones, so iOS doesn’t zoom. The caller’s box class styles the visible value.
Setting a view’s scope
A sentence of underlined words when scope reads as one line (Stats: “Showing 2025-26 season total, no minimum”). A Filters trigger with a sheet when there are many filters (Quadrants Questions). Labelled fields, label above, for a view’s own few controls (Make your own, Recent changes, the full builder, Breakouts); a lone page-level filter may keep its label beside it (Players).
Quadrants section links
One quiet line on every Quadrants view. Sections on another page are links; sections that switch this page stay buttons. Keep the order, 44px targets and the phone scroll that starts at the current section.
hoops/quiet/cd3/quadrant-sections.tsxLinks to a player
Beside a named player, the link reads “Player stats →”. In-app links end with →; ↗ only for a new tab or another site. Names inside tables are the link themselves.
Search field and source notes
Reuse QuestionField for the native question and SourceDetails for optional evidence. Field focus strengthens the bottom rule; keep the Ask button’s own keyboard ring. SourceDetails owns its compact measure, accent book icon and native disclosure. The answer layout owns its half-distance placement, not the component.
Use the body size (12px) in the field on desktop and 16px on phone or touch screens, 44px targets, Enter to ask and Shift+Enter for a line break. Source icons are hidden from assistive technology because the label names the action. Keep source dates, definitions and follow-ups intact.
components/nba/search/QuestionField.tsxcomponents/nba/search/SourceDetails.tsxExisting rules
Keep each control’s current semantics, visible focus, touch target and URL behavior. Game windows use a labelled group with pressed buttons. Content tabs retain their own tab semantics and scrolling behavior.
Dropdown values and labels use 12px regular text. Native fields use 16px on phone or touch screens to prevent zoom. Keep a 44px minimum target; selected custom options use medium weight.
Adoption and open work
Shared content-control roles are locally adopted across the documented Home, Games, Last night, Stats, Quadrants and player/projection consumers. Review remaining instances by role; do not turn navigation links into local toggles.
Done when: each named consumer uses the correct role and its keyboard, touch, scope and persistence behavior is checked.
Implementation and checks
<div role="group" aria-label="Game window">
<button aria-pressed={selected}>Last 3</button>
</div>Change the window and inspect the visible rows. Use Tab, Enter and Space to change the window. Check narrow-width containment.
proto/design/sections/ComponentFamilies.tsxproto/foundation/controls.module.cssIn Gist
| Game | Result | MIN | PTS | REB | AST | STL | BLK | TOV | FG | 3P | FT | +/- |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Average3 games | – | 25.8 | 18.0 | 3.7 | 4.0 | 0.3 | 0.0 | 1.7 | 6.7/14.047.6% | 4.0/9.044.4% | 0.7/1.066.7% | -1.0 |
| Nov 9vs LAL | L114-124 | 23 | 15 | 3 | 3 | 0 | 0 | 3 | 5-11 | 4-7 | 1-1 | -11 |
| Nov 7vs BKN | W124-121 | 26 | 20 | 4 | 6 | 1 | 0 | 0 | 7-19 | 5-13 | 1-2 | +12 |
| Nov 6vs WAS | W139-132 | 29 | 19 | 4 | 3 | 0 | 0 | 2 | 8-12 | 3-7 | 0-0 | -4 |
Game context
| Game | Min vs expected | FGA vs usual | Notes |
|---|---|---|---|
| Nov 9vs LAL | – | +1 | 1 teammate out |
| Nov 7vs BKN | – | +9 | 1 teammate out · Season high |
| Nov 6vs WAS | – | +2 | 1 teammate out |
Expected (minutes): what Gist's model expected before that game. Usual (shots): his average shot attempts before that game. Notes mark back-to-back games, teammates out, 8+ points off the model's projection and season highs.
Teammate availability: injury log from Oct 28.
Totals · FG 20-42 (47.6%) · 3P 12-27 (44.4%) · FT 2-3 (66.7%)
Back links from a projection
A projection returns the reader to where they came from (R44, Nov 11). The label names the place; the link keeps its filters.
Examples use Andre Drummond’s next projection. Each value is checked before it reaches the link, so an unknown origin falls back to Games. Switching players keeps the same origin.
Owners: players/projection/page.tsx chooses the destination; player-navigation.ts carries the origin when switching players; ProjectionFrame.tsx renders the link. Contract: docs/design/nba-projection-rules.md.
Team tiles
Identify a team or matchup without repeating its name.
- Identity
- Approved team tile
- Colour
- Team identity only
- Use
- One mark per identity
Rules and code
Reuse TeamLogo. The public-build fallback carries its own abbreviation; do not repeat it beside the tile. Team colours identify teams, not good/bad performance.
Keep contextual
Aligned is the selected game-tile anatomy. The caller owns the link, selection and layout; a team mark is not itself a navigation pattern.
Adoption and open work
TeamLogo is the shared identity owner. Aligned game tiles are locally adopted in Home, Games and Search. Check remaining call sites for repeated abbreviations, spacing and destination clarity.
Implementation and checks
<TeamLogo abbr="CLE" />
<GameRow away={away} home={home} status={status} />Verify public-build fallback, readable labels, actual destinations and selected/final/upcoming states.
components/nba/team-logo.tsxproto/games/breakdown/GameRow.tsxIn Gist
2026-11-13 · actual loaded matchup.
Data states
Say where a number comes from and how current it is. When data is missing, say so and point to the next useful place.
Live projection
Derived · Conditional on playing. Injury snapshot Nov 12, 4:26 PM EST.
No current injury report covers this game. Injury adjustments are not applied; availability is unconfirmed.
A published snapshot covers the game. Derived says it is the model's estimate; Observed names the box scores and their dates.
Saved before tip
Derived · Conditional on playing. Injury snapshot Nov 10, 6:35 AM EST.
Saved before tip · Nov 10, 6:35 AM EST
After tip, the page shows the forecast saved before the game, with its issue time. Only a saved entry carries this label; the built-in model never does.
Stale injury report
Derived · Conditional on playing. Injury snapshot Nov 10, 6:35 AM EST.
Injury report is out of date. Injury adjustments are not applied; availability is unconfirmed.
The report is older than six hours. Numbers drop the injury adjustments and say availability is unconfirmed.
Update timing
Loading update times…
Lists that refresh say when they last updated and when to expect the next one: hourly, every 5 minutes within 90 minutes of a tip.
No live data
Leaders use today’s saved projections, which haven’t been published yet.
Projected players by points →Nothing is published. Leaders explains and links to the bundled projections; Games, Projection and Home fall back to the built-in model, and the Matchup Sheet link is hidden.
Request failed
Tonight’s projections are unavailable right now.
A fetch failed. Say so in place of the content; never leave a loading message running.
Can't answer
“stephen curry clutch shooting”
I couldn't find an NBA stat in that question. Try asking about Stephen Curry's points, assists, rebounds, steals, or deflections.
Player stats →Search can't answer the question. One grey line gives the reason, Player stats links to the named player, and each recovery row previews its answer, the likeliest in ink. Every row is checked against the engine, so none is a dead end.
Waiting for the outlook
Loading the latest outlook…
Views read from the season outlook (Stats › Outlook, outlook and standings answers) wait for the latest one: a loader, then the numbers, never older numbers that change. After 4 seconds they settle on the bundled outlook (R67, Shane's pick, Nov 12).
- Sources
- Live snapshot, built-in model, observed box scores
- Labels
- Derived, Observed, Saved before tip, Updated
- Missing
- Say so; link to the next useful place
Rules and code
Sources
- Live snapshot. This Mac’s worker publishes to the private
nba-runtimebucket, hourly and every 5 minutes near tips. Games, Leaders, the Matchup Sheet, Projection and the Model record read it. - Built-in model. Bundled with each deploy. Used whenever no snapshot can be read; it never claims a saved forecast.
- One slate. Home’s board and Tonight strip, Games › Players and projected-leaders answers all read
/proto/games/slate-data: live when published, otherwise built-in, with “if he plays” numbers and the Questionable label (R47).
Labels
- Derived · a model estimate; it states its conditions (“Conditional on playing”).
- Observed · recorded statistics, with the last game date and retrieval time.
- Saved before tip · only for a forecast saved before the game, with its issue time.
- Updated · Next for lists that refresh; “Next update overdue” when a publication is late.
Missing data
- Say what is missing in one sentence and link to the closest useful place. Never show zero counts, empty filters or a retry that cannot succeed.
- A failed request says the data is unavailable right now. A past date says projections cover today and later.
- Search that can’t answer gives its reason in one line, a player link when it found a player, and recovery questions it has checked will answer (R63).
- Data that arrives after the page opens and would change numbers already shown (the season outlook) waits with a loader instead (R67).
- Dates and “Today” use Eastern time on both server and browser.
Code
search/engine/model/live-data.server.ts,live-slate.server.ts,static-data.server.tsgames/slate-data/route.ts,components/nba/tonight-leaders/data/slate-source.tsplayers/projection/ProjectionEvidence.tsx,search/engine/model/availability.tsgames/slate-leaders/SlateLeaders.tsx(UpdateTiming,LeadersUnpublished)search/ui/quiet/QuietDecline.tsx,decline-recovery.ts
Checks: scripts/nba-live/production-check.mjs runs on GitHub twice a day and fails when production is not on live data, the last publication is over 3 hours old, or a key page is blank (R51).
Work queue
Review one family at a time, then apply its rules to the pages that use it.
- Existing implementations
- Inconsistencies
- Rules
- Application
- Page refinement
Refine a page first when that helps decide the rule. Bring the result back here before applying it elsewhere.
Open nowNov 12
Choices for Shane
- R72: on phones the projection page shows factors before the stat line.
Decided Nov 12: R37 Games’ lists share the Leaders identity, Home keeps its compact board; R38 Home ends with the same “Ask next:” line as an answer; R39 a player-only question answers with his season and next game; R09 phone type matches desktop; R10 number weights stay as they are; R11 focal figures load a real 300 weight. Then, from the built pages: Home’s game-log answers read as Search’s, and Search’s ranked list and rail sit in their columns. From the R68/R69 prototype: every dropdown value reads 12px (R68), and Search’s follow-ups are rows in the rail (R69).
Settled fixes not yet done
- R70: the design-system check fails, on main too. It measures the Player page before it finishes loading.
Beyond the design system
- R52: run the test suites before merges. R53: state each NBA screen’s purpose and how to tell a change helped. R54: the public site still uses
/protoaddresses marked as prototypes.
Everything below is live on www.usegist.ca (PRs #9–#17, Nov 11–12). The settled fixes R31–R33, R55 and R62–R64 were done on Nov 12. The single prioritized backlog is docs/design/nba-table-fonts-review.md.
Page hierarchyBeside applied · Player accepted
Stat detail and Player stats
Stats first and Compact are selected. Desktop percentiles sit beside Recent form, with clearer family breaks. The player picker stays beside the name. Phone shows recent averages, open percentiles, then the game log. Longer explanations stay in Details & sources. The loaded-sample ranking check stays in place.
- View Stat detailReview the current-sample state and completed-season ranking.
- View Player statsReview the primary stat tables, supporting percentile column and optional notes.
- View Player stats on phone (new tab)Real responsive preview of the actual page, with open percentiles and direct links to the observed tables.
Search · Beside
Beside is selected and applied locally. Search and Player share the same adaptive detail spacing. Game logs lead with the full selected-game average row; Game context and Details & sources keep deeper material optional. Source notes use a narrower 240px desktop measure with half the earlier far-edge gap, with a small book icon and no closed-state divider. The player title opens Player stats. The existing team mark and full team name identify the games shown; multi-team windows name each team. These refinements are accepted. Controls now demonstrates the actual search field and source disclosure.
- View refined SearchThe isolated points headline is replaced by the complete average line. The old comparison links reach this same source.
- Review Search controlsLive shared question field, source disclosure and accepted rules.
- Compare canvas materialsFour local treatments using the same Search answer.
The pinned Game cells follow the page canvas. Search-field focus uses the bottom rule rather than a rectangular outline. Colour now compares the current paper, smooth paper, white and white paper. The app default stays as it is until a direction is chosen.
Player accepted
The lower supporting panel, Compact rows, family separators and 11px markers are accepted. Foundations review preserves that composition and improves touch, spoken labels and recovery. The accepted reading polish makes table-edge cues match actual hidden columns and exposes comparison details over the chart bar. The local care pass lets the latest player selection load immediately, ignores superseded responses and returns retry focus to the picker. Rules, source and desktop/phone checks are reconciled; no Player design choice remains in this review. The old eight-way comparison is retired; its bookmarks reach the actual page. Search and Quadrants remain in R15; external image-delivery verification remains open in R17.
Stat tables and projectionsDesktop rules applied
Applied
The ten submitted desktop choices are reflected in their named consumers: six changes and four retained values. Phone and coarse-pointer font choices remain separate.
- Player box-score headers: 12px, regular. Player percentile markers: 11px, semibold on desktop; labels and values stay 12px.
- Observed game-log headers and dates: semibold. Projection headers stay 12px, regular.
- TS% and Games player names: 12px. Games headers stay regular.
- The affected table and panel spacing follows the 4px grid. Guide specimens use the same NBA scope as the actual components.
Named consumers
- Player statisticsBox-score headers and percentile bubble numbers; team fonts retain their existing treatment.
- Player game logCurrent detail, shared by Search and guide examples.
- TS% Stat sheetShared by Home and Search; complete season averages stay available.
- Games player linesSeparate statistics and projection links remain.
- Player projectionContext: game log first, Factors on the right on desktop and below on phone; Ink pill game windows.
- Games › PlayersEvery projected player for the date, sortable, with a Projection link per row.
Projection and Matchup Sheet
View Projection with Ink pill controls. Context is selected for Projection.
Expand row is adopted in the Matchup Sheet. Open a player’s evidence in place; game details stay behind the info control.
Decided Nov 12
- R09: phones use the desktop export (TS% and Games names 12px, player markers 600, box-score headers 400).
- R10: as now. Ranked values 600, full stat lines 400, the projected game line 500.
- R11: focal figures render the 300 weight they request; Hanken Grotesk 300 is loaded.
The single prioritized backlog and rule owners are in docs/design/nba-table-fonts-review.md. These decisions do not reopen the settled fixes.
Verification and release
Run npm run check:design-system for specimen parity, font scope, dataset semantics, spacing and editor persistence. The written record keeps the final checks and remaining limits.
These rules are live. A font export does not publish anything by itself.
Data statesNew · Nov 12
Existing implementation and rules
Live, built-in, stale and missing data each have one look: Derived, Observed, Saved before tip and Updated labels, and a sentence plus a link when data is missing. The specimens are the real components.
Remaining review
Review the section with Shane, then check each page against it.
Matchup SheetExpand row · live
Existing implementation and rules
Guide and Sheet share under-player context, expandable evidence, compact Ink controls and game-info detail. Labelled states use the same row.
Remaining review
The 32px team gap stays as a named exception to shared Section spacing (Shane, Nov 11).
Hybrid score and Model recordAccepted · live
Existing implementation and rules
Games keeps its compact score pair. Model record leads with the saved-forecast chart Shane chose on Nov 11; the rest sits in dropdowns. Real components and labelled state fixtures are available under Tables and Controls.
Remaining review
Keep the update time and sample visible and the report’s width, grouping and numeric emphasis contextual. Watch the chart on phones as more nights are graded.
Type and spacingScoped rules applied
Existing implementation and rules
Quiet supplies the shared defaults. The submitted desktop table choices are applied to their named consumers; guide specimens inherit the same NBA rules.
Remaining review
Phone font parity, untouched numeric roles and the focal figure weight remain open. Legacy spacing outside this table/control unit is not claimed migrated.
Layout and navigationScoped rules applied
Existing implementation and rules
Reading, Icons and Question first define the shared frame and search proportions.
Remaining review
Review internal page composition separately. The shared frame does not settle every panel’s width or order.
ControlsScoped rules applied
Existing implementation and rules
Dropdown values and labels read 12px at every width (R68, Nov 12): each NBA dropdown is an OverlaySelect, with the native select underneath kept at 16px on phones so iOS doesn't zoom. 44px targets. Content tabs, dataset windows and filters keep separate semantics.
Remaining review
Dropdown sizing is applied to the named NBA consumers. Page hierarchy is the current priority; the tabs and segmented-selector inventory remains available for later review.
Charts and stat rowsScoped rules applied
Existing implementation and rules
Quiet chart rules cover the named ranking bars and comparison charts.
Remaining review
Inventory other chart types before applying the same geometry or emphasis.
Team and game tilesScoped rules applied
Existing implementation and rules
Approved team tiles and Aligned game tiles are shared by their documented consumers.
Remaining review
Check identity, spacing and selected states in context; retain each destination.
Player rows and linksScoped rules applied
Existing implementation and rules
Current row is selected for One matchup. Games and roster tables retain explicit statistics and projection links.
Remaining review
Keep contextual destinations clear; review other row families before changing their behavior.
When an item is finished
Its rule names the default, exceptions and real consumers. Those consumers are checked on desktop and phone, with remaining differences recorded.
The states above are live on www.usegist.ca as of Nov 12; open work is listed under Open now.
Foundations
Shared type, spacing and emphasis for Gist’s interface.
- Body
- 12px
- Headings
- 14px · 500
- Stat marks
- 2px
Rules and code
In Gist
2026-11-13 · actual shared game tile.
Charts
Compare players with shared axes, marks and context.
Minutes & deflections
Deflections / 36 distribution
Observed · 2026-27 · 150+ minutes · balldontlie tracking averages · retrieved 2026-11-03. Both previews use these same 127 players.
- Context points
- 4px
- Guides
- 1px · dashed
- Labels
- 12px
Rules and code
Current chart preset: Quiet. Presentation changes keep the same data and scales.
Selected players keep their accent and emphasis. Labels stay at 12 px. Scales, values and uncertainty ranges keep their meaning.
Every focal chart ends with one actions row: Save image, then Copy link (a full address). Quadrants uses it on every view, Make your own and Recent changes included.
Adoption and open work
Home and Search distribution strips, plus the Quadrants builder, shared scatter and poster export. Axes, reference guides and context points share presentation roles. Percentile panels, time-series charts and forecast bands keep their existing styles in this pass. The Model record’s in-range track (Nov 11) is its own chart type: a Gist red point per stat on a 50–100% axis, with a pale-green on-target band and a dashed 80% aim drawn with the guide token. It keeps local styles in games/hybrid.module.css and does not use the shared reference class.
Quadrants Recent changes and Make your own’s Combine plot use the same axis, reference and context-point roles. Recent changes keeps one fixed scale for both five-appearance windows and draws reference lines at the qualified group’s earlier medians. Both are Quadrants views, not new shared chart variants.
In Gist
Search answer · distribution strip
Principles
Gist helps people see the game inside the game.
Composition
- Statistic firstThe answer or number comes before any explanation or chrome.
- One focal work areaEverything else on the screen stays visually quieter.A newcomer identifies what to compare without prompting.
- One clear reading pathKeep the answer dominant. Accepted Player and Search layouts may place quieter context beside it on desktop.
- No eyebrows or overlinesNo kickers, uppercase overlines or category badges above titles.
- Only controls that matterA control that can’t change the answer isn’t shown.
- Faces earn their placeOnly where identity is the point. Never decoration in a table.
Quality
| Facet | What it means |
|---|---|
| Legible | People can read values, signs, units, labels, and source context at ordinary size and with enlargement. |
| Revealing | A comparison makes a previously hidden relationship apparent. |
| Grounded | The interface makes the limits of the evidence understandable. |
| Inviting | A newcomer can begin without decoding a dashboard or acronym glossary first. |
| Responsive | Manipulation feels connected to the result, with a clear resting state and recovery path. |
Truth, accessibility, privacy, and reliability are release gates, not facets that can be traded away for a higher aesthetic score.
Frame
Scaled from 1440px (page and phone are not to the same scale as each other).
| Rule | Value |
|---|---|
| Reading and navigation | At 1440px: reading starts at x=280; the sidebar is 160px. Home, Search and this guide share that reading measure. |
| Text column | 640px. Titles, sentences, controls, lists and notes sit here. |
| Data work area | 880px, starting at x=280. Charts and full tables retain their contextual width and rail clearance. |
| Wide data (Quadrants) | Starts at the same x=280 and extends right: 1128px at 1440px, up to 1150px on wider screens. Every NBA page shares one left edge. |
| Phone | 16px side gutter, one column, no horizontal page scroll. A wide table scrolls inside its own wrapper. |
Truth and copy
Every quantitative element carries one evidence word, first, before the claim: Observed, Aggregate, Derived, Simulated, AI-generated. Projections show their range and their basis. UI copy has no em dashes and no developer words. See docs/design/copy.md.
One system
Baseball and NBA were two systems through 2026-10-31. On 2026-11-01, Shane approved folding baseball onto NBA’s own foundation (docs/design-intelligence/PRDs/mlb-to-nba-system.md, “Go ahead” on all 7 decisions). What moved, and what is still open:
| Moved 2026-11-01 | What changed |
|---|---|
| Old stage retired | The ?view=outings/?stage=/?row=/?gist= routes don’t migrate; the new LandingHome is the one home. |
| All sans | Serif (GT Alpina/Georgia) drops entirely: subject names, metric symbols, lesson questions and the footer tagline move to Hanken Grotesk. No editorial exception kept. |
| NavPillCombined everywhere | Replaces the Glass pill, baseball-wide. The phone dock (Raised, fixed bottom nav) is removed, not replaced; NBA’s own collapsed-pill pattern is the one mechanism now. |
| Eyebrows removed | Uppercase tracked filter labels, footer nav labels, step labels and section eyebrows: drift to fix, independent of the rest of the migration. |
| Measurement role split | figure when the number is the page’s own focal readout, value everywhere else. 11px chart ticks stay the one exception to the 12px floor. |
/proto/home?sport=mlb retired | The dev exploration picker, same as the /preview/* pages. |
| Chart tokens join the foundation | The four chart-only tones had no NBA equivalent; see Color below. |
| Still open | Detail |
|---|---|
| 11px chart ticks | Decided as an exception to the 12px floor, but scripts/check-type-scale.mjs has no chart-tick allowance yet, and doesn’t scan baseball’s own directories either way. |
| RELEASE / PLATE chart labels | Still an uppercase tracked eyebrow (pitch-opening.tsx, 13px, 1.5px letter-spacing) despite the eyebrow-removal decision above; not yet touched. |
| MLB team logos | Loaded at runtime on /pitches (no file committed), but still an uncleared trademark. Shane is raising it with the rights holder before any deploy (nba.md §5). |
Typography
Hanken Grotesk for Gist’s interface, player names and numbers.
The game inside the game.
Hanken Grotesk · 400 / 500 / 600 loaded
Player statistics
A clear answer starts with the question.
Keep the player, sample and date in view.
Aggregate · 2026–27 · type sample
0123456789
12.3
Rules and use
- Use the shared role for the text’s job. Page and section headings can share a size while keeping their own heading levels.
- Keep labels, sources and units readable. Use tabular numbers for values people compare.
- Table values follow body size with contextual emphasis: Sheet and Projection forecasts use 500; Sheet minutes/context and Model record cells use 400. The 600 standalone value role is not a blanket table weight.
- The figure setting is a ceiling for a focal statistic, not a size for every number. The numerals above are type samples.
- Dropdown values read 12px at every width (R68, Nov 12); the native select under each keeps 16px on phones so tapping never zooms. Typed inputs keep 16px on phones. Targets stay 44px.
Guide prose keeps its own 14px reading size. The specimens use the current NBA rules, including an existing browser draft. This page does not change product defaults.
Implementations and inconsistencies
- Shared roles
proto/foundation/foundation.module.cssowns the role classes;lib/nba/design-rules.tsowns selected defaults and browser overrides.- Current type
- Quiet selects 12px body, 14px headings, 500 heading weight and a 28px figure cap. The old 14/16/600 scale is a baseline, not the current product default.
- Figure weight (R11, Nov 12)
- Focal figures use 300. The root loader declares Hanken Grotesk 300, 400, 500 and 600, so the 300 the figures request is the weight they render (before Nov 12 they fell back to 400).
- Desktop table adoption
- Player box-score headers and Games names use 12px/400. Player percentile markers use 11px/600; observed game-log headers/dates use 600. TS% uses 12px/500. Projection/Games headers and regular percentile values retain their existing weights. Since R09 (Nov 12) phones use the same values.
- Dropdown sizing applied
- View rules and consumers
- Number weights (R10, Nov 12)
- As now: ranked values 600, full stat lines and percentile values 400, the projected game line 500 (12px at every width). Team fonts keep their treatment.
- Contextual sizes
- Navigation, native inputs, full game logs and projected lines retain their component rules. Review their actual rendered text before changing a shared role; one specimen does not prove every caller is aligned.
Source owners: src/app/layout.tsx, src/app/proto/foundation/, src/lib/nba/design-rules.ts and docs/design/nba-shared-rules.md.
November 1 baseline and special roles
Historical reference. The route counts predate Quiet and are not a current adoption audit.
Every size below is Home’s own measured role, enforced by a 12/14/16/20px scale plus one 28–56px figure.
Page title
Section title
Subheading
Body text, fourteen pixels.
Secondary text, de-emphasized grey.
Caption text, grey, twelve pixels.
47.2
| Role | Element | Size / line / weight | Routes |
|---|---|---|---|
| Page title1 | h1 · .title | 16/24/600 | - |
| Section title | h2 · .section | 16/24/600 | 5 of 19 |
| Subheading | h3 · .subhead | 14/21/600 | 6 of 19 |
| Body | p · .body | 14/22.4/400 | 3 of 19 |
| Secondary2 | .secondary | 14/22.4/400 | 3 of 19 |
| Caption | .caption | 12/18/400 | 12 of 19 |
| Figure3 | .figure | 28-56 / 300 | - |
Special text
| Role | Size / weight / colour | Used for |
|---|---|---|
rank | 12/400/grey, tabular-nums | A rank number (dot rows, leaderboards, Stats bar rows) |
value | 14/600/ink, tabular-nums | A stat value in a row |
unit | 12/400/grey, +0.02em letter-spacing, no tabular-nums | A trailing stat-unit suffix (“PTS”) after a value |
meta | 12/400/grey | A team abbreviation used as metadata beside a name, not a row's own title |
tag | 12/400/grey, nowrap | A compact inline status tag (“Questionable”) |
linkPrimary | 14/ink, underlined, faint to ink on hover/focus | A primary inline action link (“Try: question”) |
linkSecondary | 12/grey, underlined, to ink on hover/focus | A secondary nav link (“See all”) |
tab / tabActive | 14, 500 at rest, 600 active; grey to ink | A tab label; tabActive is added alongside tab on the active tab only |
date | 14/400/grey, tabular-nums | A row-level, prominent date/time (a Games row's own tip time) |
evidence | 12/400/grey block; the evidence word itself 400/ink, never bold | The source-note block an evidence word like “Aggregate”/“Derived” sits inside |
Identity
Which mark to use, and where the one red may appear.
The real mark, as the nav renders itsrc/components/baseball/gist-logo.tsx
The one red
Red can mark the asked-about player or a live state. Selection and keyboard focus follow the Controls contract, including the Sheet’s Ink fill. Brand red identifies a subject, not a performance verdict. The Matchup Sheet has one scoped exception: green means at least 5% more allowed, red at least 5% less, relative to the same-position league rate. Near-average and limited samples stay neutral. This is allowance context, not confidence or betting value. See the allowance tokens in Color.
Wordmark use
GistLogo’s showName prop answers differently on each real surface.
Baseball header/footer, showName true, 38–48px
Room for a name beside the mark.
NBA sidebar, mark + “Gist” + sport, one row
The sport toggle shares the logo row (Nov 10), so the nav’s first row sits level with the page title. Phones keep the toggle in the Menu. See it in Quadrants
Loading
When an NBA page loads, the sidebar stays and the content area shows the mark playing the catch storyboard: Ready, Catch, Settle, Peek. It appears only after 200ms, so quick loads never flash it, and it is still under reduced motion. Data loading inside a view keeps its inline grey text. components/nba/loading/GistLoader.tsx
Team color
A team’s color appears only as a small identifying mark: a chip or a dot next to a name, the thin bar under a game chip. It never fills a background behind text. Every mounted component in this guide that carries a team dot (Meter, Dot rows) uses the real team-color resolver, not a hand-picked swatch.
Color
White paper with Fine grain. Keep data colour meaningful.
| Game | Result | MIN | PTS | REB | AST | STL | BLK | TOV | FG | 3P | FT | +/- |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Average10 games | – | 25.6 | 14.4 | 4.0 | 2.8 | 0.7 | 0.4 | 1.3 | 5.6/12.943.4% | 3.0/7.639.5% | 0.2/0.450.0% | +1.3 |
| Nov 9vs LAL | L114-124 | 23 | 15 | 3 | 3 | 0 | 0 | 3 | 5-11 | 4-7 | 1-1 | -11 |
| Nov 7vs BKN | W124-121 | 26 | 20 | 4 | 6 | 1 | 0 | 0 | 7-19 | 5-13 | 1-2 | +12 |
| Nov 6vs WAS | W139-132 | 29 | 19 | 4 | 3 | 0 | 0 | 2 | 8-12 | 3-7 | 0-0 | -4 |
| Nov 4@ WAS | W120-105 | 29 | 15 | 4 | 2 | 2 | 1 | 2 | 6-14 | 3-7 | 0-0 | +15 |
| Nov 2@ IND | W108-104 | 24 | 7 | 3 | 2 | 1 | 0 | 2 | 3-12 | 1-6 | 0-1 | +7 |
| Oct 31@ NOP | L99-121 | 23 | 10 | 2 | 1 | 1 | 1 | 2 | 4-11 | 2-7 | 0-0 | -7 |
| Oct 29vs CLE | W113-111 | 24 | 20 | 7 | 3 | 0 | 1 | 1 | 8-14 | 4-7 | 0-0 | +17 |
| Oct 28vs MIA | L117-129 | 24 | 10 | 4 | 3 | 0 | 0 | 1 | 4-11 | 2-7 | 0-0 | -2 |
| Oct 26@ HOU | L110-120 | 27 | 12 | 4 | 3 | 1 | 0 | 0 | 5-12 | 2-6 | 0-0 | -12 |
| Oct 24vs HOU | L114-121 | 29 | 16 | 5 | 2 | 1 | 1 | 0 | 6-13 | 4-9 | 0-0 | -2 |
Game context
| Game | Min vs expected | FGA vs usual | Notes |
|---|---|---|---|
| Nov 9vs LAL | – | +1 | 1 teammate out |
| Nov 7vs BKN | – | +9 | 1 teammate out · Season high |
| Nov 6vs WAS | – | +2 | 1 teammate out |
| Nov 4@ WAS | – | +4 | 1 teammate out |
| Nov 2@ IND | – | +3 | 1 teammate out |
| Oct 31@ NOP | – | +2 | 1 teammate out |
| Oct 29vs CLE | – | +5 | 1 teammate out · Season high |
| Oct 28vs MIA | – | +2 | 1 teammate out |
| Oct 26@ HOU | – | +3 | – |
| Oct 24vs HOU | – | +5 | – |
Expected (minutes): what Gist's model expected before that game. Usual (shots): his average shot attempts before that game. Notes mark back-to-back games, teammates out, 8+ points off the model's projection and season highs.
Teammate availability: injury log from Oct 28.
Totals · FG 56-129 (43.4%) · 3P 30-76 (39.5%) · FT 2-4 (50.0%)
Details & sources
Aaron Wiggins averaged 14.4 points, 4.0 rebounds, 2.8 assists over the last 10 games, Oct 24 to Nov 9 (2026-27).
Aggregate · balldontlie.io /v1/stats (player box scores; 0-minute DNP rows dropped). Regular season by balldontlie's own postseason flag, which excludes the playoffs but not the play-in tournament — a few play-in nights beyond the NBA.com game logs' cutoff may be included., 2026-27, retrieved 2026-11-11. Regular season.
Canvas rules and implementation
Fine is the selected paper treatment: a pure-white base with continuous fine grain and small flecks. The app, this guide, phone menus and pinned comparison cells share the same material.
White game tiles remain deliberate surfaces. Stat values, team marks, percentile meaning and focus states retain their existing roles.
Historical token reference
This token inventory records the November 1 baseline. Current NBA pages use the shared selected pure-white Fine paper canvas and grain. Token samples below do not establish the current appearance of every route.
The one red
4.99:1 on canvas · AA
Used on 5 of 19 routes
Search’s accepted source disclosure uses the selected accent on its small book icon. Team colour remains in identifying marks; stat values stay neutral. Controls demonstrates the shared implementation.
Opponent allowance · Sheet only
Green: at least 5% above the same-position league rate. Red: at least 5% below. Expanded history leads with vs expected (Nov 11): the same green and red mark at least 5% more or less than those players’ own earlier norms, and vs league follows in grey. Near-average, limited and unavailable states stay neutral. Text carries the comparison independently of color; forecasts stay ink.
Leader trends · Games Leaders
Green: Last 5 above the season average. Red: below. Threes compares attempts (3PA); Points, Assists and Rebounds compare their own statistic (Nov 11). The season includes those five games. Unchanged, baseline-only, short-sample and missing states stay neutral. Arrows and values carry direction; projected makes stay ink.
- Points up 3.1 per game, last five versus season averageLast 5 above the season average
- Assists down 1.7 per game, last five versus season averageLast 5 below the season average
- Rebounds unchanged per game, last five versus season averageUnchanged at the shown tenth: neutral
- Three-point attempts up 1.0 per game, last five versus season averageThrees compares attempts, not makes
- Points: 3/5 gamesFewer than five recent games: no direction
- Assists: Baseline onlyFive games are the whole season: baseline only
- Rebounds: UnavailableNo recent or season value: unavailable, never zero
Model record chart · Games
The in-range track marks each stat’s Gist forecasts with the one red. A dashed guide marks the 80% aim; a pale-green band marks on target (75–85%). The status word repeats the reading in text: On target in green, Too narrow or Too wide in red, Not graded yet in grey. Typical miss and which way it ran stay ink (Shane, Nov 11).
Projection contributions
Blue raises the estimate; amber lowers it. The signed value is the contribution in the named statistic’s units. Near-baseline and unused terms stay neutral. Text and arrows carry direction independently of color.
Neutrals
17.15:1 on #fafafa, 17.90:1 on #ffffff.
Used on 19 of 19 routes
5.31:1 on #fafafa, 5.54:1 on #ffffff; passes WCAG AA for normal text.
Used on 19 of 19 routes
Chart tokens
Four tones with no NBA equivalent before 2026-11-01, now foundation tokens any chart can use.
Other structural tokens
Team colour and data colours
Small identifying marks only. The accepted team tile carries its abbreviation on the team colour when a logo is unavailable; team colours never mean good or bad.
Historical drift · November 1
Icons
Which icon library and size to use.
Two libraries are both in real production use today, not a choice already made.
Proposed rule
Proposed: Phosphor regular at 14/20px, the library already in more files.
Decision needed: one icon library
Keep Phosphor
Costs changing the real top nav’s search icon.
Keep Lucide
Costs more files: 17 already import Phosphor.
More examples
The existing catalogue, retained as an implementation reference.
These recorded examples include earlier navigation and anatomy. The component-family topics and current family contracts own the defaults; check a recorded difference before treating it as a migration rule.
Navigation
App navigation
Use the one persistent way to reach another NBA section, switch sport and open search: a 160px rail on desktop, a Menu on phones (Nov 7 rail, Icons chosen after; nba-layout-rules.md, App navigation). Don’t use it for a second nav inside a page's own content (Games' view links and Stats' tabs are page navigation), an icon-only label variant, or a new destination; this guide's own topic rail is a separate family.
| Part | Type role / token / spacing |
|---|---|
| Rail | 160px at x=32 on desktop; logo row with the NBA/MLB sport buttons (Home's question field shares it since Nov 11); Design system as a plain footer link |
| Destination row | Home, Quadrants, Stats, Games, Players; 44px tall, 8px radius, 16px Lucide icon at 1.5 stroke, 10px icon/label gap, 12/400 grey label |
| Current row | aria-current="page"; ink label at 500 on a quiet neutral fill (--hair family); icons stay decorative |
| Search | 44px icon button, top right on every NBA page; mounts once |
| State | Value |
|---|---|
| default | grey label and icon, no fill |
| current | ink 12/500 on the quiet neutral row; measured on Games, Nov 11 |
| phone (≤760px) | rail hidden; a 64×44 Menu button opens the same destinations and icons, closes on outside press, focus loss, Escape or navigation, and restores trigger focus on Escape; Search stays 44×44 at top right |
/, /nba/quadrants, /nba/stats, /nba/games, /nba/playersreal, same component; measured on /nba/games at 1512 and 390 (Nov 11)
Can’t mount standalone: the shell (NavPillCombined, which mounts AppNavigation) owns live routing (its sport switch calls next/navigation’s router) and calls its own shell-hiding effect on the same [data-public-baseball]ancestor this guide’s own useHideProductionShellalready manages; mounting both on one page risks fighting over the same element’s inert state, and a click on its sport switch would navigate this guide away. See it live at /.
Stat tabs
Use switching which single stat a ranked list shows, when the list itself doesn't change shape. Don’t use it for switching between unrelated views (that's a route, a section, or the top nav pill's own section row).
Tonight's projected leaders; the real Stat tabs + Dot rows together, as a search answer mounts them (top ten)
Loading tonight’s projections…
| Part | Type role / token / spacing |
|---|---|
| Tab row | role="tablist"; 4px gap; 44px-tall tap targets |
| Tab label | f.subhead-adjacent: 14px/600/21, ink when active |
| Active indicator | ink 2px underline + ink text; inactive tabs are var(--grey) |
| State | Value |
|---|---|
| default (inactive) | grey text, no underline |
| hover | color: var(--ink) (tonight-leaders.module.css:25) |
| focus-visible | 1.5px solid ink outline, 2px offset (tonight-leaders.module.css:26) |
| active/current | data-active attribute: ink text + ink border-bottom + 600 weight |
| loading | "Loading tonight's projections…", 14px grey, no skeleton |
| empty | "No games tonight" / "No games scheduled {date}" via noGamesMessage(), same 14px grey class as loading |
| phone | same tab row, no layout change measured at 390; the 44px targets already fit |
/ (NBA Home) Players boardsame component (tonight-leaders/StatTabs.tsx, one owner since Nov 12); one choice drives both columnssearch answer (option-quiet.tsx, result.slateLeaders)10 leaders per tab; same component/nba/stats (controls.tsx, role="tablist")Drift: Stats' own CategoryTabs; a separate implementation of the same underline-tab pattern, not this component./proto/hoops/quiet/cd3/underline-tabs.tsxDrift: a third, explicitly parallel implementation; its own header comment: "the same visual contract Stats' CategoryTabs uses... not imported from src/app/proto/stats/**... a parallel component tuned to the same numbers." Three real underline-tab components for one job.
Asking
Ask field
Use the one way to ask a free-text question about any NBA season, at the top of a page before any answer. Don’t use it for a filter or a settings control; this submits a new question, it doesn't adjust the current one (see Stats' own token selects for that job).
The real form, a stub nav (this specimen doesn't actually submit)
| Part | Type role / token / spacing |
|---|---|
| Field | Body-size input text, 12px (16px on phone or touch), 44px minimum form, hairline bottom border, no enclosing box |
| Submit | 44×44px icon-only button, arrow glyph, var(--grey) default |
| State | Value |
|---|---|
| default | 1px solid var(--hair) bottom border |
| focus (within) | 2px ink focus outline around the form; the submit button keeps its own keyboard focus outline |
| hover / focus-visible (submit) | color: var(--ink) (default var(--grey)) |
| phone | 16px input text and 44px Ask target; long questions scroll inside the field |
/ (NBA Home)in the logo row above Tonight (Nov 11); the calendar date sits in Tonight's caption/nba/search (NBA)Question first: the shared field
Ask next ("Ask next: …")
Use a handful of real, askable questions: at the end of NBA Home, and directly under an answer. One treatment in both places (R38: Shane chose the line over rows, Nov 12). Don’t use it for navigation (these submit a question in place, they don't change page) or a generic empty-state message.
The real component with Home's own questions (this specimen doesn't submit)
Ask next:
| Part | Type role / token / spacing |
|---|---|
| Line | f.body (14/400/22.4), wraps as one paragraph, not a button row |
| Label | "Ask next:" in var(--grey) |
| Each example | inline text link, underline var(--faint), 12px/4px padding reaching a real 44px tap target without enlarging the type |
| State | Value |
|---|---|
| default | ink text, faint underline |
| hover / focus-visible | underline colour darkens to var(--ink) (redesign.module.css:64) |
| empty | renders nothing (ChipRow returns null when items is empty), not a placeholder message |
| spacing | 32px below the evidence on Home answers (Nov 12) |
/ (NBA Home)after the Players board, before a question is asked/ (NBA Home) after an answerthe same component under the answer, 32px below the evidence/nba/search (NBA)Search shows its follow-ups as their own rail section, above Details & sources (R69, Nov 12), not as this line
Next-game line
Use under a player's season line, when a question only names him: his next game and its projected points, rebounds and assists, one step from the Projection (R39, Nov 12). Don’t use it for any other answer, or as a stand-in for the Projection page's ranges and factors.
The real component for Stephen Curry's next game, styled as under an answer's subject header
Loading the next game…
| Part | Type role / token / spacing |
|---|---|
| Line | body size (12px in the app; the guide shows its own body size) in caption grey, 4px under the subject header |
| Link | "Projection →" in ink, opens the Projection page |
| State | Value |
|---|---|
| loading | "Loading the next game…" while the model loads |
| no scheduled game | renders nothing |
| listed injury | ", if he plays (status)" before the link |
/?q=stephen curry · /nba/search?q=sgaseason-line answers on Home and Search (ObservedLog header)
Sections & headings
Page heading row
Use the one h1 per page, with an optional right-aligned control (a date, a season, a basis) on the same row. Don’t use it for a second h1, or a title styled bigger than 20/600; repo visual default, not page-configurable (PageFrame.tsx's own comment).
PageFrame with a title and a right-aligned control
Home
(page content mounts below the title row)
| Part | Type role / token / spacing |
|---|---|
| h1 | f.title: 20/600/30, ink, letter-spacing -0.01em, measured live on Home |
| Controls | right-aligned page control (Games' date strip, Stats' season); not restyled per page. Home hides its title row and puts the date in Tonight's caption (Nov 11) |
| State | Value |
|---|---|
| default | as above |
| phone | same size; PageFrame doesn't step the title down at 390; nba.md §2's own open question ("Open: Games uses 40" refers to the figure role, not this one) |
Home, Stats, Games, Quadrantsreal, same component
Section header
Use opening a named group of content with a heading, an optional grey caption beside it (a date, a count), and, when there's more to see, one right-aligned link. Don’t use it for a label with no following content, or a heading styled different sizes on different sections of the same page.
The real component, Home's own Tonight heading
Tonight
4 games| Part | Type role / token / spacing |
|---|---|
| h2 (or h3 inside a section) | f.section, ink; page-frame-h2 class (h3: f.subhead) |
| Caption | 12px grey, baseline-aligned beside the heading, 8px gap |
| Right link | f.caption (12/400/18), underline var(--faint), 44px-tall target via padding |
| Row gap | space-between, 12px gap, 8px margin below (redesign.module.css:16); "so 'Today' reads as a heading for the chip strip, not a floating label" |
| State | Value |
|---|---|
| default | grey underline |
| hover / focus-visible | underline + text colour to var(--ink), 1.5px ink outline on focus-visible |
/ (NBA Home)Tonight, Leaders, Projected tonight / Latest results (h3), then the Ask next line/nba/quadrants/last-night, /nba/team, /nba/playerDrift: reuse the global page-frame-h2 class directly (not this HomeHeading component); same type role, independently assembled markup each time rather than one shared component.
Matchup comparison
One matchup
Use comparing both complete team pools in one selected game. Don’t use it for a ranked overview across the entire slate or a multi-stat box score.
| Part | Type role / token / spacing |
|---|---|
| Frame | 1040px comparison module on the shared left edge; 16px phone gutters |
| Chooser | Desktop: quiet, flush shared controlTab buttons; at760px and below: a labelled OverlaySelect, 12px value over a 16px native select (R68), 44px target |
| Team sheets | Two columns above760px; stacked below. Approved tile and full team name once per group |
| Spacing | --nba-group-gap:24/32px between desktop teams; two-thirds on phone; half-gap below the chooser. Identity gaps8px and touch floors44px stay fixed |
| State | Value |
|---|---|
| Selection / resize / reload | One game state/URL feeds both responsive controls; full team pools and selected game persist |
| Missing average | Literal Unavailable; never zero. Source/season and eligibility remain explicit |
| System rules | Body and contextual gaps follow /design. Prototype Spacing overrides only after interaction; Use system spacing clears them |
/nba/search?q=potential assists leaders tonightReal answer, shared renderer and canonical eligible pool/emil-prototype/potential-assists?round=accessComparison route reuses the same component
Player row: Current row
Use a single-metric player list inside an identified team/game group. Don’t use it for replacing the separately accepted Projection action in Games or the player roster.
| Part | Type role / token / spacing |
|---|---|
| Identity | Distinct linked name opens player statistics; preserve accents and44px hit target |
| Value | Shared body/value roles, tabular figures, one aligned value column, Unavailable when missing |
| Secondary context | Game details at group level; projections remain accessible through that game view |
| State | Value |
|---|---|
| Default | Current row is preferred. Inline Projection, Under name and On demand remain comparison references |
| No statistics destination | Plain player name; no fabricated or broken profile link |
/nba/search?q=potential assists leaders tonightAdopted locally; Games/roster explicit links and Home/Stats contextual rows retain their meaning/emil-prototype/potential-assists?round=player-linksComparison route reuses the same selected row renderer
Slate leader rows
Use comparing players across today's games in Assists, Threes, Points and Rebounds. Don’t use it for a single team’s grouped roster, which uses the Current row above.
| Part | Type role / token / spacing |
|---|---|
| Identity | Rank and name/team group vertically centred; 4px between name, matchup and optional status. One 44px minimum identity link |
| Rank spacing | Desktop: 28px rank column + 20px gap. Phone: 24px + 16px. Player heading shares the name’s left edge |
| Row | 8px vertical cell padding; ordinary two-line rows are 61px including divider. Long names and status may grow |
| Phone | Player, projected value and 44px expansion control. Season opportunity, recent results and minutes remain in expansion |
| State | Value |
|---|---|
| All four metrics | One renderer and spacing rule; no Assists exception or separate name-height rule |
| trend | Coloured Last 5 vs season change sits inline with team/matchup: 3PA on Threes; PTS, AST or REB on the other lists (Nov 11) |
| Unavailable / status | Status stays on its own line. Missing forecasts remain a dash with their reason in expansion |
/nba/games/assist-leadersObserved potential assists and projected assists/nba/games/threes-leadersSelected Inline team source; coloured attempt trend/nba/games/points-leadersObserved FGA and projected points/nba/games/rebounds-leadersObserved rebounds and projected rebounds/nba/games?view=playersSame identity (name, then team mark and matchup); its own columns (R37, Nov 12)
Lists & rankings
Dot rows
Use a ranked list of players by one live stat, each row showing where the value sits in its own projected range. Don’t use it for a list that isn't rankable by a single number, or more than ~10 rows without a "see all" link out.
| Part | Type role / token / spacing |
|---|---|
| Rank | f.caption (12/400/18), grey, right-aligned, 20px fixed column |
| Name + meta | f.secondary (14/400/21.4≈measured 21), ink name / grey matchup line, status flag inline |
| Range track | faint band (the projected range) + a team-colour dot (the point projection), not a bar chart axis, a single relative track per row |
| Value | f.secondary weight 600 (14/600/21), unit in a lighter span beside it |
| State | Value |
|---|---|
| default | as above, each row a real Link |
| hover / focus-visible | not defined yet; .dotRow carries no hover/focus-visible rule in tonight-leaders.module.css, unlike every other row pattern on Home (Section header, chip links, ranking rows all darken on hover/focus). A real gap, not a style choice. |
| current/subject | not applicable; no row is ever "the asked-about one" in this context (that's the ranking-row pattern's own job, see Last night leaderboard) |
| loading / empty | shared with Stat tabs above; one .empty 14px-grey message for both |
| phone | no column collapse measured; the row's own flex layout holds at 390 |
search answers (projected leaders)real, same component; Home's Players board uses two-line rows without the track (Nov 11)
Players board
Use NBA Home's leaders: one stat choice drives tonight's projections beside the latest real results, so the two nights compare directly. Don’t use it for a single deep ranking (that's Dot rows in a search answer) or a list mixing stats on one axis.
The real component, tonight's projections and the latest results
Leaders
Projected tonight
Thu, Nov 12Loading tonight’s projections…
| Part | Type role / token / spacing |
|---|---|
| Stat tabs | Points, Rebounds, Assists, Threes; one tablist for both columns |
| Columns | Projected tonight | Last night (or Latest results with its date when last night has no box scores); 32px gap; one column below 760px |
| Row | 48px: rank, team mark + name over the matchup (12px grey), value + unit at the right; links to the player's game. A named exception (R37, Shane, Nov 12): Games' lists share the Slate leader identity; Home keeps these compact rows for a glance |
| Note | the projection caveat in 12px grey under the projected column |
| See all | opens Games › Players sorted by the chosen stat (Nov 11) |
| State | Value |
|---|---|
| loading | "Loading tonight's projections…" in the projected column; results render at once |
| no games tonight | noGamesMessage() with the next date |
| no box scores | "No box scores yet." |
| phone (≤760px) | columns stack: projected, then results |
/ (NBA Home)after Tonight (Nov 11, Shane's pick from the Home structure study)
Last night leaderboard
Use the night's real top performers across several stat categories at once, each category its own quiet card, never a cross-stat axis. Don’t use it for a single-stat deep ranking (that's Dot rows) or comparing two different stats' raw numbers on one bar (statShareOfSeasonHigh deliberately normalizes each bar to its OWN stat's season high, never a shared scale).
The real component, last night's real results
| Part | Type role / token / spacing |
|---|---|
| Category title | f.subhead (14/600/21); page-frame-h3, 6px margin below |
| Rank | f.caption (12/400/18), grey, 16px column |
| Big number | 14/600/21, ink, tabular-nums, 24px column; not the figure role (28-56px); this is a dense list value, not a hero numeral |
| Name + team | f.secondary, ink name / grey team code; collapses to first-initial short form under 760px (leaderNameShort) |
| Bar | 3px track, var(--hair) background, ink or grey fill; share of that stat's own season high, never cross-stat |
| “+N more with X” | f.caption (12px), 44px-tall toggle button, appears only when a real tie crosses the top-3 cutoff; collapses a tie instead of dropping it (round-7 fix, redesign.module.css's own comment) |
| State | Value |
|---|---|
| default | collapsed to 3 visible rows per category |
| hover / focus-visible (row) | name underlines, var(--faint) → darkens on focus |
| focus-visible (row, outline) | 1.5px solid ink, 2px offset |
| expanded (“+N more”) | aria-expanded toggles, overflow rows render, button label becomes “Show fewer” |
| empty | “No games last night”, 14px grey; same .empty class used everywhere else on Home |
| phone (≤760px) | grid: 3 columns → 2 columns (leaderGrid); names: full → first-initial short form |
Quadrants Last night share view (hoops/last-night/share-view.tsx)real, same component; Home replaced it with the Players board on Nov 11
Data marks
Game strip tiles
Use a game summary: matchup first, time and model chance together below, or a final score. Don’t use it for a detailed box score; keep the Home slate in one horizontal strip.
The real component, tonight's real slate
| Part | Type role / token / spacing |
|---|---|
| Matchup | shared Body role, medium ink; team tiles at narrow widths |
| Time / chance | one aligned caption line; chance in ink |
| Probability track | 4px, ink fill, width = favourite win chance |
| Final | score beside the matchup; Final caption; no chance track |
| Spacing (Nov 11) | 12px padding, 16px under a closing track (the matchup line's leading sits above it); 4px logo to team code, 8px around “at”; 12px between tiles on Home, Games and Search; the selected Games rail keeps 4px between its quiet rows |
| State | Value |
|---|---|
| default | white fill, 1px solid var(--hair) border, 10px radius |
| hover / focus-visible | border gains contrast on hover; keyboard focus has a 2px ink outline |
| selected in Games | ink tile border |
| empty | “No games tonight.” in the shared secondary role |
| phone (≤600px) | becomes a scroll-snap row with a faded right edge (mask-image), instead of wrapping; “Tonight costs one row at every width” |
/TonightStrip uses the shared Row adapter/nba/gamesselectable game list/nba/searchnight-list answer links
Notes & links
Footnote / caption lines
Use a short, quiet qualifier under a figure or a list; what the number means, or its real limits. Don’t use it for a primary instruction or a control's own label; this role is read last, not first (nba.md §4.1, "statistic first").
The real exported caveat string, the real caption role
Includes back-to-backs. Corrects for listed-out stars only; other absences, trades and late scratches aren't counted.
| Part | Type role / token / spacing |
|---|---|
| Text | f.caption: 12/400/18, var(--grey); the one role the 10-31 audit found "already consistent almost everywhere" (foundation.module.css's own comment) |
| State | Value |
|---|---|
| default | as above; no interactive states (plain text) |
Tonight (“Minutes drive the miss…”), Last night (“Includes back-to-backs…”)real, same role class/value. Last night's own "bar: share of this season's single-game high" legend was removed 2026-11-03 (Shane: "Hate these captions"). The bars stay unexplained, same as every other bar in this product.
“See all … →” links
Use pointing from a short, truncated list to its own full version elsewhere, from the list's own heading. Don’t use it for the primary way into a feature (that belongs in the top nav pill or a section header); this is a continuation link, not a destination link.
| Part | Type role / token / spacing |
|---|---|
| Link | f.caption (12/400/18), ink text, 44px-tall target via padding, arrow glyph suffix |
| State | Value |
|---|---|
| default / hover / focus-visible | same underline-darkens contract as Section header's own right link |
Home Leaders (“See all →” beside Projected tonight)real, same class. The older link under the dot rows went with TonightLeaders' unused Home mode (Nov 12)Section header (“All games →”); visible in the Section header specimen abovereal, same class
Accepted elsewhere
Home doesn’t need these five, but each one was already accepted in an earlier options round, and an accepted ruling still rules. Lighter entries than the twelve above: a specimen or a live link, the ruling, the source, and where it’s used.
Sortable table / Shot diet
Use many stats per player or game, with the reader choosing the sort order, and a player's shot mix as a stacked diet bar. Don’t use it for a short ranked list (that's Dot rows) or a single value (that's Meter).
A real scheduled game's projected shot profile
Loading the shot profile…
search answer (option-quiet.tsx)real, same component
Dense table + game context
Use a player's game log: full box score first, then minutes/shots comparisons and quiet notable-game notes. Don’t use it for a season-level summary (too dense) or fewer than a handful of rows (a plain list reads better).
Aaron Wiggins, last 10 games
| Game | Result | MIN | PTS | REB | AST | STL | BLK | TOV | FG | 3P | FT | +/- | Min vs expected | FGA vs usual | Notes |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Nov 9vs LAL | L114-124 | 23 | 15 | 3 | 3 | 0 | 0 | 3 | 5-11 | 4-7 | 1-1 | -11 | – | +1 | 1 teammate out |
| Nov 7vs BKN | W124-121 | 26 | 20 | 4 | 6 | 1 | 0 | 0 | 7-19 | 5-13 | 1-2 | +12 | – | +9 | 1 teammate out · Season high |
| Nov 6vs WAS | W139-132 | 29 | 19 | 4 | 3 | 0 | 0 | 2 | 8-12 | 3-7 | 0-0 | -4 | – | +2 | 1 teammate out |
| Nov 4@ WAS | W120-105 | 29 | 15 | 4 | 2 | 2 | 1 | 2 | 6-14 | 3-7 | 0-0 | +15 | – | +4 | 1 teammate out |
| Nov 2@ IND | W108-104 | 24 | 7 | 3 | 2 | 1 | 0 | 2 | 3-12 | 1-6 | 0-1 | +7 | – | +3 | 1 teammate out |
| Oct 31@ NOP | L99-121 | 23 | 10 | 2 | 1 | 1 | 1 | 2 | 4-11 | 2-7 | 0-0 | -7 | – | +2 | 1 teammate out |
| Oct 29vs CLE | W113-111 | 24 | 20 | 7 | 3 | 0 | 1 | 1 | 8-14 | 4-7 | 0-0 | +17 | – | +5 | 1 teammate out · Season high |
| Oct 28vs MIA | L117-129 | 24 | 10 | 4 | 3 | 0 | 0 | 1 | 4-11 | 2-7 | 0-0 | -2 | – | +2 | 1 teammate out |
| Oct 26@ HOU | L110-120 | 27 | 12 | 4 | 3 | 1 | 0 | 0 | 5-12 | 2-6 | 0-0 | -12 | – | +3 | – |
| Oct 24vs HOU | L114-121 | 29 | 16 | 5 | 2 | 1 | 1 | 0 | 6-13 | 4-9 | 0-0 | -2 | – | +5 | – |
Teammate availability: injury log from Oct 28.
About game context
Expected (minutes): what Gist's model expected before that game. Usual (shots): his average shot attempts before that game. Notes mark back-to-back games, teammates out, 8+ points off the model's projection and season highs.
search answer (option-quiet.tsx)real, same component/proto/boxlog (context layouts C5, C7)real, same component
Meter
Use one value against a league range: a defender's effect on opponents, with the qualified opponents it's built from underneath. Don’t use it for a ranked list of several players (that's Dot rows or the Last night leaderboard).
Matchup defense, page variant
/nba/player, search answer (option-quiet.tsx)real, same component
Quadrant, Corner-top faces
Use a two-stat comparison with named extremes, faces shown because identity is the point of the comparison. Don’t use it for a table of more than two stats, or faces as decoration where identity isn't the point (nba.md §4.6).
/nba/quadrants/questions?v=elive route; builds from a large preset catalog this guide doesn't fork
Strip / Columns
Use one player's value shown among others, or across seasons, inside a specific answered question. Don’t use it for a standalone specimen with fabricated data; it only means something attached to a real asked question.
/nba/searchlive route; ask a question to see it, not reproducible with fixture data here
MLB, on the same foundation
Baseball moved onto this foundation 2026-11-01 (mlb-to-nba-system.md). These two are the real, promoted patterns that are MLB’s own, not reused from Home.
MLB Pitches: team chips + table
Use narrowing a dense, sortable, searchable pitcher table by team, when team identity is part of the question. Don’t use it for a single-pitcher detail view (not built) or fewer than roughly a dozen rows (a plain list reads better).
| Part | Type role / token / spacing |
|---|---|
| Team chip | logo + abbreviation, multi-select, toggles a filter on the table below |
| Table | sortable columns, the page's one focal view (nba.md §4: "statistic first"); the old scatter chart is gone |
| Source note | f.evidence: the same snapshot source, CSV download and Baseball Savant link kept from the old page |
/pitchesreal, same component, promoted 2026-11-01
Can’t mount standalone: like the top nav pill above, this component renders its own full NavPillCombined internally (it is the whole /pitches route, not an extractable widget). Mounting it here risks the same shell-hiding/inert conflict and an accidental navigation away from this guide. See it live at /pitches. MLB team logos are loaded at runtime and are still an uncleared trademark (nba.md §5); this guide doesn’t ship a second copy of them either way.
MLB Home leaders
Use today's real pitching/batting performers, or (off-season) 2025 season leaders, in the same underline-tab + ranked-list shape as Home's own stat tabs. Don’t use it for a side-by-side two-column layout; MLB switches between the two categories with tabs instead, since NBA has no two-category stat switcher to match.
| Part | Type role / token / spacing |
|---|---|
| Tabs | Pitching / At the plate (daily) or each 2025-leader stat (season fallback) |
| Ranked rows | redesign.module.css's own rankList/rankRow, imported read-only, for exact visual parity with NBA Home's own ranking list |
/LandingHome, the new default; real, same components; daily data shows when there is any, 2025 season leaders otherwise
Can’t mount standalone:the daily tab’s own data comes from an async fetch, and the season-leader fallback’s data function is explicitly server-only (its own header comment: “never by the client landing-home-season-leaders.tsx”). A static specimen here would either need fake data (this guide doesn’t fabricate data) or a real fetch this guide’s own client-rendered page can’t safely make. See it live at /.
Open questions
Three underline-tab implementations existfor the same job (Home’s TonightLeaders, Stats’ own CategoryTabs, hoops’ own UnderlineTabs; the last one’s own header comment calls itself “a parallel component tuned to the same numbers,” not a reuse). Worth a real consolidation decision.
Dot rows define no hover/focus-visible state.Every comparable row pattern on Home (ranking rows, Last night rows, section links) darkens on hover/focus; TonightLeaders.tsx’s own dot rows don’t. Not invented here per the brief (“not defined yet” is the honest finding); flagging it as a real gap.
“Last night” divider has no shared component.It’s inline JSX in HomeLayouts.tsx, not an exported piece like Section header; the ruling above exists, but a second screen wanting this exact divider would currently copy markup, not import a component.
Communication
What a screen should say first, and how much.
Answer the question the reader asked, in their words
Lead with the answer to the question as asked, not the topic around it. A section heading names the subject, and the first sentence under it makes the point.
“Who will take the most shots in Blazers at Jazz” opens with the shot leaders, not a team overview.
Decide the one takeaway before designing the view
Every answer, card and chart has one thing the reader should leave knowing. Write it down first; everything else supports it or goes.
The quiet search answer is one figure plus one sentence. A second large element means two takeaways, so cut one.
Quiet claims get quiet presentation
How strong the visual emphasis is matches how strong the evidence is. An early-season number isn't shown at the same weight as a full season's.
Dot rows' range track sits next to the projected value. Red marks the subject, never certainty.
Use the simplest view that still explains
Pick the smallest structure that answers the question. Keep the cause and the evidence; remove interaction that doesn't change the answer.
Point, why it matters, example
An important claim comes with one concrete thing to inspect: the game, the player row, the source.
“Minutes drive the miss” sits under projected leaders, next to the rows whose minutes it refers to.
Bound every claim to its real scope
Name the sample, the window and the qualifier in plain words. Say the caveat once, in the source note, not as a pile of disclaimers.
“Among players with 500+ minutes”, “6 games in”, “by 2025-26 season average”.
Explain why a number matters, not only what it is
Tie a count to its scope and to what it tells you. A number is never a trophy.
The scorecard answer gives the games scored, the favourites' record and the dates covered, never a bare accuracy percentage.
Show the contrast and name its cost
When the answer changed for a reason, show the before and after and the specific thing that moved it.
The labelled star correction names the listed-out star behind a changed win chance, rather than moving the number silently.
Say what would change the answer
Projections and models state the main thing that would overturn them.
“Minutes drive the miss: a projection is only as good as tonight's playing time.” The injury report's as-of time is shown where it changes the numbers.
Clear options, concrete labels, explicit tradeoffs
Toggles and choices use labels that name the result, and options are comparable. Every option states its cost and when it fails.
“Last 10”, “Season”, “Points”.
The visual follows the explanation, under the reader's control
The evidence view changes with the answer kind and points at the current claim. Nothing moves on a timer, and motion stays off under reduced motion.
Quick answer first, depth on request
Each screen gives the answer at a glance, with the full table, the method and the source one step away.
Home's Leaders show tonight's top five, with “See all” beside the heading.
Say what the reader can now do
Describe what a view lets someone notice or decide, not what the feature is called.
“Gist helps people see the game inside the game.” A guide topic description says what it helps you decide.
Start from the reader's real situation
Open with something the reader recognises: tonight's games, a player they asked about, last night. Relevance comes from their question, never manufactured urgency.
Check before showing a screen
- What did the reader ask, and does the first thing they see answer it?
- What is the one takeaway, and does anything compete with it?
- Is every claim bounded to its sample, window or qualifier, said once?
- Does each number say why it matters?
- If the answer is a model, does it say what would change it?
- Is the full detail one step away, rather than all on screen?
How we review designs
Critique specifically, and make the next step actionable
Name the observed defect, its cost to the reader and the fix. Praise doesn't substitute for evidence.
Name what would change the recommendation
Every provisional pick says what would make it wrong.
Search suggestions are hypotheses about reader vocabulary
The phrases people type into Gist search can suggest what to support next, but they aren't representative research.
In use
Where each pattern is already live, to see it in context.
Real routes on this running server, not a second rendering of them. Each link names which patterns from Components it uses.
- HomeThe question field in the logo row, Tonight's games, the Players board (projected tonight beside the latest results) and the Ask next line (Nov 11; one line with answers since R38, Nov 12).
- Search answersEvery answer shape: quiet figures, Sortable table / Shot diet, dense game-log tables, Meter.
- StatsPageFrame with a tab strip; the category index and per-stat bar lists.
- GamesPageFrame with a date strip and four view links (Matchups, Model record, Leaders, Players). Matchups is the split pane: game list plus the selected game's forecast, estimated score and player lines.
- Games › PlayersEvery projected player on the date: sortable roster table with a Projection link per row (Nov 11).
- Games › LeadersAssists, Threes, Points and Rebounds leaders with the Last 5 vs season trend under each player, and Updated · Next timing. With no live data it explains and links to Games › Players.
- Matchup SheetBoth rotations side by side with opponent allowance colour and Expand row evidence. Reached from a game with live data.
- PlayersTeam rosters, each player linking to statistics and to a next-game Projection that returns here.
- Player projectionContext: game log first, Factors on the right on desktop and below on phone; Derived, Observed and Saved before tip lines.
- Last nightLeaders by stat from the real completed slate.
- QuadrantsOpens on Make your own: combine two stats; Recent changes, Questions and the other sections follow in one quiet line.
- A player pageMeter, dense tables and Dot rows together on one subject.
Baseball
- HomeMLB's daily and season leaders; the sport toggle sits beside the logo. `/` is NBA Home.
- PitchesTeam chips (logo + abbreviation) narrowing a sortable, searchable table of every four-seam pitcher.
- PlayersPlayer browsing and search.
- MetricsThe metric index; movement, velocity and the rest, one per page.
- Metrics: IVBOne metric's own lesson page, the figure role for its hero readout.
- Pitch labThe interactive movement plot and its own release/plate chart.
Assets
Whether to download the tokens or print the guide.
These are the foundation baseline tokens. Export your current NBA draft from Shared rules.