# UX States Spec - every screen has four states, not one

> **Who fills this in:** UI/UX (fast, so devs can build against it). **Why:** the "happy path with data" is the easy 20%. Loading, empty, and error are where real users live. Specifying all four *before* the build is [Way #1](../../ways-of-working.md) for the front end - and it's how you avoid a demo that white-screens when the API hiccups.
>
> Fill the state tables for each endpoint, make the data-viz call, then the accessibility note. Delete the quote-blocks before handing it over.

---

## The four states - define each, every time

For each consumer screen, specify what the user sees in **Loading**, **Populated**, **Empty**, and **Error**. "Empty" and "Error" are different: empty = the call succeeded but there's nothing to show; error = the call failed (renders from the `Problem` envelope, `REQ-API-5`).

### Screen A - Compare (`GET /api/compare`)

| State | Trigger | What the user sees |
|---|---|---|
| **Loading** | Request in flight | Skeleton radar + skeleton bars; no layout shift when data lands |
| **Populated** | `200` with `user[]` + `comparison[]` | Radar overlay: the user's scores vs the scope average (see decision below) |
| **Empty** | `200` but every dimension is `responded:false` | Friendly "No comparison yet - complete your assessment to see how you stack up", not a blank chart |
| **Error** | non-2xx / `Problem` body | Inline error card showing `Problem.title`; a Retry action; the rest of the dashboard stays usable |

> Edge case to spec explicitly: Alice has **two N/A dimensions** (governance, business_impact = `0.0`, `responded:false`). Decide how the radar renders those - a visible gap/dashed segment beats plotting a misleading `0`.

### Screen B - Recommend (`GET /api/recommend`)

| State | Trigger | What the user sees |
|---|---|---|
| **Loading** | Request in flight | 5 placeholder list rows |
| **Populated** | `200` with up to 5 recommendations | Ranked list: title + one-line `reason` + gap indicator (see decision below) |
| **Empty** | `200` with `[]` (e.g. user completed everything relevant) | "You're all caught up on your weak spots" - a win, framed as one |
| **Error** | non-2xx / `Problem` body | Inline error row + Retry; never silently show an empty rail on failure |

---

## Data-visualisation decision (record the *why* - [Way #5](../../ways-of-working.md))

| Screen | Chosen viz | Why | Rejected |
|---|---|---|---|
| **Compare** | **Radar overlay** - two series on one radar (you vs scope average) | One shape, instant "am I inside or outside the team?" read across all 6 dimensions at once | Side-by-side bars (harder to compare 6 dims at a glance) |
| **Recommend** | **Ranked list** - each row: lesson title + reason + a **gap indicator** (bar/pip sized to `gapScore`) | Order *is* the message; the gap indicator shows *why* this lesson ranks where it does, tying the UI back to the contract's `gapScore` | A chart (recommendations are a to-do list, not a trend) |

> Capture this in a one-line ADR if it was contested - that's where the trade-off reasoning lives ([`adr-template.md`](adr-template.md)).

---

## Accessibility note (non-optional - `QUAL-PRINC-SDLC`)

- **Don't rely on colour alone.** The compare overlay must distinguish "you" from "average" by shape/pattern/label, not just hue - and the recommend gap indicator needs a text/numeric value beside the bar.
- **Contrast & text:** meet WCAG AA contrast; never hard-code an unreadable colour pair for the two radar series.
- **Screen readers:** charts need a text alternative - e.g. the populated radar exposes the same numbers as a visually-hidden table; each recommendation row reads as "[title], gap [n], because [reason]".
- **Keyboard & focus:** Retry actions and any list interactions are reachable and operable by keyboard; visible focus states.
- **Motion:** skeleton/loading shimmer respects `prefers-reduced-motion`.

---

*Hand this to your devs alongside the contract in [`../../../../stacks/nextjs/openapi.yaml`](../../../../stacks/nextjs/openapi.yaml). The five ways: [ways-of-working.md](../../ways-of-working.md).*
