DRIFTLANDS co-op survival on drifting tides — studio progress

UX Spec: Pause

Status: Reviewed — APPROVED (/ux-review verification 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 (Menu Esc / Start, protected; exits build mode first, rule (g); under UI capture Menu closes 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; wantsToQuit latch); 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


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 / Menureturns here, focus restored to the Settings button
Exit Destination Trigger Notes
GameplayResume, Menu again, UI.Cancel (Esc / East)instant; input released on the close edge (#3)
SettingsSettings buttonpause stays beneath on the panel stack
Title screen (client)Leave — single presscharacter 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"
DesktopOS quit while pausedADR-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

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.

All px at 100 % UI scale (pattern-library scale convention).

Component Inventory

Component Pattern Interactive Notes
Header linetextnofixed string; the one teaching line
Resume#1 Secondaryyesfirst 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 Secondaryyesopens settings.md's panel on the stack; focus returns here on its Back
Leave (client) / End session (host)#1 Secondaryyesrole-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 lineSmall text (#6 tone)norole-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 — clientopened by a clientthird button Leave, client consequence line
Default — hostopened by the hostthird button End session, host consequence line
End armed (host)first press on End sessionthe 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 armedall 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 Leavebuttons inert for the beat of the disconnect; then the title screen
Beneath SettingsSettings pressedpause 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 lostthe 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 / Deadcombat statenothing — ordinary pause (the standing precedent); Resume returns you to your Downed / Dead character
Reduced motion ONthe settingthe 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 buttonFocushover (hover == focus, #4) / ↑ ↓UI.Navigate (d-pad / left stick)3 px outline + inset tick—
ResumePressEnter / clickSubmit (South)100–150 ms close fadeback to gameplay; input released on the close edge
SettingsPressEnter / clickSubmitthe settings panel opens instantly on the stackpause beneath, not rendered
Leave (client)PressEnter / clickSubmitbuttons go inertdisconnect → title screen
End session (host)PressEnter / clickSubmit1st press: instant label swap to armed; 2nd press while armed: Ending statearm → end
panelCloseEsc (UI.Cancel or Menu — both close here)East or Startsame as Resumeback to gameplay; disarms the End button if armed

Focus adjacency — first focus = Resume:

From ↑ ↓ ← / →
ResumenothingSettingsnothing
SettingsResumeLeave / End sessionnothing
Leave / End sessionSettingsnothingnothing

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?
openPanelOpened(Pause) → panel manager SetUiCaptured(true) (ADR-0007 §3)role (host / client)no
Resume / Esc / MenuPanelClosed(Pause) → SetUiCaptured(false)—no
SettingsPanelOpened(Settings) — stack pushopener = pauseno
End — first pressnone (armed is local UI state — deliberate)—no
End — second pressEndSessionRequested → 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 movesnone (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
Enterinstant (≤ 1 frame, #3); the focus tick is on Resume the same framesame
Exit to gameplay100–150 ms ease-out fade; input released on the close edge, not at animation endcut
Armed label swap / revertinstant — it must read the frame it happens; no pulse, no colour flashsame
Ending / Leavingbuttons flip to dashed instantly; no spinner — the bounded wait is sub-second in the normal case and "saving — a moment" is the readsame
To the title screencut (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)Readonce on open; picks the third button + consequence line
armed flag + 5 s revert timerthis panel, local—the one UI-owned datum: transient, no game meaning, never persisted
EndSessionRequestednetcode session + save (StopHost → FlushAllAsync, ADR-0004)Write (trigger)the operation belongs to those systems; this screen only asks
LeaveSessionRequestednetcode session (client shutdown)Write (trigger)same
labels + consequence linesstring 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).


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, MediumHIGH
Button labels≤ 18 chars (EN max "End session" = 11)288 px, one lineMEDIUM
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, SmallMEDIUM
"saving — a moment"≤ 24 charsconsequence slotLOW

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


Open Questions