Puzzles redesign: a landing, solving on GameScreen with a result dialog, a home with Puzzles, and pages that use the width #450

Closed
opened 2026-10-19 14:25:15 +00:00 by root · 18 comments
root commented 2026-10-19 14:25:15 +00:00 (Migrated from gitlab.core-project.space)

Problem Statement

A Player who clicks Puzzles in the navigation lands straight on a board. Nothing introduces Puzzles, shows their Puzzle rating at a glance, or helps them choose what to train. The Theme picker is a plain select in a sparse aside, beside a bare board that has no move list and no player rows. A solved or failed Puzzle only changes a line of text, so the result and the rating change are easy to miss. The home page offers two doors, Local and Online, and nothing at all about Puzzles. It also leaves most of the screen empty.

The same waste shows on other pages. PageFrame pages are pinned to the left at a fixed width, and pages with little content leave most of the screen blank (see the layout audit under Further Notes).

Solution

  • Home. The home is split into two halves: Puzzles and Play.
    • The Puzzles half shows a signed-in Player their Puzzle rating, a Mixed training (or Resume) button and three Theme shortcuts. It asks a visitor to create an Account.
    • The Play half leads with Online (create a Room, or join one by code). Local becomes a link beneath it.
  • Puzzles landing at /puzzles. It introduces training:
    • The big door. The Puzzle rating sits beside one large door: Mixed training, or Resume when an Attempt is pending.
    • The families. The curated Themes are grouped into Theme families. Each family has its own tab showing its Themes as large cards. A card holds a position illustrating the motif, a one-line description and the Player's progress on that Theme. Every card is a door: picking a Theme starts training on it.
    • Recent Attempts. The last few Attempts link to the full History.
  • Solving at /puzzles/train. Solving moves to its own route, built on GameScreen.
    • Board column. The board is framed by a Puzzle row and the Player's row.
    • Rail. The rail shows the Theme being trained and a move list. Clicking the Theme opens a Theme switcher in place, so the Player can change Theme without leaving the screen.
  • Result dialog. A dialog opens when a Puzzle is solved or failed on the board. It shows what happened and the rating change, with Next, Retry and Review.
  • PageFrame is centred and uses the width, which fixes My games, the profile, the account and the History together.
  • Every screen is designed for 390, 1280 and 1920 px. On a 1920 px screen, GameScreen lets the board grow with the window height, which also helps Local, Room and Game review. The landing and PageFrame widen to 80 rem, and each home half centres its content.

The design was settled in two prototype rounds, and the owner picked home G, landing E and train screen C. All variants live on the throwaway branch prototype/puzzles-ui-redesign; see Further Notes.

User Stories

Home

  1. As a signed-in Player, I want the home to show a Puzzles half beside a Play half, so that training is as easy to reach as playing.
  2. As a signed-in Player, I want the Puzzles half to show my Puzzle rating and a Mixed training button, so that I can start in one click.
  3. As a signed-in Player with a pending Attempt, I want the Puzzles half's button to read Resume, so that I know I will land on the Puzzle I left.
  4. As a signed-in Player, I want three Theme shortcuts in the Puzzles half (the three I have tried least) and a link to every Theme, so that I can train a motif straight from the home.
  5. As a visitor, I want the Puzzles half to explain what Puzzles are and offer to create an Account, then bring me back to the Puzzles landing once I have, so that I can try them.
  6. As a Player, I want the Play half to lead with creating or joining an online Game, with Local as a secondary link, so that the common case comes first.
  7. As a Player on a phone, I want the two halves stacked, Puzzles first, so that each reads at full width.

Puzzles landing

  1. As a Player, I want Puzzles in the navigation to open a landing page that introduces training, so that I see where I stand before I start.
  2. As a Player, I want my Puzzle rating at the top of the landing, with the provisional mark while it applies, so that I know my level.
  3. As a Player, I want one big Mixed training door, so that starting needs no choice.
  4. As a Player with a pending Attempt, I want that door to read Resume and take me back to that Puzzle, so that leaving a Puzzle never loses it.
  5. As a Player, I want the Themes grouped into Theme families that I switch between, each shown as large cards with an illustrating position and a one-line description, so that I understand a Theme before I train on it.
  6. As a Player, I want each family's tab to show its share of solved Attempts, and each card my solved and tried counts for that Theme, or "New" when I have not tried it, so that I see where I am weak.
  7. As a Player, I want a Theme card to start training on that Theme in one click, so that choosing and starting are one gesture.
  8. As a Player, I want my last five Attempts as small boards with their rating change and a "See all" link to the History, so that I can review recent work.
  9. As a new Player with no Attempt yet, I want the recent strip to say my Puzzles will appear there, and every card to read New, so that the empty page still makes sense.
  10. As a Player, I want each block of the landing to show a placeholder while it loads, so that the page does not jump.

Solving

  1. As a Player, I want the solving screen built like a Game screen, with the board, a row for the Puzzle above it, a row for me below it, and a rail with the moves, so that Puzzles feel part of the same app.
  2. As a Player, I want the Puzzle row to keep the Puzzle's rating hidden until the Puzzle is resolved, then show it, so that the rating does not hint at the difficulty.
  3. As a Player, I want my row to show my Puzzle rating, and its change once the Attempt is rated, so that I see the effect of each Puzzle.
  4. As a Player, I want a move list with the opponent's first Move and every Move played since, which I can step back through as in a Game, so that I can follow the line.
  5. As a Player, I want the rail header to show the Theme I am training, so that I know what I am on.
  6. As a Player, I want to click that Theme to open a switcher with Mixed and every Theme grouped by family, plus a link to the landing. Picking a Theme there applies from the next Puzzle, and the switcher tells me so, so that I can change course without leaving the board or skipping a pending Attempt.
  7. As a Player, I want the status line to tell me to find the best move for my Color, so that I know it is my turn.
  8. As a Player, I want Hint and Show solution in the rail's footer while I play, with Hint still asking me to confirm during a rated Attempt, so that I can get help knowingly.
  9. As a Player, I want Show solution to play the rest of the line into the move list, with the status line saying it is the solution and no dialog, since I chose it.
  10. As a Player replaying a Puzzle, I want an Unrated badge in the rail header, so that I know nothing is at stake.
  11. As a Player, I want the solving frame to show while the next Puzzle loads, so that the layout does not jump between Puzzles.
  12. As a Player who has tried every Puzzle of a Theme, I want the frame to say so in the status line and offer Mixed training in the footer.
  13. As a Player who has tried every Puzzle there is, I want the frame to say so in the status line and offer the landing in the footer.
  14. As a Player opening the Replay of a Puzzle I never tried, I want the frame to say I can only replay a Puzzle I tried, and offer the History.

Result dialog

  1. As a Player who solves a rated Attempt, I want a dialog saying it is solved, with my new Puzzle rating and its change, Next as the main action and Review, so that I cannot miss the result.
  2. As a Player who fails a rated Attempt, I want a dialog naming the right Move, with my new Puzzle rating and its change, and Next, Retry and Review, so that I learn the answer and can try again unrated.
  3. As a Player who solves or fails a Replay, I want the same dialog, with "Unrated" in place of the rating line.
  4. As a Player, I want Review to close the dialog and leave the board and move list in place, so that I can study the final position.
  5. As a Player, I want Next after a Replay opened from the History to take me to Mixed training, so that I carry on.

