# UX Audit: Recommendations

> Generated by the `ux-auditor` agent on 2026-03-24.
> Reference: [chess-com-reference.md](chess-com-reference.md)

## Executive Summary

The game list view is clean and functional but suffers from critical data gaps that undermine user trust and usefulness. A P1 bug in `analysis.py` causes all opening-phase moves to be labeled `eval_source = "opening_explorer"` regardless of the actual evaluation source (cloud eval or Stockfish fallback), which makes the PWA skip those moves for accuracy and classification calculations. This results in games with many opening moves showing **no accuracy and no classification badges**, even when valid evaluation data exists. Beyond this data bug, the game list lacks filtering, shows only the player's accuracy (not the opponent's), has no analysis status indicator, and uses classification symbols without tooltips or a legend -- all of which create friction for every persona, from beginner Leo to club player Marc.

## Findings: Game List View

### 1. Game List -- Desktop (1280x720)

**Screenshot:** `/tmp/ux-audit/01-game-list-desktop.png`

**Strengths:**
- Clean dark theme with good contrast for card backgrounds against the page
- Result badge (W/L/D) is immediately visible with color coding (green for win, red for loss)
- Opponent name, date, move count, and opening name are all present on each card
- Cards are clickable and have a hover effect for interactivity feedback

**Issues:**
- Game 1 ("vs Opponent1", 11 moves, Loss) shows **no accuracy and no badges** -- the right side of the card is completely empty. This is caused by the eval_source bug (all 11 moves are `opening_explorer`).
- Game 2 ("vs Opponent2", 20 moves, Win) shows "98%" but with no label -- a new user cannot tell what "98%" means. Is it accuracy? Win probability? Score?
- No classification badges visible on either card (Game 1 has none due to the bug; Game 2 appears to have no inaccuracies/mistakes/blunders at all, or they are below the threshold)
- "Select all" checkbox and "20 games" dropdown are toolbar elements that feel disconnected from the cards below (no visual grouping)
- The toolbar serves app-mode batch analysis ("Analyze selected" button, hidden in demo) -- in demo mode, "Select all" is functionless and confusing

### 2. Game List -- Mobile (375x667)

**Screenshot:** `/tmp/ux-audit/03-game-list-mobile.png`

**Strengths:**
- Layout adapts reasonably to mobile width -- cards stack vertically and remain readable
- Opening name wraps properly ("King's Pawn Game", "Queen's Pawn Game")
- Result badge stays visible on the left

**Issues:**
- Same accuracy/badge issues as desktop
- The toolbar ("Select all" + "20 games") takes up valuable vertical space on mobile for functionality that is irrelevant in demo mode
- No visual separation between the toolbar and the card list
- Card tap targets are adequate but have no active/pressed state feedback

### 3. Game Review -- Desktop

**Screenshot:** `/tmp/ux-audit/07-game-review-desktop-large.png`

**Strengths:**
- Board, eval bar, move list, and opening info are all visible in a single screen
- "Back to games list" link provides clear navigation

**Issues (relevant to game list context):**
- When entering a game with no accuracy data (Game 1), there is no indication in the review that accuracy could not be computed
- The game info bar ("Opponent1 vs TestPlayer 1-0") does not show accuracy even though the game list does for Game 2
- No way to return to the game list via keyboard shortcut

### 4. Navigation Menu -- Mobile

**Screenshot:** `/tmp/ux-audit/08-nav-menu-mobile.png`

**Issues:**
- The nav menu has no "Games" or "Analysis" item -- the game list is the default view but has no named menu entry. A user navigating away via "Training" has to figure out that closing the menu returns to the game list.
- Menu items (Training, Settings, About) are clear but there is no active/selected state indicator

---

## Detailed Recommendations

### UX-001: Fix eval_source mislabeling in analysis.py

