UX Spec: Pause
Status: Reviewed — APPROVED (
/ux-reviewverification pass 2026-08-31: 0 findings; pass 1 2 B / 5 A and pass 2 1 B / 2 A applied same day) · v0.4
Author: Jeroen + ux-designer
Last Updated: 2026-08-31
Journey Phase(s): unknown — no player journey map yet (see Open Questions)
Template: UX Spec
Variants: host and client — same panel, the third button differs (End session vs Leave)
Requirements input: ADR-0007 §1/§3 (MenuEsc / Start, protected; exits build mode first, rule (g); under UI captureMenucloses the top panel instead — pause opens from raw gameplay only);design/ux/settings.md(pause offers a Settings entry; settings' Back returns here); netcode-session quick-spec Rule 5 (host quit ends the session for everyone, no migration; client leave despawns the character, CARRY persists host-side, rejoin = late join; "a save-file fact, not a failure state"); ADR-0004 (StopHost awaits the flush;wantsToQuitlatch);design/accessibility-requirements.md(pause never pauses the world — intentional limitation)
Patterns used: #1 Button · #3 Panel/Modal · #4 Focus & Nav · #6 Reason String — no new patterns
Related specs:design/ux/settings.md(APPROVED — its OQ 7 names this spec),design/ux/build-mode.md("Esc exits then opens pause"),design/ux/hud.md(HUD never dimmed under a panel); title-screen spec does not exist yet
Purpose & Player Need
Pause is the in-game front door: a breather from your own inputs, the way into Settings, and the way out of the session — nothing more. Because the world never stops (host-and-play; the tide is wall-clock), this screen must be honest about what it pauses: your input, not the world. The player arrives wanting to step away safely, change a setting, or leave — and each of those must be one obvious press, because the commonest trigger is real life interrupting (the doorbell, the kid, the kettle). It must never manufacture anxiety about the running world, and it must state plainly what leaving means for you (client) or for everyone (host).
Player Context on Arrival
- First encounter: minutes into the first session — usually a reflexive Esc to "pause the game". The screen's most important first impression: the world behind it visibly keeps moving, teaching the one rule the game can't bend.
- Typical: (a) stepping away mid-session — wants safety in one press (the panel captures input; your character stands still and can still be hit — nothing in this screen hides that); (b) heading to Settings; (c) ending the night — the host wrapping up for everyone, or one friend leaving early.
- Emotional assumption: interrupted, often hurried on the way in; calm on the way out. The screen must open instantly and read in one glance.
- Voluntary: always (it is never shown by the game — disconnects go to the title screen, not here).
Navigation Position
Gameplay → Menu (Esc / Start) → Pause. Top-level while in a session, reachable from any world state — with the two ADR-0007 precedences: in build mode, Menu exits the mode first, then opens pause (rule (g)); while any panel is open, Menu closes that panel instead (panel stack) — including pause itself, which is why a second Menu press closes rather than re-opens (AC-9); so pause opens from raw gameplay only, one press after any panel closes. One child: Settings (design/ux/settings.md — its Back returns here). Downed / Dead: pause opens normally (ordinary screen — the standing precedent). The title screen is this screen's exit target and does not have a spec yet (dependency, Open Questions).
Entry & Exit Points
| Entry Source | Trigger | Player carries this context |
|---|---|---|
| Gameplay (any state, incl. Downed / Dead) | Menu (Esc / Start) | the live world behind; role (host / client) picks the third button |
| Settings (child) | its Back / UI.Cancel / Menu | returns here, focus restored to the Settings button |
| Exit Destination | Trigger | Notes |
|---|---|---|
| Gameplay | Resume, Menu again, UI.Cancel (Esc / East) | instant; input released on the close edge (#3) |
| Settings | Settings button | pause stays beneath on the panel stack |
| Title screen (client) | Leave — single press | character despawns; CARRY persists host-side; rejoin = late join, back where you stood or at the home shore (netcode Rule 5) — not one-way in spirit: rejoining restores you |
| Title screen (host) | End session — two-step in place (second press within 5 s) | ends for everyone; StopHost awaits the save flush (ADR-0004), then all machines return to their title screens. Everything confirmed is durable — "a save-file fact, not a failure state" |
| Desktop | OS quit while paused | ADR-0004's wantsToQuit latch flushes; equivalent to End (host) / Leave (client) |
No writes belong to this screen itself — the leave / end actions are netcode + save operations owned by their systems. Disconnect errors route to the title screen, never through pause.
Layout Specification
Information Hierarchy
- The header fact — "Paused — the world keeps moving" (ruled 2026-08-31): the conventional word plus the honest correction, taught on the very first Esc. A fact, not a warning (tone rule).
- Resume — the safety press, first focus.
- Settings.
- Leave / End session with its one-line consequence beneath.
Absent by design: a session / player list (the HUD's party rows are visible right behind the panel — duplicating them here is redundancy and brushes the no-aggregation rule), save status (saving is continuous and durable — nothing to report), any timer or tide readout (the world says it), and a fourth button of any kind.
Layout Zones
One small centred column panel (#3): 320 × 264 px @100 % (480 × 396 at 150 %, 420 while armed — trivially fits 1080p). Height = 16 pad + 44 header (two Medium lines + 8 gap — the EN header wraps at 288 px) + 3 × 48 buttons + 2 × 12 gaps + 20 consequence line + 16 pad = 264; while the armed label wraps to two lines the third button grows to 64 px and the panel to 280 (ruled 2026-08-31). Width = 16 + 288 button column + 16.
- Z1 — Header (one line, Medium 16–18 px): "Paused — the world keeps moving".
- Z2 — Button column: Resume · Settings · Leave / End session, each 288 × 48 px, 12 px gaps.
- Z3 — Consequence line (Small 13–14 px, under the third button): client "your bag stays with the world — rejoin any time" · host "ends the session for everyone — everything is saved".
- Background: the live world + the full closed-list HUD — never dimmed (
hud.mdruling; #3 layering). The moving world is the screen's honesty; no scrim, no blur, no desaturation.
All px at 100 % UI scale (pattern-library scale convention).
Component Inventory
| Component | Pattern | Interactive | Notes |
|---|---|---|---|
| Header line | text | no | fixed string; the one teaching line |
| Resume | #1 Secondary | yes | first focus, top position — the screen has no Primary (ruled 2026-08-31, matching settings.md Z4: a dismissal is not a commit; hierarchy comes from first focus + position, #1 as amended) |
| Settings | #1 Secondary | yes | opens settings.md's panel on the stack; focus returns here on its Back |
| Leave (client) / End session (host) | #1 Secondary | yes | role-resolved at open. Host: two-step in place — the first press swaps the button's own label to "End for everyone — press again" (armed state; reverts after 5 s or on a deliberate focus move — never on a bare hover pass-over, see States); the second press ends. Borrows #9's principle — preview the consequence in place, never a dialog (#3: none exist at MVP) — with a different mechanism (two discrete presses, not press-and-release). Client: single press |
| Consequence line | Small text (#6 tone) | no | role-resolved. While armed it shows only the neutral half — "everything is saved" (ruled 2026-08-31: the armed label already says "for everyone"; showing it twice reads more alarming than the tone rule wants). Third named state: "saving — a moment" during Ending |
No new patterns — a first for the /ux-design queue; every behaviour above is #1 / #3 / #4 reuse plus #9's principle. The two-step in-place arm is a candidate pattern only if a second screen ever needs one (pattern-library Gaps table).
ASCII Wireframe
Host variant (left, third button armed after one press) and client variant (right). * = focused (3 px outline + inset tick).
live world keeps moving ─ tide, friends, enemies ─ HUD fully visible, never dimmed
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ Paused — the world keeps │ │ Paused — the world keeps │
│ moving │ │ moving │
│ ┌──────────────────────────┐ │ │ ┌──────────────────────────┐ │
│ │ Resume │ │ │ │ Resume ─▸* │ │
│ ├──────────────────────────┤ │ │ ├──────────────────────────┤ │
│ │ Settings │ │ │ │ Settings │ │
│ ├──────────────────────────┤ │ │ ├──────────────────────────┤ │
│ │ End for everyone — ─▸* │ │ │ │ Leave │ │
│ │ press again │ │ │ │ │ │
│ └──────────────────────────┘ │ │ └──────────────────────────┘ │
│ ends the session for every- │ │ your bag stays with the │
│ one — everything is saved │ │ world — rejoin any time │
└──────────────────────────────┘ └──────────────────────────────┘
The header is budgeted at two Medium lines and drawn so (EN wraps at 288 px). Checked at 150 %: 480 × 396 (420 while armed) leaves over 700 px of margin on each side of 1920 × 1080.
States & Variants
| State / Variant | Trigger | What Changes |
|---|---|---|
| Default — client | opened by a client | third button Leave, client consequence line |
| Default — host | opened by the host | third button End session, host consequence line |
| End armed (host) | first press on End session | the button's own label swaps to "End for everyone — press again"; reverts after 5 s or on a deliberate focus move — UI.Navigate / arrow keys / a click elsewhere (ruled 2026-08-31; not Tab — Tab is the Inventory binding, inert under capture, and the UI map has no Tab traversal: a bare mouse pass-over does not disarm — a documented local deviation from #4's hover == focus, so a desk-bumped mouse can never silently disarm a keyboard user's armed press); Resume / Settings / Esc all still work and disarm it; a second press while armed ends |
| Ending (host) | second press while armed | all three buttons inert (dashed, #1 disabled grammar); consequence line becomes "saving — a moment"; StopHost awaits FlushAllAsync (ADR-0004 §4 — an await, not a timeout: the ADR bounds only the wantsToQuit path at ≤ 2 × flushDeadlineMs; whether StopHost should carry the same bound is Open Question 5), then every machine goes to its title screen |
| Leaving (client) | press on Leave | buttons inert for the beat of the disconnect; then the title screen |
| Beneath Settings | Settings pressed | pause is not rendered (one panel at a time, #3 — the stack remembers it); restored with focus on the Settings button when Settings closes |
| Session ends while paused (client) | the host ended / connection lost | the panel closes with the session; the machine goes to the title screen — the tone-safe "session ended" notice is the title screen's to show (dependency, Open Questions) |
| Downed / Dead | combat state | nothing — ordinary pause (the standing precedent); Resume returns you to your Downed / Dead character |
| Reduced motion ON | the setting | the close fade becomes a cut (open is already instant); the armed revert is a label swap either way — nothing to remove |
| Loading / empty | — | none: the screen reads only the role flag; nothing fetches |
No tutorial, locked, or platform-variant states. The Ending wait is visible but not cancellable — the second press was the commitment (ruled 2026-08-31).
Interaction Map
Input scope (technical-preferences): Keyboard / Mouse primary, Gamepad partial. The panel captures the UI map (ADR-0007 §3); Menu and Inventory are the only surviving Player edges, and Inventory does nothing here (it only closes the bag it opened, #3).
| Component | Action | KB/M | Gamepad | Feedback | Outcome |
|---|---|---|---|---|---|
| any button | Focus | hover (hover == focus, #4) / ↑ ↓ | UI.Navigate (d-pad / left stick) | 3 px outline + inset tick | — |
| Resume | Press | Enter / click | Submit (South) | 100–150 ms close fade | back to gameplay; input released on the close edge |
| Settings | Press | Enter / click | Submit | the settings panel opens instantly on the stack | pause beneath, not rendered |
| Leave (client) | Press | Enter / click | Submit | buttons go inert | disconnect → title screen |
| End session (host) | Press | Enter / click | Submit | 1st press: instant label swap to armed; 2nd press while armed: Ending state | arm → end |
| panel | Close | Esc (UI.Cancel or Menu — both close here) | East or Start | same as Resume | back to gameplay; disarms the End button if armed |
Focus adjacency — first focus = Resume:
| From | ↑ | ↓ | ← / → |
|---|---|---|---|
| Resume | nothing | Settings | nothing |
| Settings | Resume | Leave / End session | nothing |
| Leave / End session | Settings | nothing | nothing |
Three stops, no traps; Esc / East closes from any state — there is no listening or prompt state on this screen, so the close is never ambiguous. Nothing is pointer-only; no hold exists.
Events Fired
No analytics system exists; nothing here crosses the wire except the leave / end operations themselves.
| Player Action | Event | Payload | Persistent? |
|---|---|---|---|
| open | PanelOpened(Pause) → panel manager SetUiCaptured(true) (ADR-0007 §3) | role (host / client) | no |
| Resume / Esc / Menu | PanelClosed(Pause) → SetUiCaptured(false) | — | no |
| Settings | PanelOpened(Settings) — stack push | opener = pause | no |
| End — first press | none (armed is local UI state — deliberate) | — | no |
| End — second press | EndSessionRequested → StopHost awaits FlushAllAsync (ADR-0004), NGO shutdown, every client's session ends | — | the flush is the save system's own write — this screen writes nothing |
| Leave (client) | LeaveSessionRequested → NGO client disconnect | — | no local write; CARRY persists host-side (netcode Rule 5) |
| focus moves | none (deliberate) | — | no |
Flagged for architecture: EndSessionRequested / LeaveSessionRequested are requests to the session system (ADR-0008's territory after the playtest); the UI owns only the armed flag and its 5 s timer — transient, no game meaning.
Transitions & Animations
Art bible §7.5 binds: 100–150 ms ease-out only; nothing animates that gates an input.
| Moment | Motion | Reduced motion ON |
|---|---|---|
| Enter | instant (≤ 1 frame, #3); the focus tick is on Resume the same frame | same |
| Exit to gameplay | 100–150 ms ease-out fade; input released on the close edge, not at animation end | cut |
| Armed label swap / revert | instant — it must read the frame it happens; no pulse, no colour flash | same |
| Ending / Leaving | buttons flip to dashed instantly; no spinner — the bounded wait is sub-second in the normal case and "saving — a moment" is the read | same |
| To the title screen | cut (the title screen owns its own entrance) | same |
No screen shake, no bounce, no open delay, no hold. The armed state's 5 s revert is a safety revert, not an input window — pressing again within it is optional, and letting it lapse costs nothing.
Data Requirements
| Data | Source System | Read / Write | Notes |
|---|---|---|---|
| role (host / client) | netcode session (NGO IsHost) | Read | once on open; picks the third button + consequence line |
| armed flag + 5 s revert timer | this panel, local | — | the one UI-owned datum: transient, no game meaning, never persisted |
EndSessionRequested | netcode session + save (StopHost → FlushAllAsync, ADR-0004) | Write (trigger) | the operation belongs to those systems; this screen only asks |
LeaveSessionRequested | netcode session (client shutdown) | Write (trigger) | same |
| labels + consequence lines | string table (L10n) | Read | — |
Nothing per-frame, nothing fetched, no null cases — role always exists in a session, and pause only exists inside one.
Accessibility
Tier: Standard (design/accessibility-requirements.md).
- Keyboard-only and gamepad-only: complete — three focus stops, ↑ / ↓ walk them, Enter / South activates, Esc / East / Start close; nothing pointer-only; no hold (the project's hold inventory stays at three).
- Focus: 3 px outline + inset tick (#4); first focus Resume; exactly one focused element.
- Colour never alone: armed = the words change ("End for everyone — press again"); inert = dashed (#1 disabled grammar); focus = weight + tick.
- Text: header + button labels Medium 16–18 px; consequence lines Small 13–14 px; ≥ 4.5:1 verified at theme lock.
- Hit targets: 288 × 48 px buttons.
- The armed state is lockout- and strand-proof both ways: the deliberate-move disarm (+ 5 s cap) means switch-access and slow-input players can never leave a live "End for everyone" button behind — and the hover exemption means a stray mouse move can never silently disarm it either (the double-press-does-nothing trap). The 5 s revert is a safety cap, not an input window — pressing again within it is optional, letting it lapse costs nothing.
- Cognitive: three choices, plain-verb labels, one two-state control on the whole screen; the header teaches the one unbendable rule as a fact, not a warning (tone rule).
- Downed / Dead: ordinary pause. Reduced motion: per Transitions. Screen reader: out of scope (a11y doc, Known Intentional Limitations).
Localization Considerations
| Element | Limit (EN source) | Layout constraint | Expansion risk |
|---|---|---|---|
| Header | ≤ 34 chars, 2 lines budgeted (EN = 31, wraps to two at 288 px — the height formula pays for both) | 288 px, Medium | HIGH |
| Button labels | ≤ 18 chars (EN max "End session" = 11) | 288 px, one line | MEDIUM |
| Armed label | ≤ 32 chars, 2 lines allowed (EN "End for everyone — press again" = 30) | the button grows 48 → 64 px on wrap; panel 264 → 280 (Layout Zones) | HIGH |
| Consequence lines | ≤ 56 chars, 2 lines allowed (EN 44 / 42) — a deliberate relaxation of #6's ≤ 32 (full-width 288 px slot; the settings-footer precedent) | 288 px, Small | MEDIUM |
| "saving — a moment" | ≤ 24 chars | consequence slot | LOW |
The header and armed label are flagged for 40 % expansion (the two-line allowances are the accommodation). No numbers, dates or currencies on this screen. No RTL commitment at MVP.
Acceptance Criteria
- [PLAYMODE] Opens within 1 frame of
Menufrom raw gameplay; in build mode the same press exits the mode first and pause opens with no live ghost behind it; with any panel open,Menucloses that panel and pause does not open. - [MANUAL] The world visibly continues behind the panel (tide, friends, enemies) and the full closed-list HUD stays visible, never dimmed; no scrim, blur or desaturation.
- [MANUAL] Host: the first press on End session arms it (label swap); a deliberate focus move (arrow /
Navigate/ a click elsewhere) or 5 s reverts it — a bare mouse pass-over does not; a second press while armed ends — every machine reaches its title screen, and reloading the host's save restores everything confirmed before the quit (netcode AC mirror). - [MANUAL] Client: Leave returns to the title; rejoining restores the bag bit-identically and places them where they stood if the land is still docked, or at the home shore if it departed. Leave while Downed with the bleed-out timer running → rejoin returns you Downed and revivable, timer carried over (netcode Rule 5's session-scoped state, exercised end-to-end from this screen).
- [MANUAL] Gamepad-only: three stops with visible focus; East and Start both close; South activates; no pointer-only element.
- [MANUAL] Pause opens and all three buttons work while Downed and while Dead (no invented gate).
- [MANUAL] During Ending / Leaving all buttons are dashed and inert; no input reaches gameplay. Killing the host process mid-flush loses at most the drains after
lastDurableDrain— the save is never partial or torn (ADR-0004's actual durability claim). - [MANUAL] Reduced motion ON: the close is a cut; nothing else on the screen moves (the armed swap is already instant).
- [MANUAL] Esc twice quickly from gameplay: opens then closes pause — never falls through to Settings or arms End (first focus is Resume, and Esc closes rather than activating focus).
- [MANUAL] 1080p and 1440p at 100 / 125 / 150 %: the panel stays centred (480 × 396 at 150 %; 420 while armed), no clipping, focus order unchanged, consequence lines on one line in EN.
- [MANUAL] Arm End on KB/M with Enter, sweep the mouse across Resume and Settings without clicking → still armed; press ↑ → disarmed; click Resume while armed → disarms and closes.
Open Questions
- Player journey map not yet created. Template available at
.claude/docs/templates/player-journey.md. Run/ux-designPhase 2b or create it manually to establish player context for this screen. - The title-screen spec does not exist — it owns the session-ended notice ("the session has ended — everything was saved"-class copy), the reset floor (hold default Esc / Start 3 s, ADR-0007 §4), and it is this screen's exit target (
/ux-design title-screen). - The armed revert duration (5 s) and any surfacing of the flush bound are data-driven values — owner ADR-0014 / the standing
ui.json→ ADR-0006 manifest question. EndSessionRequested/LeaveSessionRequestedshapes and teardown ordering → the netcode ADR (ADR-0008, after the friend playtest).- Should
StopHostcarry the same ≤ 2 ×flushDeadlineMsbound ADR-0004 gives thewantsToQuitpath? As written itsFlushAllAsyncawait is unbounded, so the Ending state could in principle hang on a stuck flush — the UI needs to know whether Ending can hang. Route to ADR-0004 (amendment) / ADR-0008. Until ruled, "saving — a moment" is the only read and nothing more is shown.