Space

  1. As a Player, I want My games, my profile, my account and the Puzzle History centred on the page and using its width, so that wide screens are not half empty.
  2. As a Player on a 1920 px screen, I want the board of every board screen (solving, Replay, Local, Room, Game review) to grow with my window's height, so that it is not a small square in a big empty page.
  3. As a Player on a 1920 px screen, I want the home, the landing and the PageFrame pages to spread their content across the width, so that nothing hugs one side of the page.

Screens

Home /

  • Entry and exit:
    • Entry: the site name, the "New game" nav entry, a redirect that asks for the home heading.
    • Puzzles half:
      • Mixed training or Resume → /puzzles/train.
      • A Theme shortcut → /puzzles/train?theme=<theme>.
      • "All Themes" → /puzzles.
      • A visitor's "Create an Account" → /sign-up?redirect=/puzzles.
    • Play half:
      • Create → the new Room.
      • Join → /room/$code.
      • "or on one screen" → /local.
  • Template: the home's doors (rule 8): two doors, each under a LandingHeading. The Puzzles door is a puzzles widget that the route hands to the home page, as the route already hands it Room creation. The home vertical cannot import the puzzles vertical.
  • Wireframe (1280 px):
┌sidebar┬────────────────────────────────┬────────────────────────────────┐
│       │ (muted)                        │                                │
│       │  Puzzles                       │  Play                          │
│       │  Find the best move. Rated on  │  You get a link to send        │
│       │  your first try.               │                                │
│       │                                │  [ Create a game            ]  │
│       │  Puzzle rating                 │  [code or invite link] [Join]  │
│       │  1523 ⓘ     [ Mixed training ] │                                │
│       │                                │  or on one screen →            │
│       │  ┌────┐ ┌────┐ ┌────┐          │                                │
│       │  │░░░░│ │░░░░│ │░░░░│          │  ┌──────────┐                  │
│       │  │Fork│ │Mat2│ │Pin │          │  │ still    │                  │
│       │  └────┘ └────┘ └────┘          │  │ board    │                  │
│       │  All Themes                    │  └──────────┘                  │
└───────┴────────────────────────────────┴────────────────────────────────┘

Phone: the halves stack, Puzzles first, each at its content's height (not a full screen each).
1920 px: the halves stay full-bleed, and each half centres its content in a column of at most 36 rem, so the Puzzles shortcuts and the Play actions sit mid-half rather than against its left edge.

  • States:
    • Signed-in, no pending Attempt: the button reads Mixed training.
    • Signed-in, pending Attempt: it reads Resume.
    • Signed-in, rating loading: the rating and the shortcuts show placeholders, and the button already works.
    • Visitor: the pitch line, a still board and "Create an Account" replace the rating, the button and the shortcuts.
    • Creating a Room: Create is disabled while in flight, as today.
    • An empty or invalid code: unchanged from today; the room route judges every non-empty code.

Puzzles landing /puzzles

  • Entry and exit:
    • Entry: Puzzles in the sidebar and the phone bar; the home's "All Themes"; sign-up returning a visitor; "Change Theme" → "All Puzzles" in the switcher.
    • Exit:
      • The big door → /puzzles/train (Resume serves the pending Attempt).
      • A Theme card → /puzzles/train?theme=<theme>.
      • A recent Attempt → /puzzles/replay/$puzzleId.
      • "See all" → /puzzles/history.
    • A visitor is sent to sign-in, as today.
  • Template: none of the four fits; the owner settled it with the prototype (variant E). It reuses the PageFrame width rule from this spec (centred, same max width) and the cards' still boards.
    • Header band: title, Puzzle rating, the big door.
    • Body: a family list on the left; the picked family's cards on the right; recent Attempts under the family list.
  • Wireframe (1280 px):
┌sidebar┬──────────────────────────────────────────────────────────────────┐
│       │ (muted band)                                                     │
│       │  Puzzles   Puzzle rating      ┌───────────────────────────────┐  │
│       │            1523 ⓘ             │░░│ Mixed training           › │  │
│       │                               │░░│ Every Theme, at your level  │  │
│       │                               └───────────────────────────────┘  │
│       ├──────────────────────────────────────────────────────────────────┤
│       │ ┌────────────────┐  ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│       │ │▓Mates    5·77%▓│  │ ░░░░░░░░░░ │ │ ░░░░░░░░░░ │ │ ░░░░░░░░░░ │ │
│       │ │ Tactics  5·63% │  │ ░░board░░░ │ │ ░░board░░░ │ │ ░░board░░░ │ │
│       │ │ Material 2·88% │  │ Mate in 1  │ │ Mate in 2  │ │ Mate in 3  │ │
│       │ │ Techniq. 5·38% │  │ one line   │ │ one line   │ │ one line   │ │
│       │ │ Endgames 3·67% │  │ ▬▬▬▬ 21/24 │ │ ▬▬ 7/12    │ │ ▬ 1/3      │ │
│       │ └────────────────┘  └────────────┘ └────────────┘ └────────────┘ │
│       │  Recent    See all  ┌────────────┐ ┌────────────┐                │
│       │  ▢ ▢ ▢ ▢ ▢          │ Back rank  │ │ Smothered  │                │
│       │  +9 +6 -14 +8 -12   │ ▬▬▬ 8/9    │ │ New        │                │
│       │                     └────────────┘ └────────────┘                │
└───────┴──────────────────────────────────────────────────────────────────┘