- **Priority**: P1 (critical data correctness bug)
- **Scope**: [app] (analysis.py is backend; affects data consumed by both [demo] and [app] PWA)
- **Category**: data correctness
- **Chess.com pattern**: Chess.com classifies and computes accuracy for ALL moves including opening moves. Opening moves are labeled "Book" but still contribute to the accuracy calculation when evaluation data is available.
- **Current state**: In `analysis.py:501`, all opening-phase moves are labeled `eval_source = "opening_explorer"` even when the evaluation came from Lichess Cloud Eval (`_eb_src = "cloud_eval"`) or Stockfish fallback (`_eb_src = "sf_fallback"`). The PWA's `computeAccuracy()` (app.js:1758) and `classifyMove()` (app.js:1683) both skip moves with `eval_source === "opening_explorer"`. Result: games analyzed primarily via cloud eval show **no accuracy and no classification badges** in the game list, even though valid `score_cp` values exist in `eval_before`/`eval_after`.
- **Proposed change**: Separate the two concerns that `eval_source` currently conflates:
  1. **Opening phase** (boolean): whether the move was found in the opening explorer database. This is an informational label ("Book" badge in the move list).
  2. **Evaluation source** (string): what actually provided the `score_cp` values -- `"cloud_eval"`, `"stockfish"`, or `"none"` (when no eval data is available).

  Concrete approach: add an `in_opening: true/false` field to the move output, and set `eval_source` to the actual source (`"cloud_eval"` or `"stockfish"`) even for opening moves. Update the PWA to use `in_opening` for the "Book" badge and `eval_source` for accuracy/classification gating.
- **Persona impact**:
  - Leo (beginner): Currently sees empty game cards with no feedback -- thinks the app is broken. Fix gives him accuracy and error badges for all games.
  - Samir (grinder): Cannot compare accuracy across games because some show nothing. Fix makes stats consistent and trustworthy.
  - Nadia (reviewer): Missing classification data in the move list for opening moves that actually had eval data. Fix gives her complete analysis.
  - Marc (club): Opening move accuracy is particularly important for his tournament preparation. Fix surfaces errors in the opening phase that are currently invisible.
- **ROADMAP target**: New item -- data correctness fix, prerequisite for all accuracy/classification features

---

### UX-002: Label the accuracy percentage

- **Priority**: P2
- **Scope**: [demo] and [app] (PWA change)
- **Category**: feedback | accessibility
- **Chess.com pattern**: Chess.com labels accuracy explicitly: "Accuracy: 92.3%" with a small "Accuracy" label above or beside the number. Both players' accuracy values are shown side by side.
- **Current state**: The game card shows a bare "98%" with no label (screenshot: `/tmp/ux-audit/01-game-list-desktop.png`). A first-time user, especially a beginner who has never used chess.com Game Review, cannot tell what this number represents.
- **Proposed change**: Add a small "Accuracy" label below or above the percentage value. Consider using a subtle icon (e.g., a target icon) as an alternative or complement.
- **Persona impact**:
  - Leo (beginner): Has never seen accuracy metrics before. Without a label, the number is meaningless. A label like "Accuracy" makes it immediately understandable.
  - Samir (grinder): Knows what accuracy is but appreciates clarity. No negative impact.
  - Nadia (reviewer): Expects labeled metrics from chess.com/Lichess. Current bare number feels unfinished.
  - Marc (club): Minor -- he knows what accuracy is, but proper labeling signals app maturity.
- **ROADMAP target**: Quick win (CSS + minor JS change)

---

### UX-003: Show both players' accuracy

- **Priority**: P2
- **Scope**: both
- **Category**: feedback
- **Chess.com pattern**: Chess.com Game Review shows accuracy for both White and Black side by side in the game summary header. This gives immediate context: "I played 85% but my opponent played 92% -- they outplayed me."
- **Current state**: `computeAccuracy()` (app.js:1752-1773) only computes accuracy for `game.player_color`. The game card shows one number with no indication of whose accuracy it is. The opponent's accuracy is never computed or displayed.
- **Proposed change**: Compute accuracy for both colors. In the game card, show two accuracy values: one for the player (highlighted) and one for the opponent (dimmer). Example layout: `92% / 87%` with the player's value in the card's accent color.
- **Persona impact**:
  - Leo (beginner): Seeing both accuracies helps him understand "was my opponent also making mistakes?" -- important context for learning.
  - Samir (grinder): Wants to see relative performance. A high accuracy against a low-accuracy opponent is less impressive than against a high-accuracy one.
  - Nadia (reviewer): Comparing accuracies is a core part of her post-game analysis workflow.
  - Marc (club): Useful for tournament preparation -- seeing opponent accuracy patterns across games.
- **ROADMAP target**: Medium effort (JS logic change + card layout adjustment)

---

### UX-004: Show opponent's classification counts

