UI/UX review checklist
The manual review that the UI/UX designer, QA and the frontend engineers walk at the end of every phase, with Ghostframe screenshots, before the tech lead tagsphase-<n>. It complements the
automated suite (task e2e, e2e.md); items marked (auto) are also asserted there, and
the review only confirms them on screenshots.
Specs this checks against: CLAUDE.md (“Layout consistency”, Motion, TS/React),
PLAN §4 (click budgets, §4.1 motion, §4.2 decisions), design/README.md
principles 1–8, design/interactions.md, and the screen specs in
design/screens/.
How to record results: one row per failed item in docs/design/review-phase-<n>.md (the format of
review-phase-1.md): id, severity, screen, problem (spec), fix, owner.
Attach the screenshot path.
Setup
- Start the app against the fake venue:
task dev:fake(backend 127.0.0.1:8080, Vite http://localhost:5173). Log in with your dev password. For the load items (§15) restart the backend withSESAME_FAKE_VENUE_STRESS=1(worst-case names, extreme prices and balances, 64 orders, 200 fills, a stale book). - Screenshots go under
C:\Users\Administrator\Desktop\sesame-screens\<phase>\(for examplephase-3), named<screen>-<state>-<width>-<theme>.png.
Ghostframe recipe
Every “Ghostframe:” step below uses these calls. Always start from a fresh isolated context, so no cookie, cached theme or dock layout from an earlier review leaks in.| Step | Call |
|---|---|
| Open | mcp__ghostframe__new_page with url: "http://localhost:5173/login" and a new isolatedContext name per pass (e.g. review-p3-1440-dark) |
| Desktop size | mcp__ghostframe__resize_page {width: 1440, height: 900} (or the width under test) |
| Phone | mcp__ghostframe__emulate {viewport: "390x844x3,mobile,touch"}; landscape: "844x390x3,mobile,touch,landscape" |
| Theme | mcp__ghostframe__emulate {colorScheme: "dark"} or "light", with Theme set to System in the app menu; or pick Light/Dark in the menu |
| Offline, slow | mcp__ghostframe__emulate {networkConditions: "Offline"} / "Slow 3G"; clear by omitting it |
| Block the backend | mcp__ghostframe__set_blocked_urls {patterns: ["*/api/*", "*/ws*"]}; clear with [] |
| CPU load | mcp__ghostframe__emulate {cpuThrottlingRate: 4} |
| Time zone | mcp__ghostframe__emulate {timezone: "America/Los_Angeles"} (and Asia/Tokyo) |
| Measure | mcp__ghostframe__evaluate_script with a function that returns getBoundingClientRect() heights, scrollWidth/clientWidth, or computed colours |
| Structure | mcp__ghostframe__take_snapshot for roles, names and focus order |
| Capture | mcp__ghostframe__take_screenshot with filePath under the phase folder (fullPage: true for long pages) |
| Look | Read the saved PNG back (the Read tool) and judge it; a screenshot that was not looked at does not count |
function of evaluate_script; pass the row selector):
() => {
const row = document.querySelector("header"); // the row under review
const els = [...row.querySelectorAll("button, a[href], input, [role=status]")];
return {
heights: els.map((e) => [e.getAttribute("aria-label") ?? e.textContent.trim(), Math.round(e.getBoundingClientRect().height)]),
overflow: [row.scrollWidth, row.clientWidth],
pageOverflow: [document.documentElement.scrollWidth, innerWidth],
};
}
1. Layout consistency
| Check | How | Pass |
|---|---|---|
| One control height per row (auto) | Ghostframe at 1440 and 390: run the measuring script on the top bar, mobile header, board toolbar, portfolio tab rows, orders toolbar, ladder header, dock tab strip | Every control in a row is --control-h (32 px) on desktop or --touch-min (44 px) on touch. No 24/28/34/36 px mixes. Text that is not a control is visibly smaller inline text |
| Vertical centring | Screenshot each bar at 200% crop | Equal space above and below each control; bar inner padding equals the gap between items |
| Spacing scale | evaluate_script returning computed padding/gap of bars and rows | Only 4, 8, 12, 16, 24 px |
| One container level per section | Look at every panel: count borders and fills from the page surface inward | At most one border or one fill per section; no box in a box in a box |
| Divider or spacing instead of a box | For each bordered or filled container ask: “Could this box be a divider or spacing instead? Is any section boxed only to separate it?” | Sections are separated by plain spacing or a single 1 px low-contrast divider. Cards, borders and fills only for separate, actionable things: dialogs, sheets, popovers, toasts, a mobile position card |
| Tab strips and segmented controls | Screenshot Portfolio, Orders, the board window switch, chart ranges | Fill the bar’s height rhythm exactly; never a filled group inside a bordered strip inside a panel |
| Nothing spills out (auto) | Measuring script: overflow and pageOverflow at every width in §10 | scrollWidth <= clientWidth for bars and rows; the page never scrolls sideways |
| Nothing squeezed (auto, stress) | evaluate_script over flex rows: width of each dot, icon, badge, chip and number | No visible element narrower than 1 px or its min-width; icons and dots stay square or round (a live dot squeezed to 0 px fails) |
| Essential values never truncate (auto, stress) | Stress mode at 1440 and 390: scores, clocks, prices, sizes, P&L | scrollWidth <= clientWidth for every one of them (a tennis score shown as 7… fails); they reserve width or move to their own line |
| Truncation is earned (auto, stress) | Any text ending in … | It has a tooltip with the full value, and its row has under about 24 px free (text truncated while the row has 600 px spare fails) |
| One gap per group (auto, stress) | Phone Sell/Buy pairs, stake presets, steppers | Horizontal and vertical gaps equal within 1 px, from the spacing scale (4 px across and 0 px down fails) |
| Optically centred (auto, stress) | Price buttons with reduced motion on and off | The value’s text centre is the button centre within 1 px; markers and units that come and go are absolutely placed and take no width |
| Scrolling chip rows show that they scroll | Board league chips and phone scope chips at 390 with more chips than fit | A fade edge or a visibly cut chip at the end signals more; the scrollbar is hidden |
2. Content-driven sizing
| Check | How | Pass |
|---|---|---|
| Long venue text | Palette search for the longest event title; open it at 390 and 1440 | Titles truncate with an ellipsis (tooltip or full title on the detail page) or wrap on their own line; nothing overlaps a price column |
| Extreme numbers | Fake account positions: 12,000 sh; a 1,234,567.89balanceanda−99,999.99 P&L (stress mode) | Columns keep their width; digits are tabular; no wrapped number (“12,000.00 sh” on two lines fails) (auto for wrapping) |
| Live values | Watch the board and a ladder for 30 s | No column or row changes width when a price goes from 9¢ to 10¢ or a size from 999 to 1.2k |
| Charts | Event view, every chart range, and an empty range | The chart keeps its height; “No trades in this range” sits inside the plot; attribution does not overlap the plot |
| Loading, empty and stale states keep the loaded geometry | Throttle to Slow 3G, reload each screen; block */ws* to see stale | Skeletons have the real row geometry (no shimmer); empty and stale states do not collapse or grow the panel |
3. Trading safety
| Check | How | Pass |
|---|---|---|
| Misclick spacing | Measure gaps between Buy and Sell, ladder bid and ask cells, Exit and Add, Cancel All and the SAFE/ARMED switch | Adjacent opposite actions are visually separated (position, colour and label) and at least 4 px apart; touch targets 44 px |
| Side, size and price visible before a click | Hover a price (desktop) or look at the stake bar (phone) | The cursor tag or stake bar shows side, stake and estimated size ($25 → 59.52 sh) and the exact price that the click sends |
| Clear feedback after a click | Click a price in ARMED (fake venue) | Within one frame a sending chip appears at that level; then placed/filled/rejected, with a toast that names side, outcome and price |
| SAFE is obvious | Toggle SAFE/ARMED on each screen | The switch state is visible on every screen; trade clicks in SAFE do nothing and ring the switch |
| Cancel All always reachable | Every screen and every open sheet at 390 | The header Cancel All is never covered by a sheet or the palette |
| Price never moves under the cursor | Hover a ladder and the board while the book moves | The ladder does not re-centre; rows do not insert, remove or reorder under the pointer (deferred N updates pill instead) |
4. One number everywhere
| Check | How | Pass |
|---|---|---|
| Same value, same text | Pick one position; read size, average, P&L, liquidation value on board, dock, portfolio, ladder header and palette Held badge | Identical rounding, units and sign on every screen |
| Rounding | Prices at the market tick (42¢, 42.5¢ on a 0.001 tick); averages rounded for display | Never 28.8571¢; averages to the tick or one decimal (auto for more than one decimal) |
| Signs | All P&L | Always signed with U+2212 minus: +$12.40, −$3.10; colour is never the only cue |
| Units | Money $1,234.56, compact $1.2k in dense cells; shares sh; prices ¢ | One unit style per concept across screens |
5. Trust
| Check | How | Pass |
|---|---|---|
| Stale looks stale | Ghostframe set_blocked_urls ["*/ws*"], or stop the backend | Prices stay visible but grey, stop flashing, cannot be clicked, and show their age (Stale 6s) (auto: stale.spec) |
| No value from before a reconnect | Stop the backend, change nothing, start it; watch the first frames after reconnect | No stale value is shown as live; prices return live only after the new snapshot |
| Age of account data | Portfolio and top bar while the user feed is down | Account figures are marked stale; order updates paused note shows |
6. Every state designed
Capture each state at 1440 and 390 (§ Screenshot pass).| State | How to produce it | Pass |
|---|---|---|
| Backend down | Stop the backend | Status Disconnected, whole app stale, trade clicks disabled; no raw Failed to fetch |
| Reconnecting | Stop the backend, screenshot within 10 s | Status Reconnecting (or the higher-priority state), spinner |
| Venue 429 / maintenance / cancel-only | Stress mode or a 429/503 from the fake venue | Factual toasts (Not placed · rate limited (429), retry after 2s), status pill Cancel-only |
| No credentials | Run without venue credentials (dev host setup) | Orders and cash panels say which credential is missing; nothing looks broken |
| Geoblocked | Dev host (US) | Status Geoblocked · US-VA; switch shows OFF with the reason |
| Empty | New data dir: empty watchlist, no positions, no orders | Designed empty states with one line and at most one action |
| First run | Fresh isolated context, first login | Login focused, theme paints without a flash, board loads with skeletons only on the cold load |
7. Complete interaction states
| Check | How | Pass |
|---|---|---|
| Hover | Hover every control type | A visible change on fine pointers only |
| Focus | Tab through each screen | A 2 px --ring outline, 1 px offset, on every focusable element |
| Pressed | Press and hold | Pressed state within a frame; nothing waits for an animation |
| Disabled with a reason | Hover or focus a disabled control | Tooltip or accessible name says why (Trading comes in Phase 3, No open orders) |
| Pending | Slow 3G, then save a setting or pin | Pending state (spinner or sending chip) until the server answers |
| Error | Block */api/*, then act | Factual error in place or as a toast; the control returns to a usable state |
8. Keyboard and focus
| Check | How | Pass |
|---|---|---|
| Logical order | take_snapshot, then Tab through | Order follows the visual order: top bar, rail, page, dock |
| Dialogs and sheets trap and return | Open the palette, menu, hotkey sheet, ladder sheet; Tab around; close | Focus stays inside while open and returns to the opener on close |
| Esc | Esc in each overlay, during a drag, with a selection | Closes the top overlay, else cancels the drag, else clears the selection |
| Hotkeys never fire while typing | Type g p, ?, `, b into the palette and any input | Text only; no navigation or trade (auto for g p). Only Cancel All (Mod+Shift+X) works in inputs |
| Navigation hotkeys in SAFE | Mod+K, /, ?, g l/w/p/o/a/s, F6, ` | Each works in SAFE |
9. Scrolling
| Check | How | Pass |
|---|---|---|
| No nested-scroll traps | Wheel and swipe over the board, a docked ladder, portfolio, dock | One scroll container per region; the page itself does not scroll behind a panel |
| Sticky headers | Scroll the board and portfolio | League and group headers stick below the panel header |
| Body lock under sheets | Open a phone sheet, swipe inside and outside | The page under the sheet does not move |
| Preserved position | Scroll the board, open an event, go back | Scroll position restored per route |
10. Viewports
At 768, 1024, 1280, 1440, 1920 and 2560 wide (height 900), and at 390 × 844 portrait and 844 × 390 landscape (Ghostframeemulate with ,mobile,touch[,landscape]):
| Check | Pass |
|---|---|
| Top bar and headers fit (auto at 1024–2560 and 390) | No control past the viewport; no wrapped labels; page never scrolls sideways |
| Dock slots | 4 ladder slots from 1280 up, fewer below; opening a slot never shifts a price column left of it |
| Breakpoint edges | 767 and 768 both render one layout cleanly |
| Landscape phone | Header, tab bar and a sheet leave usable content height |
11. 200% zoom and large system fonts
| Check | How | Pass |
|---|---|---|
| 200% zoom | Desktop at 1440, browser zoom 200% (or resize_page to 720 × 450 at DPR 2) | Everything reachable; no clipped text in controls; reflow without sideways scroll |
| Large fonts | Phone with evaluate_script setting document.documentElement.style.fontSize = "20px" | Labels wrap or truncate cleanly; touch targets stay 44 px |
12. Phone specifics
| Check | How | Pass |
|---|---|---|
| Safe areas | iPhone-sized emulation; on a device, notch and home indicator | Header and tab bar pad by env(safe-area-inset-*); nothing under the notch or home bar |
| On-screen keyboard | Open the palette and type (real device or iOS simulator) | Results stay above the keyboard; the input is not covered |
| Long-press vs scroll | Long-press an order row, then scroll starting on a price | Long-press (500 ms) cancels or opens the action sheet; a moving finger scrolls and never trades |
| iOS Safari viewport | Real iPhone: address bar collapsing, 100dvh | No jump of the tab bar; sheets fit the visible viewport |
13. Cross-browser
task e2e covers Chromium, Firefox and WebKit at 1440 and Chromium at 390. Mobile WebKit is
configured but crashes on the Windows host (e2e.md); check 390 in Safari on a
Mac or iPhone by hand each phase: login, board, a ladder sheet, portfolio.
14. Accessibility
| Check | How | Pass |
|---|---|---|
| Contrast in every state and theme (auto: axe) | node docs/design/contrast.mjs; evaluate_script computed colours on stale, disabled, flash, warning chips in both themes | 4.5:1 for text, 3:1 for large text and UI parts, including during the price flash |
| Names | take_snapshot | Every button and link has a name that says what it does; icon buttons included |
| Live regions | Screen reader or snapshot while prices tick | Toasts and status changes announce; ticking prices do not |
| Target size | Phone snapshot | 44 px targets (24 px minimum per WCAG 2.2) |
15. Performance under live load
| Check | How | Pass |
|---|---|---|
| 50+ live games | SESAME_FAKE_VENUE_STRESS=1, board and dock with 4 ladders; emulate cpuThrottlingRate: 4 | Scrolling stays smooth; clicks respond within a frame; no dropped input |
| Long sessions | Leave the board open for an hour | No memory growth trend, no slowed ticks |
| Background tab | Hide the tab 5 minutes, return | Data resyncs at once; no burst of stale animations or toasts |
16. Consistency
| Check | Pass |
|---|---|
| Terminology | One word per concept on every screen (Exit, Cancel all, Watchlist, Pin) |
| Icons | One icon per action; outline pin = not pinned, filled = pinned |
| Shared formatters | Every price, size and money value comes from lib/format (review the diff for ad-hoc toFixed) |
17. Time
| Check | How | Pass |
|---|---|---|
| Local display, UTC limits | emulate timezone to Los Angeles and Tokyo | Start times show in local time; day boundaries (Today, Fills today) are stated and consistent |
| Relative times tick | Watch Stale 6s, 1h ages, countdowns for a minute | They update at least every second (ages) or minute (times); no frozen or negative values |
18. Visual simplicity
| Check | How | Pass |
|---|---|---|
| Colour only for meaning | Screenshot every screen in both themes | Green/red for bid/ask, buy/sell, profit/loss, plus one accent; everything else greyscale |
| Hierarchy by weight and muted colour | Compare type across a screen | About 3 sizes and 2 weights; secondary text muted rather than smaller and smaller |
| Two surfaces | Count background colours | At most two surface colours; shadows only on overlays (dialogs, popovers, sheets, toasts) |
| Tables | Portfolio, orders, dock, board | No zebra stripes or vertical lines; numbers right-aligned, text left; muted headers |
| Units in headers, no trailing zeros | Read the size and price columns | Size (sh) in the header, 150 in the cell; no 150.00 sh (auto) |
| Blank for nothing | Rows without a position, orders or P&L | Blank cell, not 0, ×0 or — (auto on the portfolio table) |
| No Buy/Sell text under a labelled column | Desktop board and watchlist | Buttons under Buy/Sell headers show only the price (auto) |
| Compact numbers | Dense cells | Compact ($1.2k, 12.5k) with full precision in a tooltip |
| Quiet status | Status pill and feed indicators when all is well | A muted dot; colour and words only when something changed or blocks |
| Row actions on hover and focus | Desktop rows; the same rows on touch | Secondary actions and details appear on hover or focus on desktop and stay visible on touch |
| One primary per area | Each panel and dialog | At most one filled primary button per area |
19. Icons and text
| Check | How | Pass |
|---|---|---|
| No emoji or pictographs (auto) | evaluate_script returning document.body.innerText minus <kbd>, tested against /\p{Extended_Pictographic}/u and the arrow, geometric-shape and dingbat ranges; repeat under reduced motion (emulate has no switch, so use the OS setting or Playwright’s reducedMotion) | None in UI, toasts, notifications, Telegram text or docs. Triangles, ticks, crosses, stopwatch, bell and pin are lucide icons. · – − → ¢ and ↵ ⇧ ⌥ inside <kbd> are text |
| Icon only for known repeated actions | Hover each icon-only control on desktop; take_snapshot for names | Only close, cancel an order (X), search, settings, chevrons, steppers, pin, alert, overflow, external link, theme; each has an aria-label (auto) and a desktop tooltip |
| Text only for money and state actions | Look at Buy, Sell, Exit, Join, Add, Arm/Safe, Save, confirmations, tabs, filters, headers, status words | Text, no icon |
| Icon and text only for navigation and Cancel all | Rail, bottom nav, Cancel all | Icon plus label; menus stay text unless every item has an icon |
| No decorative icons; one icon size | Headings, labels, numbers | No icon beside them; 16 px desktop, 20 px touch, currentColor, muted unless meaningful |
20. Copy
| Check | How | Pass |
|---|---|---|
| Sentence case (auto) | Read every title, button, tab, menu, header and badge | Open orders, Cancel all, Armed; no all-caps words (abbreviations such as NBA, USD, GTC, P&L stay), no text-transform: uppercase, no letter-spaced upper-case labels |
| Glossary | Compare labels, toasts and docs with docs/design/glossary.md | One term per concept: Cancel (not Remove), Exit (not Close), Stake, Armed/Safe, sh; no synonyms |
| British spelling | Read the screens and toasts | colour, unrealised, cancelled, favourite |
| Plain words | Read toasts and empty states | No “instantly”, “simply”, “just”, “seamless”; no “Oops”, “Sorry”, “Please”, “Click here”; no exclamation marks (auto) |
| Errors | Trigger rejects (13 sh, SAFE, band) and failures | Brief, factual (Order rejected: price outside the band); no blame, no advice |
| Punctuation | Labels, tooltips, toasts | No trailing full stop on single sentences; … only on actions that open a dialog; − for negatives, – for ranges, · as separator, curly quotes |
| Times | Order and fill ages, game starts | Relative under 24 h (2m ago, in 12m), then 30 Sep 14:05 (24-hour), exact local time in a tooltip |
21. Numbers and states
| Check | How | Pass |
|---|---|---|
| One format per quantity | Compare prices, money, P&L and sizes across screens | From lib/format only; $ before, ¢ after; separators; P&L signed +/−; tabular digits; truncation ends in … with the full text in a tooltip |
| No broken values (auto) | Every route, both themes, 1440 and 390 | Never undefined, NaN, null, [object Object], Invalid Date, placeholder or debug text; a missing value is blank |
| No console errors (auto) | Ghostframe list_console_messages on each route | No errors or uncaught exceptions |
| Four states per data view | Slow 3G (loading), new data dir (empty), block */api/* (error), block */ws* (stale) | Skeleton with the final geometry; one line and at most one action; what failed plus Retry; greyed with age. Disabled buttons say why |
| Undo over confirm | Bulk or destructive actions that can be undone | Undo toast, no confirm dialog; Cancel all shows its count and needs no dialog |
22. Polish and accessibility
| Check | How | Pass |
|---|---|---|
| Focus ring everywhere (auto for top bar and one table) | Tab through each screen | A visible ring on every interactive element, in a logical order |
| Contrast | node docs/design/contrast.mjs; axe (auto) | AA in both themes: 4.5:1 text, 3:1 controls and focus rings; buy/sell and P&L never colour only |
| Zoom and motion | 200% zoom; reduced motion | Everything works; nothing moves that should not |
| Two radii, 4 px spacing | Inspect cards, controls, overlays | At most two border-radius values; spacing on the 4 px scale |
| Titles and icons (auto for titles) | Tab strip, home-screen install | document.title per route (Orders · sesame); favicon, theme-color per theme, home-screen icon |
| Hotkeys in tooltips | Hover icon-only controls with a hotkey | Tooltip shows it (Cancel all · Ctrl Shift X) |
23. Alerts and notifications (Phase 4)
Specs: alerts.md and notifications.md. Items marked (auto) are inalerts.spec.ts, notifications.spec.ts and the Phase 4 rows of
e2e.md.
| Check | How | Pass |
|---|---|---|
| One-click ladder alert (auto) | Hover a ladder level and click the bell; on a phone, long-press a level and choose Set alert at … | One action; toast Alert set · {condition} with Undo; the level keeps its marker; the row is in /alerts |
| Direction from the level (auto for the midpoint) | Set a bell above the midpoint and one at or below it | Above reads bid at or above, at or below reads ask at or below; neither holds when set |
| Fires within 2 s (auto, fake venue) | With the exit gate’s live run: a price alert on a moving market | Toast (and desktop notification when the tab is hidden) within 2 s of the book change; the history row; Telegram within 2 s when configured |
| Game menu alerts (auto for score) | Board [ellipsis] or right-click; phone card [ellipsis] or long-press | Score change, game start, game end as checkbox items; start disabled with Game already live; everything disabled with Game ended |
| Alerts list (auto for pause, resume, delete, 200) | /alerts at 1440 and 390, with the stress venue | Rows 36 px (64 px phone); hover actions on desktop, [ellipsis] on touch; Delete leaves at once with Undo and sends nothing until the toast closes; the 200th alert disables Set alert… with its tooltip |
| Now column and stale | Block */ws*; also the stress book that never loads | Values grey with Stale Ns · the alert does not fire while stale; nothing is filled from elsewhere |
| Edit form | Click a row or a ladder marker; phone: tap a row | Popover on desktop, bottom sheet on a phone; tick stepper, now value, validation lines; Esc and [x] close without saving |
| History (auto for paging and mark read) | Inbox popover, phone menu sheet, /notifications | Newest first, unread dot and weight, Load older by 50; opening marks read in every tab; rows with a target open it |
| Unread badge (auto) | Make over 99 unread | Rail, inbox, phone menu and the Notifications tab all read 99+, --secondary, never red or green |
| Toasts per severity | Send test; a fill; a reject | Info 4 s (alerts 6 s), warning 8 s with a stripe, critical stays; one toast per event (see NT-01); nothing covers Cancel all |
| Settings > Notifications (auto for save, failure, test, Telegram) | Toggle rules, fail a save (block */api/*), Send test | Saves on its own, reverts on failure with Not saved · …, test cooldown tooltip counts down; Not configured · Telegram runbook without Telegram |
| Desktop permission (auto in Chromium) | Fresh browser profile | Never asked on load or login; the first Desktop checkbox asks; denied disables the column with Blocked in this browser |
| Desktop notification | Allow, turn a Desktop rule on, hide the tab, Send test | One OS notification per notification (several tabs show one), app title only, a click focuses the app on the target; none while a tab is visible |
| Sound | Turn Sound on, set volume 30 and 100, trigger a fill and an alert | One short tone per severity at the set volume, none before the first click or key, one tab plays |
| Feed and account notifications | Stop the backend 40 s (feed down), foreign activity from polymarket.com in a live run | Feed disconnected after 30 s and Feed reconnected after; Activity outside this app sets Safe; copy as in notifications.md |
Screenshot pass
Capture every row at 1440 × 900 and 390 × 844 (touch), in dark and light, first with the fake venue (task dev:fake), then again with SESAME_FAKE_VENUE_STRESS=1. Save as C:\Users\Administrator\Desktop\sesame-screens\<phase>\<screen>-<state>-<width>-<theme>.png
and read each file back before judging it.
| Screen | States to capture |
|---|---|
| Login | empty, wrong password, rate limited (after 5 tries), logging in |
| Live board | live, today, upcoming, empty (league with no games), loading (Slow 3G), stale (WS blocked), deferred N updates pill |
| Board with dock (desktop) | 1, 2 and 4 docked ladders; ladder hovered while the book moves |
| Ladder sheet (phone) | open, order selected (order bar), long-press action sheet |
| Palette | empty query (Recent, Pinned, Held, Live now, commands), results, no matches, > commands, pinned row |
| Event view | moneyline selected, spread group expanded, closed markets, chart ranges, empty range |
| Watchlist | populated, empty |
| Portfolio | open (group by event, league), redeemable with claim link, closed, activity with P&L chart, row expanded with ladder, no credentials |
| Orders | open grouped, filter Buy/Sell/Delayed, fills today, empty, order updates paused |
| Bottom dock (desktop) | collapsed, positions, orders, fills |
| Shell | status pill in every state (live, stale, reconnecting, disconnected, geoblocked, trading off), SAFE, ARMED, Cancel All enabled and disabled |
| Mobile header and tab bar | each route, back button on detail routes, menu sheet, status sheet |
| Settings | account (credentials states), notifications (Telegram not configured, desktop blocked, save failed, test cooldown), risk limits (Phase 3) |
| Alerts (Phase 4) | empty, loading, error, list with every kind and status, stale, at 200, row hovered, edit popover or sheet, phone row menu; stress mode (long names, the tennis score) |
| Notifications (Phase 4) | empty, loading, error, history with unread rows, Load older, inbox popover (desktop), menu sheet (phone), unread badges at 3 and at 99+ |
| Ladder alerts (Phase 4) | bell on a hovered level, marker, paused marker, phone long-press sheet with Set alert at … |
| Toasts | fill, reject, cancel result, error; above the dock; Phase 4: alert set with Undo, alert fired, test, warning and critical stripes, over the phone ladder sheet |
| Trading (Phase 3) | sending, placed, delayed with countdown, partially filled, rejected, checking (unknown outcome), moving (drag) |