Phone: the header stacks (title and rating, then the door at full width). The families become one row of chips that scrolls sideways. The cards stack in one column, two from sm. Recent comes after the cards.
1920 px: the page widens to 80 rem, centred, and the cards run four to a row from 2xl.

  • States:
    • Loading: the rating, the family rates, the cards' progress and the recent strip each show a placeholder. The cards' boards and words are static, so they render at once.
    • No Attempt yet: every card reads New, every family rate reads New, and the recent strip reads "Your Puzzles will appear here".
    • Pending Attempt: the door reads Resume with its own line. The Theme cards still work, but the pending Attempt is served first (the API's rule).
    • Provisional rating: an InfoTip beside the rating, as today.
    • The picked family is kept in the address (?family=), so Back and a reload keep it. The default is the first family.

Solving /puzzles/train (and the Replay at /puzzles/replay/$puzzleId)

  • Entry and exit:
    • Entry: the landing's door and cards; the home's button and shortcuts; the switcher; Next. The Replay comes from a History row or a recent Attempt.
    • Exit:
      • "All Puzzles" in the switcher → /puzzles.
      • Next in the footer or the dialog → the next Puzzle on the same Theme. After a Replay it goes to /puzzles/train.
      • A refusal's way out → /puzzles/train, /puzzles or /puzzles/history.
    • A visitor is sent to sign-in, as today.
  • Template: GameScreen.
    • Board column: the Puzzle row above the board (name "Puzzle", its rating hidden until resolved) and the Player's row below (Username, Puzzle rating and change; "to move" while playing).
    • Rail:
      • Header: the Theme switcher button and the Unrated badge on a Replay. The Replay shows "Replay" in place of the switcher.
      • Status: the visible status line.
      • Move list: with review controls.
      • Footer: Hint and Show solution at the start while playing; Next at the end once resolved.
  • Wireframe (1280 px):
┌sidebar┬──────────────────────────────────────────────┬─────────────────────┐
│       │ ● Puzzle   rating hidden                     │ [Back rank ▾]       │
│       │ ┌──────────────────────────────────────────┐ │ Find the best move  │
│       │ │                                          │ │ for White           │
│       │ │                                          │ ├─────────────────────┤
│       │ │                 board                    │ │ MOVES   |< < > >|   │
│       │ │                                          │ │ 1. … Bb2            │
│       │ │                                          │ │                     │
│       │ │                                          │ │                     │
│       │ └──────────────────────────────────────────┘ ├─────────────────────┤
│       │ ○ LeaMartin  1523               YOUR MOVE    │ [Hint] [Solution]   │
└───────┴──────────────────────────────────────────────┴─────────────────────┘

Phone: GameScreen's own reflow (the rail under the board, its move list capped).
1920 px: the board grows with the window height up to 56 rem (about 830 px on a 1080 px screen, against 640 px today), and the frame widens to keep the rail beside it.

  • States:
    • Loading the next Puzzle: the frame with an empty board, the rows' words as placeholders and the footer disabled.
    • Playing a rated Attempt: as drawn; Hint asks to confirm (existing dialog).
    • Opponent's first Move playing: the status line reads whose move it is; no Move can be made.
    • Refused Move: the board rolls back silently, as today.
    • Hint taken: the piece to move is highlighted; during an Attempt it fails the Attempt, so the Player's row shows the rating change and play goes on unrated with the Unrated badge.
    • Show solution: the rest of the line plays into the move list; the status line reads "Here is the solution"; no dialog; Next in the footer.
    • Solved or failed on the board: the result dialog opens. Behind it, the Puzzle row reveals the Puzzle's rating, the Player's row shows the change (none on a Replay), and Next is in the footer.
    • Retry (from the dialog after a failure): the board resets to the opening Move, as a Replay, with the Unrated badge.
    • NO_PUZZLE_LEFT with a Theme: the frame with an empty board; the status line reads "You have tried every Puzzle"; the footer offers Mixed training.
    • NO_PUZZLE_LEFT with no Theme: the status line reads "You have tried every Puzzle"; the footer offers the landing.
    • NOT_REPLAYABLE (Replay route): the status line reads "You can only replay a Puzzle you tried"; the footer offers the History.

Theme switcher (dialog over Solving)

  • Entry and exit:
    • Entry: the Theme button in the rail header.
    • Exit:
      • Mixed or a Theme → /puzzles/train[?theme=], closing the dialog.
      • "All Puzzles" → /puzzles.
      • Close or Escape → back to the board.
  • Template: the design-system Dialog, wide (sm:max-w-3xl), its body scrolling within 60 % of the viewport height.
  • Wireframe:
┌ Change Theme ───────────────────────────────────────── × ┐
│ The Puzzle in play stays pending: the Theme you pick     │
│ applies from the next Puzzle.                            │
│ [ Mixed training                                       ] │
│ Mates                                                    │
│ [▢ Mate in 1] [▢ Mate in 2] [▢ Mate in 3]                │
│ [▢ Back rank] [▢ Smothered]                              │
│ Tactics …                                                │
│                                            All Puzzles   │
└──────────────────────────────────────────────────────────┘

Phone: one column of Theme buttons, two from sm.

  • States:
    • Open while an Attempt is pending: shows the line about the next Puzzle.
    • Open once the Puzzle is resolved: no such line.
    • The Theme in training is marked as current.

Result dialog (over Solving)

  • Entry and exit:
    • Entry: a Puzzle solved or failed on the board, in an Attempt or a Replay. Hint and Show solution never open it.
    • Exit:
      • Next → the next Puzzle.
      • Retry (failure only) → a Replay of this Puzzle.
      • Review, Close or Escape → the board, left as it ended.
  • Template: the design-system Dialog, default width.
  • Wireframe:
┌ Solved! ─────────────────────── × ┐   ┌ Not the move ───────────────── × ┐
│ Back rank mate found in one move. │   │ The right move was Rd8#.          │
│ Puzzle rating  1532  +9           │   │ Puzzle rating  1511  -12          │
│                                   │   │                                   │
│            [Review] [Next puzzle] │   │   [Review] [Retry] [Next puzzle]  │
└───────────────────────────────────┘   └───────────────────────────────────┘

Phone: the dialog's own calc(100% - 2rem) width.

  • States:
    • Solved, rated: rating line in the primary colour.
    • Failed, rated: the right Move in SAN, rating line in red.
    • Solved Replay or failed Replay: the rating line becomes "Unrated: your Puzzle rating does not change".
    • Next is the focused, primary action.

Puzzle History /puzzles/history

  • Entry and exit: unchanged, plus the landing's "See all"; a row → its Replay. Its empty state's "Train" → /puzzles (the landing).
  • Template: PageFrame, now centred and wider (see Implementation Decisions). No other change to the page.
  • Wireframe: unchanged; the column is centred in the space beside the sidebar. At 1920 px it is 80 rem wide, centred.
  • States: unchanged (loading, empty, empty failed-only, rows, Show more).

Seams

  • API: the puzzle.progress procedure, through the RPC integration harness (integration test). Covers stories 3, 11 and 13 (the pending flag, per-Theme tried and solved counts, Replays not counted, every curated Theme present).
  • Front: the pages on the app's router (@/app/router.harness, view tests with the vertical's MSW handlers). Covers stories 1, 2, 4–10, 12 and 14–37: every state listed under Screens, the switcher, the result dialog and the refusals. The API seam owns the numbers behind stories 3, 11 and 13; their rendering is still asserted here, as part of the page states.
  • E2E: apps/e2e/tests/puzzles.spec.ts (browser). It covers the main stories end to end. First, a signed-in Player goes from the home's Puzzles half to the landing, opens a Theme card, solves the Puzzle, sees the result dialog with the rating change, and presses Next. Second, a visitor goes from the Puzzles half through sign-up and lands on the landing. The existing scenario (solve, fail, History, unrated Replay) moves onto /puzzles/train and asserts the dialog. Stories 38 and 39 are covered by one 1920×1080 check: the solving board is at least 800 px tall, and the landing's and My games' main column is centred within 1 px.

Implementation Decisions