- **Priority**: P3
- **Scope**: both
- **Category**: feedback
- **Chess.com pattern**: Chess.com shows classification counts (brilliant, great, best, excellent, good, inaccuracy, mistake, blunder) for BOTH players in the game review summary, not just the reviewed player.
- **Current state**: The classification badge loop (app.js:1910-1916) only counts moves where `game.moves[i].side === game.player_color`. Opponent errors are never shown in the game list.
- **Proposed change**: Add a second row of badges for the opponent's errors (dimmer styling or a different section). This could be a toggle or always visible. For the game card, showing opponent blunders is most valuable ("they had 3 blunders").
- **Persona impact**:
  - Leo (beginner): Seeing opponent mistakes validates his wins and explains his losses.
  - Samir (grinder): Wants complete game statistics for comparison.
  - Nadia (reviewer): Full classification data is essential for her deep analysis workflow.
  - Marc (club): Opponent error patterns inform preparation strategy.
- **ROADMAP target**: Medium effort (JS logic change, depends on UX-001 being fixed first)

---

### UX-005: Add tooltips to classification symbols

- **Priority**: P2
- **Scope**: both
- **Category**: accessibility
- **Chess.com pattern**: Chess.com explains each classification category with an icon, name, and description. Premium users see this in Game Review; free users see it in the accuracy summary. Each symbol has a clear label.
- **Current state**: The game card badges use symbols like `?!`, `?`, `??` with color coding (yellow, orange, red) but no tooltip, title attribute, or legend. A beginner who has never used chess.com premium (like Leo) does not know what `3?!` means -- "3 inaccuracies" is not self-explanatory from the symbol alone.
- **Proposed change**: Add `title` attributes to badge elements: `title="3 inaccuracies"`, `title="1 mistake"`, `title="2 blunders"`. On mobile (where hover is not available), consider a long-press tooltip or a legend icon that opens a classification explanation panel.
- **Persona impact**:
  - Leo (beginner): This is critical. He has never seen these symbols before. Without tooltips, the badges are cryptic and intimidating rather than helpful.
  - Samir (grinder): Nice-to-have. He knows the symbols but tooltips do not hurt.
  - Nadia (reviewer): Expects professional labeling. Tooltips signal completeness.
  - Marc (club): No impact -- he knows all classification categories.
- **ROADMAP target**: Quick win (2 lines of JS per badge: add `title` and `aria-label`)

---

### UX-006: Add game list filtering

- **Priority**: P2
- **Scope**: both
- **Category**: navigation | interaction
- **Chess.com pattern**: Chess.com's game archive has filters for result (Win/Loss/Draw), time control, date range, and opponent. The Game Review list can be filtered by accuracy range.
- **Current state**: The game list toolbar has only a "Select all" checkbox and a game count limit selector (20/50/100). There is no filter by result, opening, date range, or analysis status. For a user with 50+ games, finding a specific game or category requires scrolling through all cards.
- **Proposed change**: Add a filter row below the toolbar with toggle buttons for result (W/L/D -- clickable to filter), and a text search for opponent name or opening. Keep it minimal: three toggle buttons + one search input. More advanced filters (date range, accuracy range) can come later.
- **Persona impact**:
  - Leo (beginner): Wants to focus on losses to learn from mistakes. Currently must scan all games manually.
  - Samir (grinder): Wants quick access to recent losses for review. Filtering by result is his most-wanted feature.
  - Nadia (reviewer): Wants to filter by opening to compare games in the same opening. This is core to her workflow.
  - Marc (club): Opening filter is essential for tournament preparation. "Show me all my Sicilian games."
- **ROADMAP target**: Medium effort (new UI component + JS filtering logic)

---

### UX-007: Add analysis status indicator to game cards

- **Priority**: P2
- **Scope**: both
- **Category**: visual | feedback
- **Chess.com pattern**: Chess.com shows a "Reviewed" badge on games that have been through Game Review, and an "Analyze" button on unreviewed games. The distinction is immediately visible.
- **Current state**: All games in the list look the same regardless of whether they have analysis data. A game with no accuracy data (like Game 1 in the demo, due to the eval_source bug) is visually identical to a game that genuinely has not been analyzed. The user cannot tell if the game needs analysis or if the analysis produced no results.
- **Proposed change**: Add a visual indicator to each game card:
  - **Analyzed**: subtle checkmark or "Analyzed" badge, accuracy and classification badges displayed
  - **Not analyzed**: "Not analyzed" label or grayed-out state, with an "Analyze" prompt (in app mode)
  - **Partially analyzed**: "Partial" indicator (for interrupted analyses)