Contracts and API

  • New account-only procedure puzzle.progress → { pending: boolean, themes: { theme, tried, solved }[] }:
    • themes holds every curated Theme in curated order, with zeros for an untried one.
    • tried counts the Account's resolved Attempts on Puzzles carrying that Theme; solved counts the solved ones among them.
    • An Attempt failed by a Hint or by Show solution counts as failed. Replays are never counted, and a pending Attempt is not counted.
    • pending is whether the Account has a pending Attempt.
  • The Puzzle's own rating is added to the solved and failed move results and to the solution result, so the Puzzle row can show it once resolved. It is not on ServedPuzzle, so it never reaches the page before the Puzzle is resolved.
  • next keeps serving the pending Attempt whatever Theme is asked (the service's existing rule). The switcher's "applies from the next Puzzle" line is the front's only change for it.

Front

  • Routes. /puzzles becomes the landing page; solving moves to /puzzles/train, with ?theme= moving with it; /puzzles/replay/$puzzleId renders on the same solving frame. Links to /puzzles that meant "train" (the History's empty state, the Replay's Next) point to /puzzles/train.
  • Solving on GameScreen. The bespoke board-and-aside layout goes; the solving and Replay pages build on GameScreen.
    • The Puzzle and the Player fill its two player rows: the Puzzle's occupant is its rating or "hidden", and the Player's is the Puzzle rating and change.
    • The status prop carries the visible status line.
    • If GameScreen's player-row type cannot say "Puzzle" without a Username, it gains the smallest change that lets it, inside game/.
  • Theme families. They are a front-side grouping of the curated Themes, kept in the puzzles vertical with their names and one-line descriptions in the Paraglide catalogue (English and French):
    • Mates: mate in 1, 2, 3, back rank, smothered.
    • Tactics: fork, pin, skewer, discovered attack, double check.
    • Material: hanging piece, trapped piece.
    • Techniques: deflection, attraction, sacrifice, quiet move, zugzwang.
    • Endgames: endgame, promotion, en passant.
  • Theme illustrations. One FEN per curated Theme, hand-picked from the committed Lichess sample (a position where the motif is visible), kept as a constant in the puzzles vertical. The prototype's positions are placeholders.
  • Home shortcuts. The three Themes with the fewest tried Attempts, ties broken by curated order.
  • Home composition. HomePage takes the Puzzles door as a slot from the route, as it takes createRoom today. The door is a puzzles widget that reads the account, puzzle.rating and puzzle.progress.
  • Queries. The landing reads puzzle.rating, puzzle.progress and the first History page (its first five rows). Every Puzzle mutation invalidates puzzle.progress and puzzle.rating in onSettled (rule 7).
  • PageFrame. It is centred (mx-auto) and widened to the landing's max width (80 rem), keeping its padding. My games, the profile, the account and the History follow with no other change.
  • GameScreen on wide screens. The upper bound of --board-size at lg rises from 40 rem to 56 rem; it stays bound by the window height, as today. The frame's max width rises from 68 rem to 80 rem, so the board and the 19 rem rail still sit side by side. Every GameScreen page (Local, Room, Game review, solving, Replay) gets this.
  • Glossary. The implementation adds Theme family to GLOSSARY.md (a named group of curated Themes, shown to the Player; it changes no Puzzle).
  • ADRs. 0046 (only the Attempt moves the Puzzle rating) is unchanged; no new ADR is needed. Every change here is reversible.

Testing Decisions

  • Prior art:
    • API: apps/api/test/rpc-puzzles.integration.test.ts and rpc-puzzle-history.integration.test.ts.
    • Front: apps/front/src/puzzles/puzzles.test.tsx, puzzles-themes.test.tsx, puzzle-history.test.tsx and home/home.test.tsx, through @/app/router.harness with puzzles.handlers.ts.
    • E2E: the existing apps/e2e/tests/puzzles.spec.ts.
  • Edge and unhappy paths:
    • progress for an Account with no Attempt (all zeros), with only Replays (still zeros), and with a pending Attempt (pending true, not counted).
    • A Puzzle with several curated Themes counts toward each.
    • The landing and the home with a pending Attempt (Resume); with a provisional rating; with every card New.
    • The switcher during a pending Attempt (the line shows; the same Puzzle comes back).
    • The result dialog on each of the four endings; no dialog after a Hint or Show solution.
    • Each refusal on the solving frame: no Puzzle left with a Theme, no Puzzle left at all, not replayable.
    • The Puzzle's rating never appears before the Puzzle is resolved.
  • Browser check (the implement skill): every state above at 390, 1280 and 1920 px. Also check My games, the profile and the account after the PageFrame change, and Local, Room and Game review after the GameScreen change, at the same three widths.

Out of Scope

  • Session statistics on the solving screen (prototype train variant B).
  • Theme progress inside the result dialog.
  • The not-found screens (/games/$id not found) and AuthNotice, which show small centred text in a whole empty page. Also a follow-up.
  • Puzzle data in the account export (#449).

Further Notes

Prototype. Branch prototype/puzzles-ui-redesign (throwaway, never merged). Run pnpm dev, sign in, then:

  • /?variant=G|H (with &as=player|visitor) for the home.
  • /puzzles?variant=D|E|F (with &data=sample|empty|pending) for the landing.
  • /puzzles/train?variant=A|B|C&state=playing|solved|failed|replay&theme=backRankMate for the train screen.

Round 1 (home A–C, landing A–C) is in the branch history. The owner's verdicts:

  • Round 1, home: Local was too prominent, and the page should split between Puzzles and games.
  • Round 1, landing B: choosing a Theme was split from Start, which was unclear, and the Themes read as a plain chip list.
  • Round 2 picks: home G, landing E, train C.

Layout audit, of the pages as they are on main, not the prototypes. It was measured in the browser at 1280×800, 1920×1080 and 390×844, signed in as the demo Account. At 1280 the content area beside the 16 rem sidebar is 1023 px wide.

Route Template Finding In this spec
/ doors Content covers 23 % of the page; 282 px empty above and below; on a phone each door is a full screen for one heading and one button Replaced by home G
/puzzles (today) none Board with no player rows; the aside is sparse; 169 px empty under the board Replaced by landing E and train C
/games, /players/$u, /account, /puzzles/history PageFrame Fixed 896 px, pinned left: 127 px empty on the right at 1280, 767 px at 1920; the account leaves 478 px empty below, an empty History 592 px PageFrame centred and widened
/local, /room/$code, /games/$id GameScreen Fills the width at 1280; at 1920 the board stops at 40 rem (288 px empty each side, 296 px below); the rail is mostly empty before the first Move Board grows to 56 rem, frame to 80 rem
/games/$id not found, AuthNotice none One short line centred in an empty page Follow-up
/sign-in etc. AuthPage Balanced split; the form column is narrow but reads well No change
My games on a phone PageFrame Fine No change

Next step: this spans more than one PR (an API procedure, the landing, the solving screen, the home), so run to-tickets on this spec, then implement-spec.

## Problem Statement A Player who clicks Puzzles in the navigation lands straight on a board. Nothing introduces Puzzles, shows their Puzzle rating at a glance, or helps them choose what to train. The Theme picker is a plain select in a sparse aside, beside a bare board that has no move list and no player rows. A solved or failed Puzzle only changes a line of text, so the result and the rating change are easy to miss. The home page offers two doors, Local and Online, and nothing at all about Puzzles. It also leaves most of the screen empty. The same waste shows on other pages. `PageFrame` pages are pinned to the left at a fixed width, and pages with little content leave most of the screen blank (see the layout audit under Further Notes). ## Solution - **Home.** The home is split into two halves: **Puzzles** and **Play**. - The Puzzles half shows a signed-in Player their Puzzle rating, a Mixed training (or Resume) button and three Theme shortcuts. It asks a visitor to create an Account. - The Play half leads with Online (create a Room, or join one by code). Local becomes a link beneath it. - **Puzzles landing at `/puzzles`.** It introduces training: - **The big door.** The Puzzle rating sits beside one large door: Mixed training, or Resume when an Attempt is pending. - **The families.** The curated Themes are grouped into **Theme families**. Each family has its own tab showing its Themes as large cards. A card holds a position illustrating the motif, a one-line description and the Player's progress on that Theme. Every card is a door: picking a Theme starts training on it. - **Recent Attempts.** The last few Attempts link to the full History. - **Solving at `/puzzles/train`.** Solving moves to its own route, built on `GameScreen`. - **Board column.** The board is framed by a Puzzle row and the Player's row. - **Rail.** The rail shows the Theme being trained and a move list. Clicking the Theme opens a Theme switcher in place, so the Player can change Theme without leaving the screen. - **Result dialog.** A dialog opens when a Puzzle is solved or failed on the board. It shows what happened and the rating change, with Next, Retry and Review. - **`PageFrame`** is centred and uses the width, which fixes My games, the profile, the account and the History together. - **Every screen is designed for 390, 1280 and 1920 px.** On a 1920 px screen, `GameScreen` lets the board grow with the window height, which also helps Local, Room and Game review. The landing and `PageFrame` widen to 80 rem, and each home half centres its content. The design was settled in two prototype rounds, and the owner picked home **G**, landing **E** and train screen **C**. All variants live on the throwaway branch `prototype/puzzles-ui-redesign`; see Further Notes. ## User Stories Home 1. As a signed-in Player, I want the home to show a Puzzles half beside a Play half, so that training is as easy to reach as playing. 2. As a signed-in Player, I want the Puzzles half to show my Puzzle rating and a Mixed training button, so that I can start in one click. 3. As a signed-in Player with a pending Attempt, I want the Puzzles half's button to read Resume, so that I know I will land on the Puzzle I left. 4. As a signed-in Player, I want three Theme shortcuts in the Puzzles half (the three I have tried least) and a link to every Theme, so that I can train a motif straight from the home. 5. As a visitor, I want the Puzzles half to explain what Puzzles are and offer to create an Account, then bring me back to the Puzzles landing once I have, so that I can try them. 6. As a Player, I want the Play half to lead with creating or joining an online Game, with Local as a secondary link, so that the common case comes first. 7. As a Player on a phone, I want the two halves stacked, Puzzles first, so that each reads at full width. Puzzles landing 8. As a Player, I want Puzzles in the navigation to open a landing page that introduces training, so that I see where I stand before I start. 9. As a Player, I want my Puzzle rating at the top of the landing, with the provisional mark while it applies, so that I know my level. 10. As a Player, I want one big Mixed training door, so that starting needs no choice. 11. As a Player with a pending Attempt, I want that door to read Resume and take me back to that Puzzle, so that leaving a Puzzle never loses it. 12. As a Player, I want the Themes grouped into Theme families that I switch between, each shown as large cards with an illustrating position and a one-line description, so that I understand a Theme before I train on it. 13. As a Player, I want each family's tab to show its share of solved Attempts, and each card my solved and tried counts for that Theme, or "New" when I have not tried it, so that I see where I am weak. 14. As a Player, I want a Theme card to start training on that Theme in one click, so that choosing and starting are one gesture. 15. As a Player, I want my last five Attempts as small boards with their rating change and a "See all" link to the History, so that I can review recent work. 16. As a new Player with no Attempt yet, I want the recent strip to say my Puzzles will appear there, and every card to read New, so that the empty page still makes sense. 17. As a Player, I want each block of the landing to show a placeholder while it loads, so that the page does not jump. Solving 18. As a Player, I want the solving screen built like a Game screen, with the board, a row for the Puzzle above it, a row for me below it, and a rail with the moves, so that Puzzles feel part of the same app. 19. As a Player, I want the Puzzle row to keep the Puzzle's rating hidden until the Puzzle is resolved, then show it, so that the rating does not hint at the difficulty. 20. As a Player, I want my row to show my Puzzle rating, and its change once the Attempt is rated, so that I see the effect of each Puzzle. 21. As a Player, I want a move list with the opponent's first Move and every Move played since, which I can step back through as in a Game, so that I can follow the line. 22. As a Player, I want the rail header to show the Theme I am training, so that I know what I am on. 23. As a Player, I want to click that Theme to open a switcher with Mixed and every Theme grouped by family, plus a link to the landing. Picking a Theme there applies from the next Puzzle, and the switcher tells me so, so that I can change course without leaving the board or skipping a pending Attempt. 24. As a Player, I want the status line to tell me to find the best move for my Color, so that I know it is my turn. 25. As a Player, I want Hint and Show solution in the rail's footer while I play, with Hint still asking me to confirm during a rated Attempt, so that I can get help knowingly. 26. As a Player, I want Show solution to play the rest of the line into the move list, with the status line saying it is the solution and no dialog, since I chose it. 27. As a Player replaying a Puzzle, I want an Unrated badge in the rail header, so that I know nothing is at stake. 28. As a Player, I want the solving frame to show while the next Puzzle loads, so that the layout does not jump between Puzzles. 29. As a Player who has tried every Puzzle of a Theme, I want the frame to say so in the status line and offer Mixed training in the footer. 30. As a Player who has tried every Puzzle there is, I want the frame to say so in the status line and offer the landing in the footer. 31. As a Player opening the Replay of a Puzzle I never tried, I want the frame to say I can only replay a Puzzle I tried, and offer the History. Result dialog 32. As a Player who solves a rated Attempt, I want a dialog saying it is solved, with my new Puzzle rating and its change, Next as the main action and Review, so that I cannot miss the result. 33. As a Player who fails a rated Attempt, I want a dialog naming the right Move, with my new Puzzle rating and its change, and Next, Retry and Review, so that I learn the answer and can try again unrated. 34. As a Player who solves or fails a Replay, I want the same dialog, with "Unrated" in place of the rating line. 35. As a Player, I want Review to close the dialog and leave the board and move list in place, so that I can study the final position. 36. As a Player, I want Next after a Replay opened from the History to take me to Mixed training, so that I carry on. Space 37. As a Player, I want My games, my profile, my account and the Puzzle History centred on the page and using its width, so that wide screens are not half empty. 38. As a Player on a 1920 px screen, I want the board of every board screen (solving, Replay, Local, Room, Game review) to grow with my window's height, so that it is not a small square in a big empty page. 39. As a Player on a 1920 px screen, I want the home, the landing and the `PageFrame` pages to spread their content across the width, so that nothing hugs one side of the page. ## Screens ### Home `/` - **Entry and exit**: - Entry: the site name, the "New game" nav entry, a redirect that asks for the home heading. - Puzzles half: - Mixed training or Resume → `/puzzles/train`. - A Theme shortcut → `/puzzles/train?theme=<theme>`. - "All Themes" → `/puzzles`. - A visitor's "Create an Account" → `/sign-up?redirect=/puzzles`. - Play half: - Create → the new Room. - Join → `/room/$code`. - "or on one screen" → `/local`. - **Template**: the home's doors (rule 8): two doors, each under a `LandingHeading`. The Puzzles door is a `puzzles` widget that the route hands to the home page, as the route already hands it Room creation. The home vertical cannot import the puzzles vertical. - **Wireframe** (1280 px): ``` ┌sidebar┬────────────────────────────────┬────────────────────────────────┐ │ │ (muted) │ │ │ │ Puzzles │ Play │ │ │ Find the best move. Rated on │ You get a link to send │ │ │ your first try. │ │ │ │ │ [ Create a game ] │ │ │ Puzzle rating │ [code or invite link] [Join] │ │ │ 1523 ⓘ [ Mixed training ] │ │ │ │ │ or on one screen → │ │ │ ┌────┐ ┌────┐ ┌────┐ │ │ │ │ │░░░░│ │░░░░│ │░░░░│ │ ┌──────────┐ │ │ │ │Fork│ │Mat2│ │Pin │ │ │ still │ │ │ │ └────┘ └────┘ └────┘ │ │ board │ │ │ │ All Themes │ └──────────┘ │ └───────┴────────────────────────────────┴────────────────────────────────┘ ``` Phone: the halves stack, Puzzles first, each at its content's height (not a full screen each). 1920 px: the halves stay full-bleed, and each half centres its content in a column of at most 36 rem, so the Puzzles shortcuts and the Play actions sit mid-half rather than against its left edge. - **States**: - Signed-in, no pending Attempt: the button reads Mixed training. - Signed-in, pending Attempt: it reads Resume. - Signed-in, rating loading: the rating and the shortcuts show placeholders, and the button already works. - Visitor: the pitch line, a still board and "Create an Account" replace the rating, the button and the shortcuts. - Creating a Room: Create is disabled while in flight, as today. - An empty or invalid code: unchanged from today; the room route judges every non-empty code. ### Puzzles landing `/puzzles` - **Entry and exit**: - Entry: Puzzles in the sidebar and the phone bar; the home's "All Themes"; sign-up returning a visitor; "Change Theme" → "All Puzzles" in the switcher. - Exit: - The big door → `/puzzles/train` (Resume serves the pending Attempt). - A Theme card → `/puzzles/train?theme=<theme>`. - A recent Attempt → `/puzzles/replay/$puzzleId`. - "See all" → `/puzzles/history`. - A visitor is sent to sign-in, as today. - **Template**: none of the four fits; the owner settled it with the prototype (variant E). It reuses the `PageFrame` width rule from this spec (centred, same max width) and the cards' still boards. - Header band: title, Puzzle rating, the big door. - Body: a family list on the left; the picked family's cards on the right; recent Attempts under the family list. - **Wireframe** (1280 px): ``` ┌sidebar┬──────────────────────────────────────────────────────────────────┐ │ │ (muted band) │ │ │ Puzzles Puzzle rating ┌───────────────────────────────┐ │ │ │ 1523 ⓘ │░░│ Mixed training › │ │ │ │ │░░│ Every Theme, at your level │ │ │ │ └───────────────────────────────┘ │ │ ├──────────────────────────────────────────────────────────────────┤ │ │ ┌────────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ │ │▓Mates 5·77%▓│ │ ░░░░░░░░░░ │ │ ░░░░░░░░░░ │ │ ░░░░░░░░░░ │ │ │ │ │ Tactics 5·63% │ │ ░░board░░░ │ │ ░░board░░░ │ │ ░░board░░░ │ │ │ │ │ Material 2·88% │ │ Mate in 1 │ │ Mate in 2 │ │ Mate in 3 │ │ │ │ │ Techniq. 5·38% │ │ one line │ │ one line │ │ one line │ │ │ │ │ Endgames 3·67% │ │ ▬▬▬▬ 21/24 │ │ ▬▬ 7/12 │ │ ▬ 1/3 │ │ │ │ └────────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │ │ Recent See all ┌────────────┐ ┌────────────┐ │ │ │ ▢ ▢ ▢ ▢ ▢ │ Back rank │ │ Smothered │ │ │ │ +9 +6 -14 +8 -12 │ ▬▬▬ 8/9 │ │ New │ │ │ │ └────────────┘ └────────────┘ │ └───────┴──────────────────────────────────────────────────────────────────┘ ``` Phone: the header stacks (title and rating, then the door at full width). The families become one row of chips that scrolls sideways. The cards stack in one column, two from `sm`. Recent comes after the cards. 1920 px: the page widens to 80 rem, centred, and the cards run four to a row from `2xl`. - **States**: - Loading: the rating, the family rates, the cards' progress and the recent strip each show a placeholder. The cards' boards and words are static, so they render at once. - No Attempt yet: every card reads New, every family rate reads New, and the recent strip reads "Your Puzzles will appear here". - Pending Attempt: the door reads Resume with its own line. The Theme cards still work, but the pending Attempt is served first (the API's rule). - Provisional rating: an InfoTip beside the rating, as today. - The picked family is kept in the address (`?family=`), so Back and a reload keep it. The default is the first family. ### Solving `/puzzles/train` (and the Replay at `/puzzles/replay/$puzzleId`) - **Entry and exit**: - Entry: the landing's door and cards; the home's button and shortcuts; the switcher; Next. The Replay comes from a History row or a recent Attempt. - Exit: - "All Puzzles" in the switcher → `/puzzles`. - Next in the footer or the dialog → the next Puzzle on the same Theme. After a Replay it goes to `/puzzles/train`. - A refusal's way out → `/puzzles/train`, `/puzzles` or `/puzzles/history`. - A visitor is sent to sign-in, as today. - **Template**: `GameScreen`. - Board column: the Puzzle row above the board (name "Puzzle", its rating hidden until resolved) and the Player's row below (Username, Puzzle rating and change; "to move" while playing). - Rail: - Header: the Theme switcher button and the Unrated badge on a Replay. The Replay shows "Replay" in place of the switcher. - Status: the visible status line. - Move list: with review controls. - Footer: Hint and Show solution at the start while playing; Next at the end once resolved. - **Wireframe** (1280 px): ``` ┌sidebar┬──────────────────────────────────────────────┬─────────────────────┐ │ │ ● Puzzle rating hidden │ [Back rank ▾] │ │ │ ┌──────────────────────────────────────────┐ │ Find the best move │ │ │ │ │ │ for White │ │ │ │ │ ├─────────────────────┤ │ │ │ board │ │ MOVES |< < > >| │ │ │ │ │ │ 1. … Bb2 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────────────────────────┘ ├─────────────────────┤ │ │ ○ LeaMartin 1523 YOUR MOVE │ [Hint] [Solution] │ └───────┴──────────────────────────────────────────────┴─────────────────────┘ ``` Phone: `GameScreen`'s own reflow (the rail under the board, its move list capped). 1920 px: the board grows with the window height up to 56 rem (about 830 px on a 1080 px screen, against 640 px today), and the frame widens to keep the rail beside it. - **States**: - Loading the next Puzzle: the frame with an empty board, the rows' words as placeholders and the footer disabled. - Playing a rated Attempt: as drawn; Hint asks to confirm (existing dialog). - Opponent's first Move playing: the status line reads whose move it is; no Move can be made. - Refused Move: the board rolls back silently, as today. - Hint taken: the piece to move is highlighted; during an Attempt it fails the Attempt, so the Player's row shows the rating change and play goes on unrated with the Unrated badge. - Show solution: the rest of the line plays into the move list; the status line reads "Here is the solution"; no dialog; Next in the footer. - Solved or failed on the board: the result dialog opens. Behind it, the Puzzle row reveals the Puzzle's rating, the Player's row shows the change (none on a Replay), and Next is in the footer. - Retry (from the dialog after a failure): the board resets to the opening Move, as a Replay, with the Unrated badge. - `NO_PUZZLE_LEFT` with a Theme: the frame with an empty board; the status line reads "You have tried every <Theme> Puzzle"; the footer offers Mixed training. - `NO_PUZZLE_LEFT` with no Theme: the status line reads "You have tried every Puzzle"; the footer offers the landing. - `NOT_REPLAYABLE` (Replay route): the status line reads "You can only replay a Puzzle you tried"; the footer offers the History. ### Theme switcher (dialog over Solving) - **Entry and exit**: - Entry: the Theme button in the rail header. - Exit: - Mixed or a Theme → `/puzzles/train[?theme=]`, closing the dialog. - "All Puzzles" → `/puzzles`. - Close or Escape → back to the board. - **Template**: the design-system `Dialog`, wide (`sm:max-w-3xl`), its body scrolling within 60 % of the viewport height. - **Wireframe**: ``` ┌ Change Theme ───────────────────────────────────────── × ┐ │ The Puzzle in play stays pending: the Theme you pick │ │ applies from the next Puzzle. │ │ [ Mixed training ] │ │ Mates │ │ [▢ Mate in 1] [▢ Mate in 2] [▢ Mate in 3] │ │ [▢ Back rank] [▢ Smothered] │ │ Tactics … │ │ All Puzzles │ └──────────────────────────────────────────────────────────┘ ``` Phone: one column of Theme buttons, two from `sm`. - **States**: - Open while an Attempt is pending: shows the line about the next Puzzle. - Open once the Puzzle is resolved: no such line. - The Theme in training is marked as current. ### Result dialog (over Solving) - **Entry and exit**: - Entry: a Puzzle solved or failed on the board, in an Attempt or a Replay. Hint and Show solution never open it. - Exit: - Next → the next Puzzle. - Retry (failure only) → a Replay of this Puzzle. - Review, Close or Escape → the board, left as it ended. - **Template**: the design-system `Dialog`, default width. - **Wireframe**: ``` ┌ Solved! ─────────────────────── × ┐ ┌ Not the move ───────────────── × ┐ │ Back rank mate found in one move. │ │ The right move was Rd8#. │ │ Puzzle rating 1532 +9 │ │ Puzzle rating 1511 -12 │ │ │ │ │ │ [Review] [Next puzzle] │ │ [Review] [Retry] [Next puzzle] │ └───────────────────────────────────┘ └───────────────────────────────────┘ ``` Phone: the dialog's own `calc(100% - 2rem)` width. - **States**: - Solved, rated: rating line in the primary colour. - Failed, rated: the right Move in SAN, rating line in red. - Solved Replay or failed Replay: the rating line becomes "Unrated: your Puzzle rating does not change". - Next is the focused, primary action. ### Puzzle History `/puzzles/history` - **Entry and exit**: unchanged, plus the landing's "See all"; a row → its Replay. Its empty state's "Train" → `/puzzles` (the landing). - **Template**: `PageFrame`, now centred and wider (see Implementation Decisions). No other change to the page. - **Wireframe**: unchanged; the column is centred in the space beside the sidebar. At 1920 px it is 80 rem wide, centred. - **States**: unchanged (loading, empty, empty failed-only, rows, Show more). ## Seams - **API: the `puzzle.progress` procedure**, through the RPC integration harness (integration test). Covers stories 3, 11 and 13 (the pending flag, per-Theme tried and solved counts, Replays not counted, every curated Theme present). - **Front: the pages on the app's router** (`@/app/router.harness`, view tests with the vertical's MSW handlers). Covers stories 1, 2, 4–10, 12 and 14–37: every state listed under Screens, the switcher, the result dialog and the refusals. The API seam owns the numbers behind stories 3, 11 and 13; their rendering is still asserted here, as part of the page states. - **E2E: `apps/e2e/tests/puzzles.spec.ts`** (browser). It covers the main stories end to end. First, a signed-in Player goes from the home's Puzzles half to the landing, opens a Theme card, solves the Puzzle, sees the result dialog with the rating change, and presses Next. Second, a visitor goes from the Puzzles half through sign-up and lands on the landing. The existing scenario (solve, fail, History, unrated Replay) moves onto `/puzzles/train` and asserts the dialog. Stories 38 and 39 are covered by one 1920×1080 check: the solving board is at least 800 px tall, and the landing's and My games' main column is centred within 1 px. ## Implementation Decisions **Contracts and API** - New account-only procedure `puzzle.progress` → `{ pending: boolean, themes: { theme, tried, solved }[] }`: - `themes` holds every curated Theme in curated order, with zeros for an untried one. - `tried` counts the Account's resolved Attempts on Puzzles carrying that Theme; `solved` counts the solved ones among them. - An Attempt failed by a Hint or by Show solution counts as failed. Replays are never counted, and a pending Attempt is not counted. - `pending` is whether the Account has a pending Attempt. - The Puzzle's own rating is added to the `solved` and `failed` move results and to the `solution` result, so the Puzzle row can show it once resolved. It is not on `ServedPuzzle`, so it never reaches the page before the Puzzle is resolved. - `next` keeps serving the pending Attempt whatever Theme is asked (the service's existing rule). The switcher's "applies from the next Puzzle" line is the front's only change for it. **Front** - **Routes.** `/puzzles` becomes the landing page; solving moves to `/puzzles/train`, with `?theme=` moving with it; `/puzzles/replay/$puzzleId` renders on the same solving frame. Links to `/puzzles` that meant "train" (the History's empty state, the Replay's Next) point to `/puzzles/train`. - **Solving on `GameScreen`.** The bespoke board-and-aside layout goes; the solving and Replay pages build on `GameScreen`. - The Puzzle and the Player fill its two player rows: the Puzzle's `occupant` is its rating or "hidden", and the Player's is the Puzzle rating and change. - The status prop carries the visible status line. - If `GameScreen`'s player-row type cannot say "Puzzle" without a Username, it gains the smallest change that lets it, inside `game/`. - **Theme families.** They are a front-side grouping of the curated Themes, kept in the puzzles vertical with their names and one-line descriptions in the Paraglide catalogue (English and French): - Mates: mate in 1, 2, 3, back rank, smothered. - Tactics: fork, pin, skewer, discovered attack, double check. - Material: hanging piece, trapped piece. - Techniques: deflection, attraction, sacrifice, quiet move, zugzwang. - Endgames: endgame, promotion, en passant. - **Theme illustrations.** One FEN per curated Theme, hand-picked from the committed Lichess sample (a position where the motif is visible), kept as a constant in the puzzles vertical. The prototype's positions are placeholders. - **Home shortcuts.** The three Themes with the fewest tried Attempts, ties broken by curated order. - **Home composition.** `HomePage` takes the Puzzles door as a slot from the route, as it takes `createRoom` today. The door is a `puzzles` widget that reads the account, `puzzle.rating` and `puzzle.progress`. - **Queries.** The landing reads `puzzle.rating`, `puzzle.progress` and the first History page (its first five rows). Every Puzzle mutation invalidates `puzzle.progress` and `puzzle.rating` in `onSettled` (rule 7). - **`PageFrame`.** It is centred (`mx-auto`) and widened to the landing's max width (80 rem), keeping its padding. My games, the profile, the account and the History follow with no other change. - **`GameScreen` on wide screens.** The upper bound of `--board-size` at `lg` rises from 40 rem to 56 rem; it stays bound by the window height, as today. The frame's max width rises from 68 rem to 80 rem, so the board and the 19 rem rail still sit side by side. Every `GameScreen` page (Local, Room, Game review, solving, Replay) gets this. - **Glossary.** The implementation adds **Theme family** to `GLOSSARY.md` (a named group of curated Themes, shown to the Player; it changes no Puzzle). - **ADRs.** 0046 (only the Attempt moves the Puzzle rating) is unchanged; no new ADR is needed. Every change here is reversible. ## Testing Decisions - **Prior art**: - API: `apps/api/test/rpc-puzzles.integration.test.ts` and `rpc-puzzle-history.integration.test.ts`. - Front: `apps/front/src/puzzles/puzzles.test.tsx`, `puzzles-themes.test.tsx`, `puzzle-history.test.tsx` and `home/home.test.tsx`, through `@/app/router.harness` with `puzzles.handlers.ts`. - E2E: the existing `apps/e2e/tests/puzzles.spec.ts`. - **Edge and unhappy paths**: - `progress` for an Account with no Attempt (all zeros), with only Replays (still zeros), and with a pending Attempt (`pending` true, not counted). - A Puzzle with several curated Themes counts toward each. - The landing and the home with a pending Attempt (Resume); with a provisional rating; with every card New. - The switcher during a pending Attempt (the line shows; the same Puzzle comes back). - The result dialog on each of the four endings; no dialog after a Hint or Show solution. - Each refusal on the solving frame: no Puzzle left with a Theme, no Puzzle left at all, not replayable. - The Puzzle's rating never appears before the Puzzle is resolved. - **Browser check** (the `implement` skill): every state above at 390, 1280 and 1920 px. Also check My games, the profile and the account after the `PageFrame` change, and Local, Room and Game review after the `GameScreen` change, at the same three widths. ## Out of Scope - Session statistics on the solving screen (prototype train variant B). - Theme progress inside the result dialog. - The not-found screens (`/games/$id` not found) and `AuthNotice`, which show small centred text in a whole empty page. Also a follow-up. - Puzzle data in the account export (#449). ## Further Notes **Prototype.** Branch `prototype/puzzles-ui-redesign` (throwaway, never merged). Run `pnpm dev`, sign in, then: - `/?variant=G|H` (with `&as=player|visitor`) for the home. - `/puzzles?variant=D|E|F` (with `&data=sample|empty|pending`) for the landing. - `/puzzles/train?variant=A|B|C&state=playing|solved|failed|replay&theme=backRankMate` for the train screen. Round 1 (home A–C, landing A–C) is in the branch history. The owner's verdicts: - Round 1, home: Local was too prominent, and the page should split between Puzzles and games. - Round 1, landing B: choosing a Theme was split from Start, which was unclear, and the Themes read as a plain chip list. - Round 2 picks: home G, landing E, train C. **Layout audit**, of the pages as they are on `main`, not the prototypes. It was measured in the browser at 1280×800, 1920×1080 and 390×844, signed in as the demo Account. At 1280 the content area beside the 16 rem sidebar is 1023 px wide. | Route | Template | Finding | In this spec | | --- | --- | --- | --- | | `/` | doors | Content covers 23 % of the page; 282 px empty above and below; on a phone each door is a full screen for one heading and one button | Replaced by home G | | `/puzzles` (today) | none | Board with no player rows; the aside is sparse; 169 px empty under the board | Replaced by landing E and train C | | `/games`, `/players/$u`, `/account`, `/puzzles/history` | `PageFrame` | Fixed 896 px, pinned left: 127 px empty on the right at 1280, 767 px at 1920; the account leaves 478 px empty below, an empty History 592 px | `PageFrame` centred and widened | | `/local`, `/room/$code`, `/games/$id` | `GameScreen` | Fills the width at 1280; at 1920 the board stops at 40 rem (288 px empty each side, 296 px below); the rail is mostly empty before the first Move | Board grows to 56 rem, frame to 80 rem | | `/games/$id` not found, `AuthNotice` | none | One short line centred in an empty page | Follow-up | | `/sign-in` etc. | `AuthPage` | Balanced split; the form column is narrow but reads well | No change | | My games on a phone | `PageFrame` | Fine | No change | Next step: this spans more than one PR (an API procedure, the landing, the solving screen, the home), so run `to-tickets` on this spec, then `implement-spec`.
root commented 2026-10-19 14:44:45 +00:00 (Migrated from gitlab.core-project.space)

Seam change, approved by the owner while splitting this spec into tickets: the API seam widens from the puzzle.progress procedure alone to the puzzle.* procedures through the RPC integration harness. It now also covers the Puzzle's own rating added to the solved, failed and solution results (stories 19 and 32–34 for the number; their rendering stays on the front seam).

Seam change, approved by the owner while splitting this spec into tickets: the API seam widens from the `puzzle.progress` procedure alone to **the `puzzle.*` procedures through the RPC integration harness**. It now also covers the Puzzle's own rating added to the `solved`, `failed` and `solution` results (stories 19 and 32–34 for the number; their rendering stays on the front seam).
root commented 2026-10-19 14:45:15 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #451

mentioned in issue #451
root commented 2026-10-19 14:45:16 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #452

mentioned in issue #452
root commented 2026-10-19 14:45:44 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #453

mentioned in issue #453
root commented 2026-10-19 14:45:45 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #454

mentioned in issue #454
root commented 2026-10-19 14:46:11 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #455

mentioned in issue #455
root commented 2026-10-19 14:46:12 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #456

mentioned in issue #456
root commented 2026-10-19 14:46:30 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #457

mentioned in issue #457
root commented 2026-10-19 14:46:41 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #451

marked this issue as related to #451
root commented 2026-10-19 14:46:42 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #452

marked this issue as related to #452
root commented 2026-10-19 14:46:44 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #453

marked this issue as related to #453
root commented 2026-10-19 14:46:45 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #454

marked this issue as related to #454
root commented 2026-10-19 14:46:46 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #455

marked this issue as related to #455
root commented 2026-10-19 14:46:48 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #456

marked this issue as related to #456
root commented 2026-10-19 14:46:50 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #457

marked this issue as related to #457
root commented 2026-10-19 15:01:58 +00:00 (Migrated from gitlab.core-project.space)

mentioned in merge request !557

mentioned in merge request !557
root commented 2026-10-19 15:15:09 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #458

mentioned in issue #458
root commented 2026-10-19 15:39:16 +00:00 (Migrated from gitlab.core-project.space)

mentioned in merge request !559

mentioned in merge request !559
root (Migrated from gitlab.core-project.space) closed this issue 2026-10-19 19:15:02 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amaurycoudr/chess-project#450
No description provided.