- **Persona impact**:
  - Leo (beginner): Sees an empty card and thinks the app is broken. An explicit "Not analyzed" label manages expectations.
  - Samir (grinder): Wants to quickly identify which games still need analysis. This saves time in his routine.
  - Nadia (reviewer): Needs to know which games have full analysis before diving into review.
  - Marc (club): Wants to prioritize analysis for tournament-relevant games.
- **ROADMAP target**: Medium effort (JS logic to check analysis status + CSS for indicator styles)

---

### UX-008: Hide "Select all" checkbox in demo mode

- **Priority**: P3
- **Scope**: [demo]
- **Category**: layout | navigation
- **Chess.com pattern**: N/A -- chess.com does not have a demo mode.
- **Current state**: The "Select all" checkbox and toolbar are visible in demo mode (screenshot: `/tmp/ux-audit/01-game-list-desktop.png`) even though "Analyze selected" button is hidden. The checkbox is functionless in demo mode -- clicking it does nothing useful. This is confusing, especially on mobile where toolbar space is valuable.
- **Proposed change**: In demo mode, either hide the "Select all" checkbox entirely or hide the entire toolbar. The game count limit selector could be moved to a simpler position (e.g., bottom of the list or inline).
- **Persona impact**:
  - Leo (beginner): One less confusing element. He does not know what "Select all" is for.
  - Samir (grinder): Cleaner interface in demo. No negative impact.
  - Nadia (reviewer): Less noise. Appreciates clean UI.
  - Marc (club): No impact.
- **ROADMAP target**: Quick win (CSS `hidden` class in demo mode)

---

### UX-009: Add "Games" entry to navigation menu

- **Priority**: P3
- **Scope**: both
- **Category**: navigation
- **Chess.com pattern**: Chess.com has explicit "Play", "Puzzles", "Learn", "Watch" tabs in the top navigation. Each major section has a named entry.
- **Current state**: The nav menu (screenshot: `/tmp/ux-audit/08-nav-menu-mobile.png`) has "Training", "Settings", "About" -- but no entry for the game list / analysis view. The game list is the default view, but a user who navigates to Training has no obvious way to get back to the game list. They must close the menu and realize the game list is the "home" view, or use browser back.
- **Proposed change**: Add a "Games" or "Game Review" entry at the top of the nav menu, above "Training". Add an active state indicator to show which view is currently active.
- **Persona impact**:
  - Leo (beginner): Gets lost after going to Training. A "Games" menu item provides a clear way back.
  - Samir (grinder): Alternates between Training and Games frequently. Explicit nav entries speed this up.
  - Nadia (reviewer): Her primary view is the game list. Having it named in the nav validates it as a first-class feature.
  - Marc (club): Expects standard navigation patterns. Missing entry feels like an oversight.
- **ROADMAP target**: Quick win (HTML + JS nav handler)

---

## ROADMAP-Compatible Items

Items below use the exact ROADMAP checkbox format for direct promotion.

### Quick Wins

- [ ] UX-002: Label accuracy percentage ("Accuracy: 98%") -- scope: both
- [ ] UX-005: Add tooltips to classification symbols (?!, ?, ??) -- scope: both
- [ ] UX-008: Hide "Select all" checkbox in demo mode -- scope: [demo]
- [ ] UX-009: Add "Games" entry to navigation menu -- scope: both

### Medium Effort

- [ ] UX-003: Show both players' accuracy side by side -- needs: JS logic change + card layout
- [ ] UX-004: Show opponent's classification counts -- needs: JS logic, depends on UX-001
- [ ] UX-006: Add game list filtering (W/L/D toggles + search) -- needs: new UI component + JS filtering
- [ ] UX-007: Add analysis status indicator to game cards -- needs: JS logic + CSS indicators

### Major Redesign / Bug Fixes

- [ ] UX-001: Fix eval_source mislabeling -- `analysis.py:501` labels all opening moves as `opening_explorer` even when eval came from cloud_eval or stockfish fallback. Separate `in_opening` (boolean) from `eval_source` (actual eval provider). -- depends on: analysis.py refactor, data migration for existing analysis_data.json files
