Interaction Pattern Library: Driftlands
Status: Draft —
/ux-review2026-08-29 findings all applied 2026-08-30; formally pending its own re-review, though every entry has since been cross-checked by the six screen-spec reviews of 2026-08-31 (see Version)
Author: Jeroen + ux-designer
Last Updated: 2026-08-31
Version: 0.19 (2026-09-01: Gaps — character stepper, the declared #27 deviation from the title-screen re-review) · 0.18 (2026-09-01: Gaps — text input, screen-level notice slot, invisible timed hold; from the title-screen review) · 0.17 (2026-08-31: #4 — armed-state exemption from hover==focus, after the pause re-review) · 0.16 (2026-08-31: #1 no-Primary-for-dismissal note; Gaps + two-step-arm candidate row — after the pause /ux-review) · 0.15 (2026-08-31: #20 glyph source precision; settings.md APPROVED) · 0.14 (2026-08-31: #20 — Reset Secondary, primary-only rows + fixed secondaries, glyph-templated refusal, prompt modality; after the settings re-review) · 0.13 (2026-08-31: #20 — protected collision refused pre-prompt, pair-as-rebound, capture-only exclusion; after the settings /ux-review) · 0.12 (2026-08-31: #25 Slider, #26 Toggle, #27 Option Cycler added fromdesign/ux/settings.md; #20 amended — conflict = exchange, Interact / Revive one row; Gaps: settings controls closed)
Engine: Unity 6.3 LTS (6000.3)
UI Framework: Unity UI Toolkit (screens; architecture.md ruling) · world-space flyouts/markers as sprites · project-wideDriftlands.inputactions(ADR-0007)
Template: Interaction Pattern Library
Related Documents:
-design/art/art-bible.md§7 — UI/HUD visual direction (diegetic rule, closed HUD list, typography, iconography, animation & input commitments)
-design/quick-specs/ui-hud-2026-08-27.md— the HUD ledger, the two UI-layer bans, the/ux-designqueue
-design/accessibility-requirements.md— accessibility commitments (Standard tier, committed 2026-08-29)
-docs/architecture/adr-0007-input-action-map.md— action list, shared controls,InputFrame, rebinding
-design/ux/*.md— per-screen specs that reference these patterns:inventory.md,hud.md,crafting.md,build-mode.md,settings.md(all APPROVED)
Why this document exists: every screen spec should be able to say "uses World-Fact Reason String" rather than re-specifying precedence, hysteresis, tone, and focus behaviour from scratch. When a screen spec references a pattern name, the programmer looks it up here; when the behaviour changes, it changes here and applies everywhere. Do not design a new interaction without checking here first.
Status definitions: Draft — specified, not yet implemented; Stable — implemented and validated in at least one shipped screen; Deprecated — being phased out.
Overview
Driftlands' UI is governed by three documents this library only applies: art bible §7 (the diegetic-first decision rule, the closed screen-space HUD list, typography/iconography/animation commitments), the ui-hud quick-spec (the per-system ledger and the two UI-layer bans — the tone rule: every string states a world fact, never a scolding or an urgency prompt; and the no-contribution-comparison rule: no per-player counts, logs, sorts or groupings anywhere), and ADR-0007 (one action map; Strike/Confirm share LMB and Demolish/Cancel share RMB by design; Revive is the only hold in the game; every panel is fully operable on gamepad with a visible focus state).
Patterns fall into three groups. Standard controls are the ordinary widgets (button, list, panel, tooltip, rebind row) with Driftlands' focus/animation rules baked in. Game-specific patterns are the ones the GDDs contracted: world-fact reason strings, honest no-op cues, the ghost outline, hold-channel, and the co-op affordances. Feedback standards pin the authority split every effect must respect — predicted (instant, local) vs host-confirmed (arrives ~1 RTT later, never faked) — because a pattern that shows a confirmed outcome before the host confirms it is a correctness bug, not a polish choice.
Every pattern states its when not to use; the commonest misuse in this project is reaching for a dialog, toast, or timer where the GDDs demand a diegetic cue or a silent no-op.
Scale convention: every pixel value in this library (hit targets, row heights, text sizes, flyout rise, shake amplitude) is stated at 100 % UI scale on 1080p and multiplies with the accessibility UI-scale option (100 / 125 / 150 %); nothing may be hardcoded in pixels.
Input context (technical-preferences / ADR-0007): KB/M primary (WASD + mouse-aim), gamepad secondary (twin-stick), no touch, PC 16:9 baseline. Every pattern below is specified for both schemes; where the schemes differ the difference is stated, never implied.
Pattern Catalog
Deliberately not in the catalog: enemy HP bars (banned at MVP), node health bars (banned), minimap / node overlay (banned), any build log / craft counter / contribution view (banned), a numeric tide timer (impossible by API — tide AC-27).
| # | Pattern | Category | One-liner | Contracted by | Status | |
|---|---|---|---|---|---|---|
| 1 | Button (Primary / Secondary) | Standard | Confirm/cancel affordance; 100–150 ms ease-out; focus outline slate-light | art bible §7.5 | Draft | |
| 2 | List Picker | Standard | Vertical list (never a card grid at MVP); d-pad/stick order; greyed rows carry a reason string | BB picker, crafting recipe list | Draft | |
| 3 | Panel / Modal | Standard | Opens instantly, captures UI input; the bag closes by Inventory, every panel closes by Menu / UI.Cancel; no menu-open delay | §7.5, ADR-0007 §3 | Draft | |
| 4 | Focus & Navigation Order | Standard | Explicit per-panel grid order, visible focus state, never hover-only | §7.5, technical-preferences | Draft | |
| 5 | Tooltip / Inspect | Standard | Hover on KB/M, focus-dwell on gamepad; hosts the maker's mark | crafting AC-13 | Draft | |
| 6 | World-Fact Reason String | Game | Per-cause short string; first-failing-gate precedence; held stable; minimum display / snap hysteresis | crafting Rule 3, BB UI Requirements | Draft | |
| 7 | Honest No-Op Cue | Game | A rejected action shows nothing extra except a non-modal, distinguishable cue ("bag full" ≠ "someone got there first"); never a dialog or toast | harvesting OQ6, inventory Rule 7 | Draft | |
| 8 | Ghost Outline & Reach Grid | Game | Solid ink = valid, dashed = invalid, zero fill; grid only within placementReach, only in build mode | BB Visual Requirements | Draft | |
| 9 | Demolish Target Select | Game | Cursor-raycast (KB/M) / nearest-in-reticle-cone (gamepad); highlight + refund-preview prompt | BB UI Requirements (delegated) | Draft | |
| 10 | Hold Channel | Game | The only hold: Revive HeldFor with a filling read; any cancel cause breaks it, no drain-back | combat Rule 9, ADR-0007 rule (d) | Draft | |
| 11 | Craft-Beat Progress | Game | Filling motion at the station, predicted; stance break = cancel, fill vanishes, no confirmation | crafting UI Requirements | Draft | |
| 12 | Maker's-Mark Inspect | Game | Crafter name + "forged N tides ago" + tier, readable per item in tooltip / rack / examine; no aggregation affordance | crafting Rule 5, CD C2 | Draft | |
| 13 | Storage Who-Took-What | Game | Host-confirmed live take feedback on the storage panel; the anti-double-hauling mechanism | §7.2, inventory Rule 7 | Draft | |
| 14 | Edge Indicator (Ally / Downed) | Game | Hat-silhouette edge markers; the Downed variant is the same system | §7.2, combat UI Requirements | Draft | |
| 15 | World Ping | Game | Placeable world-space marker, signal-orange, never Ember Crimson | §7.2 | Draft | |
| 16 | Compass Tide-Glint | Game | Per-socket direction + temperature glint; no timer, no countdown, ever | tide UI Requirements, §7.1 | Draft | |
| 17 | Carry Meter | Game | Slate fill + icon shake near full; never crimson; glanceable without a menu | §7.1, harvesting UI Requirements | Draft | |
| 18 | Confirmed Number Flyout | Feedback | Damage numbers / yield flyouts — world-space, host-confirmed only, the only numeric feedback | combat + harvesting UI Requirements | Draft | |
| 19 | Predicted vs Confirmed Feedback Split | Feedback | The authority table every pattern inherits: what may fire instantly, what waits for the host | combat Rule 8; harvesting, BB, crafting authority tables | Draft | |
| 20 | Rebind Row | Standard | Per-action rebind with swap-on-confirm (exchange) conflict, protected actions, reset floor; Interact / Revive one row | ADR-0007 §4 (amended 2026-08-31); design/ux/settings.md | Draft | |
| 21 | Stack Grid | Game | Fungible-slot inventory grid; one press = one whole-stack move; no drag / split at MVP | inventory quick-spec Rules 2–7; design/ux/inventory.md | Draft | |
| 24 | Interact Prompt | Game | One world-space glyph + verb + noun above the nearest interactable in range; never for enemies / nodes | design/ux/hud.md E11 | Draft | |
| 25 | Slider | Standard | Stepped track + knob + always-visible numeric readout; ←/→ one step; live-apply; the wheel never moves it | a11y doc (volumes, gamma, HUD opacity); design/ux/settings.md | Draft | |
| 26 | Toggle | Standard | Two-cell `[ OFF \ | ON ]` switch; the active cell is filled and worded; Submit / ←/→ flips; live-apply | a11y doc (reduced motion, flash, mono, toggle-hold); design/ux/settings.md | Draft |
| 27 | Option Cycler | Standard | ◂ value ▸ over a short fixed list; no wrap (end arrow dashed); Submit = forward; never a dropdown | a11y doc (UI scale); design/ux/settings.md | Draft | |
| 29 | Repair Action | Game | Pinned per-item rows at a station; one press, instant, free, host-confirmed; panel stays open | crafting Rule 9; design/ux/crafting.md | Draft | |
| 31 | Repeat Placement | Game | Confirm held → one atomic commit per cell along the last ghost-move direction; ≤ 1 in flight; stops on a refused cell | BB Rule 11b; design/ux/build-mode.md | Draft | |
| 30 | Examine Card | Game | Read-only world-space card of another player's equipped gear + maker's marks, opened by Interact | crafting AC-13; design/ux/crafting.md | Draft |
Patterns
Standard Control Patterns
1. Button (Primary / Secondary)
Category: Standard · Status: Draft Used In: every panel; BB build picker (Confirm), crafting station (Craft), settings, pause
Description: The one button style. Primary = the panel's single default action (Craft, Place, Apply); Secondary = everything else. Hierarchy comes from weight and outline, never from a second colour.
Specification:
- Visual: slate panel
#3A3D42, text#E8E6DE, ink-outline at the world's outline weight scaled down (art bible §7.4). States: idle / focused (slate-light outline highlight) / pressed (1 px settle, no bounce) / disabled (dashed outline — the ghost's invalid grammar reused — plus a World-Fact Reason String (#6) on focus, never a plain grey; a pointer-only button that mirrors a focused list row is dashed only, and the row / footer carries the reason —crafting.md). - Input: KB/M — click;
UI.Submit(Enter) on focus. Gamepad —UI.Submit(South) on focus. A Primary action never requires a hold. - Feedback: 100–150 ms ease-out on state swap; one understated click sound; no confirm-hold timers, no open delay (§7.5 "never animates").
- Accessibility: ≥ 44 px hit target at 1080p; label ≥ Medium size (16–18 px); focus visible without hover; state never conveyed by colour alone.
When to Use: any discrete confirmable action inside a panel. When NOT to Use: for in-world verbs (strike, dash, interact, demolish) — those are Player-map actions, never on-screen buttons; for "destructive" confirmations (none exist at MVP — demolish refunds in full, so there is nothing to confirm); as the weight for a dismissal — a panel whose default action is leaving has no Primary (settings.md Z4; pause.md Resume — hierarchy there comes from first focus + top position, added 2026-08-31).
2. List Picker
Category: Standard · Status: Draft Used In: BB build-mode picker (3 entries at MVP), crafting recipe list, settings lists
Description: A vertical list of rows, one focused, one selected. Never a card grid at MVP (BB UI Requirements: "a list, not a grid of cards"). Vertical by default; the build picker strip is the one horizontal instance (build-mode.md S1, in-mode context (a)).
Specification:
- Row anatomy: icon (ink-outline, tier-shaped per §7.4) · name · cost / affordability read · trailing reason-string slot (empty when the row is available).
- Greyed rows are still focusable and show their World-Fact Reason String (#6) — a row is never silently disabled. BB picker affordability is ghost-centred (CARRY ∪ STORAGE-in-range-of-ghost, the F1 predicate) once build mode is active; recipe affordability follows crafting Rule 3's three causes.
- Input — two contexts, one widget: (a) in build mode (Build map live, UI map disabled — the picker is not a captured panel): KB/M
PickPrev/PickNext(Q / scroll wheel), click selects; gamepad d-pad Left/Right,Confirm(South) selects. (b) in a captured panel (UI map live): KB/M arrow keys + click / Enter; gamepadUI.Navigateup/down,UI.Submitselects. Wraps at both ends in both contexts. - Feedback: selection swap 100–150 ms ease-out; no per-move tick sound (BB audio rule — noise fatigue).
- Accessibility: exactly one focused row at all times; focus and selection are distinct visual states; row height ≥ 32 px; MVP lists fit one screen (3 structures, cloth recipes) — scrolling is specified but unexercised until Tier 1.
Allowed variant — commit-on-Submit (added 2026-08-31, design/ux/crafting.md): a list whose focus already drives a detail pane may treat Submit as the row's commit rather than a selection, provided the panel has no selected-but-uncommitted state and any duplicate button is pointer-only (never focusable). State the variant in the screen spec.
When to Use: choosing one of N known items. When NOT to Use: with any sort / group / filter control keyed on a player identity (aggregation ban — pickers have no sort control at all at MVP); for inventories (bag/storage use a stack grid — see Gaps).
3. Panel / Modal
Category: Standard · Status: Draft Used In: bag, storage, recipe list, rack / examine, pause / settings
Description: A screen-space panel that captures UI input while open. Driftlands has no nested dialogs and no confirmation dialogs at MVP — one panel at a time.
Specification:
- Open: instant (≤ 1 frame) — the opening frame delivers the action edge; capture applies from the next frame (ADR-0007 §3). Close: 100–150 ms ease-out fade; input is released on the close edge, not at animation end.
- Capture semantics (binding, ADR-0007 §3): while open, every Player/Build edge is false except
MenuandInventory.Menucloses the open panel via the panel stack rather than opening pause;Inventorycloses the bag it opened. Only the bag has an opening-key close: every other panel (station, storage, rack) is opened byInteract, which is zeroed under capture, so those close viaMenu/UI.Cancelonly.Moveis still reported; locomotion stops via Movement'sUiCapturedrule. Build mode does not survive a panel opening. - Layering: at most one panel plus tooltips. The Downed edge indicator, party HP and the tide-glint stay visible under any panel — the closed HUD list is never hidden by UI.
- Feedback: brief, non-musical open/close cue.
- Accessibility: focus lands on the panel's declared first element on open and returns to gameplay context on close;
Menu/Esc always reachable (protected action).
When to Use: bag, storage, recipes, rack, settings. When NOT to Use: for errors or rejected actions (Honest No-Op Cue #7 — never a modal); for anything that gates an input mid-combat; for "are you sure?" (none exist — demolish shows a refund-preview prompt, not a dialog).
4. Focus & Navigation Order
Category: Standard · Status: Draft Used In: every panel (the closed HUD elements are non-interactive and have no order)
Description: The gamepad-parity contract: every panel declares an explicit navigation order and a visible focus state; nothing is hover-only (technical-preferences; art bible §7.5).
Specification:
- Each screen spec that captures UI input MUST include a numbered focus order (row-major for stack grids, top-to-bottom for lists) and a named "first focus" element. A non-capturing world-mode overlay (build mode — the UI map is disabled, Player + Build maps drive it) instead declares which actions drive its selection and asserts exactly one selected element at all times (
build-mode.md, added 2026-08-31). - Focus visual: slate-light outline highlight plus a weight change (2 → 3 px) and an inset focus tick at the element's leading edge — the tick/weight is the non-colour channel (accessibility colour audit); persists while the panel is open; never conveyed by hover, colour, or animation alone.
- Input:
UI.Navigate(d-pad / left stick; key-repeat after 300 ms at 100 ms intervals),UI.Submit,UI.Cancel; keyboard arrows + Enter / Esc mirror it; mouse hover moves focus — hover == focus on KB/M, so both schemes share one state. On KB/M the panel's declared first-focus element holds only until the first pointer move; a pointer move takes focus and tooltips (#5) then follow the pointer. **Hover == focus is a focus rule, not a state-discard rule (added 2026-08-31): a control holding a transient armed state** (pause.mdEnd session — the only instance; Gaps candidate #32) is disarmed only by a deliberate move (Navigate/ arrow keys / a click elsewhere) or its own timeout — a bare pointer pass-over moves focus but never destroys armed state. - Device switch mid-panel: focus stays put; prompt glyphs follow
InputFrame.Scheme(presentation-only). - Accessibility: this pattern is the keyboard / gamepad path. A screen spec without a declared order fails
/ux-review.
When to Use: always. When NOT to Use: no exception exists.
5. Tooltip / Inspect
Category: Standard · Status: Draft Used In: bag, storage rack, equipped-gear examine, recipe rows
Description: The per-item detail surface and the only place the maker's mark is read (#12 defines the content; this pattern defines the container).
Specification:
- Trigger: KB/M — hover 150 ms; gamepad — focus dwell 150 ms on the focused stack / row, or
UI.Submiton a focused rack item for the pinned examine variant. Never longer (nothing gates an input). - Placement: anchored to the item, flipped to stay on-screen, never covering the focused row; max width sized for Small text (13–14 px) with 40 % expansion headroom (localisation).
- Content order: item name · tier · maker's-mark line (#12) · wear state (binary: fresh / worn) · durability read if equipped. No numbers the GDDs do not expose.
- Dismiss: on focus move / hover leave; instant.
- Accessibility: text, never icon-only; contrast ≥ 4.5:1; reachable by focus, never hover-only.
When to Use: any per-item read. When NOT to Use: for aggregate views (none exist); for anything that must be read during combat — tooltips are panel-scoped.
Game-Specific Patterns
6. World-Fact Reason String
Category: Game · Status: Draft Used In: BB ghost preview, BB picker rows, crafting recipe rows, storage transfer no-ops, revive race loss
Description: A short string stating which world fact blocks an action — never a generic "invalid", never a scolding, never a grind prompt ("locked", "come back later"). Crafting has three causes (station / world / affordability); Base Building has eight (its F1 gates).
Specification:
- Content is owned by each GDD; this pattern owns the contract. Tone references (binding): "too far away", "someone is standing there", "no way out", "not enough timber in reach", "no land has offered copper yet".
- Precedence: when several causes fail, show the first failing gate in the owner's stated order (BB: reach → bounds → overlap → occupancy → exit → terrain → socket → materials; crafting: station → world → affordability).
- Stability + hysteresis (the
/ux-designdeliverable BB named): a displayed string persists for a minimum 250 ms; a change between gates requires the new gate to be the first failure for 2 consecutive frames before it replaces the old one. Hysteresis governs the string only — never the visual it explains (the ghost outline flips solid ↔ dashed truthfully every frame;build-mode.md). Bounds flicker at cell boundaries without ever showing a stale cause for longer than a quarter second. Provisional — playtest-tuned. - Placement: ghost → label anchored above the ghost; rows → trailing slot in the row; storage → beside the stack (see #7); revive → beside the ring (#10).
- Style: Small text 13–14 px,
#E8E6DEon slate; no icon, no colour coding (the ghost outline already carries the binary). - Accessibility: text only, ≥ 4.5:1; length budget ≤ 32 characters English — 40 % expansion headroom, flagged HIGH for localisation.
When to Use: any action the player could attempt but the world currently refuses. When NOT to Use: for successful actions; for anything the player could not have attempted (hidden state stays hidden — never "enemy has 3 HP left").
7. Honest No-Op Cue
Category: Game · Status: Draft Used In: harvest swing on a full bag; harvest swing on a node someone else just emptied; storage withdraw race loss; craft-commit race loss; placement race loss (BB EC11)
Description: When the host rejects an action, the world shows the truth and nothing else: the predicted effect simply fails to be confirmed, plus one non-modal cue that says which kind of nothing happened. Never a dialog, toast, error sound, or red flash. Harvesting OQ6's bar: "bag full" and "someone got there first" must be distinguishable by a first-time player with no explanation — the pre-playtest gate.
Specification:
- Two cue families on deliberately different channels: (a) it's you (bag full) → a bag pulse on the Carry Meter (#17): one 150 ms scale pulse + icon shake, HUD-side; (b) it's the world (node already empty, stack already taken, cell already occupied) → a world-side read: the node shows its husk / the stack is simply gone / the ghost is dashed with "someone got there first" (#6) — nothing on the HUD moves.
- Timing: the cue fires when the host verdict arrives (bag-full fires immediately — the client can know that). The predicted swing animation completes as a whiff; the yield burst never appears.
- Audio: the ordinary whiff sound only; no dedicated "denied" sound.
- Copy (where a string exists): world facts — "the land left with it", "bag full", "Alex got there first". Names are fine; blame is not.
- Accessibility: both families carry a shape/motion component, never colour alone; the bag pulse falls back to a static "full" outline on the meter under reduced-motion.
When to Use: every rejected host-rail action. When NOT to Use: for actions refused before being attempted (that is #6's job); never combined with a modal or a retry prompt.
8. Ghost Outline & Reach Grid
Category: Game · Status: Draft Used In: BB build mode only
Description: The placement preview: outline-only ghost (solid ink = valid, broken/dashed = invalid, zero fill in either state) plus a grid overlay drawn only within placementReach of the player, only while build mode is active.
Specification:
- Ghost follows
PointerWorldon KB/M and, on gamepad,InputFrame.Aim(the right stick — the same deadzoned vector camera and combat read;AimIsPointerdecides which, neverScheme— ADR-0007) quantised to a cell step;Aimkeeps updating while the stick drives the ghost, so camera aim-bias leans toward the placement (intended,build-mode.md); snaps via the pureghostSnapResult(1 cell = 1 u, ADR-0003); outline at the locked 2 px weight; dash ratio pinned by the art-bible fix-pass (minimum segment:gap ≥ 2:1, provisional). - Grid: cell lines inside
placementReach(6 u), fading to nothing at the edge; never full-canvas; gone the frame build mode exits. - Gamepad stick-to-ghost transfer (BB OQ10 deliverable): cell-step, not cursor-velocity — a right-stick deflection past
aimStickDeadzonemoves the ghost one cell along its dominant axis with key-repeat (300 ms initial, 100 ms interval); no acceleration; the left stick keeps moving the player. Snap: 1 u grid + 0.35 u nudge to an adjacent footprint edge (OQ6 residual, ruled 2026-08-31). AC-8's parity claim covers snap outcome; effort differs by design. - Reason string (#6) anchored above the ghost while invalid; live affordability in the picker (#2) is ghost-centred.
- Confirm / Cancel: LMB confirms and RMB or Esc cancels (
Build.Cancel) on KB/M (LMB / RMB shared with Strike / Demolish — routed inside build mode, ADR-0007 rule (a)); South / East on gamepad.Menu(Start) is a different path: it exits build mode first, then opens pause (ADR-0007 rule (g)). Any other Player-map action exits build mode first, then applies. - Feedback: predicted — ghost state, grid, brief non-musical enter/exit cue; host-confirmed — placement thunk + squash/settle + structure appears. No ghost-move tick.
- Accessibility: validity is shape-coded (solid vs dashed), never hue-coded; nothing animates, so reduced-motion is moot.
When to Use: structure placement. When NOT to Use: for demolish (#9 — a targeted action, not a mode); outside build mode; never with a fill tint.
9. Demolish Target Select
Category: Game · Status: Draft Used In: BB demolish — an instant targeted action from Idle/Move, never a mode
Description: How the player picks which structure Demolish acts on, with gamepad parity and a deterministic rule for dense clusters. Delegated to /ux-design by the BB GDD; this entry is the ruling.
Specification:
- Candidate set: structures within interaction range (BB provides).
- Selection: KB/M — cursor raycast (the structure under
PointerWorld); gamepad — nearest-in-reticle-cone: the candidate closest to the player within a 60° cone around the aim vector; exact ties → lowestinstanceId(deterministic). No candidate →Demolishis a silent no-op with no cue (nothing was targeted). Cone width provisional. - Prompt: while
Demolishis held down the targeted structure gets a solid highlight outline and a small refund preview (icon + count per material at the currentStructureDefcost — BB AC-28b) above it; release = commit (an ordinary click resolves on release — not a hold, no duration), no confirm dialog; releasing with no valid target = no-op (amended 2026-08-31,build-mode.md). A blocked storage demolish shows "empty the chest first" (#6) instead of the preview. - Feedback: host-confirmed — distinct, understated demolish sound + removal; refund lands in the demolisher's CARRY (bag flash via #17).
- Accessibility: outline-based highlight; text + icon preview; no hover-only path (the gamepad cone covers it).
When to Use: demolish only. When NOT to Use: for any other interact target (Interact uses proximity, not this selector); never as a persistent "demolish mode".
10. Hold Channel
Category: Game · Status: Draft Used In: Revive — the only in-world channel hold (ADR-0007 rule (d): exactly one asset-level hold). The other Held reads are the title-screen reset floor (#20) and Repeat Placement (#31); all three are inventoried in accessibility-requirements.md. A further hold is a design change, not a reuse
Description: A held input that fills a read over reviveChannelTime (3 s) and completes host-side. Any cancel cause breaks it instantly with no drain-back and no confirmation.
Specification:
- Trigger:
Reviveheld (E / West) while a Downed ally is withinreviveReach(1.5 u — pending combat-GDD assent). The owner sendsReviveStarton press andReviveCancelon release; the host times the channel (ADR-0015) — the local fill predicts the host's timer and resyncs from replicated state. - Read: a world-space ring fill around the Downed ally (not a HUD bar), filling over 3 s; prompt "hold to lift [Name]" appears when in reach.
- Cancel causes (combat EC5/EC6 + the proposed 4th): reviver takes damage, reviver dashes, release, reviver leaves reach — the ring vanishes instantly (no drain-back), the prompt returns. Race (combat EC7 "first channel-start wins by host timestamp" — implemented as first
arrivalSeqon the one ordered channel, ADR-0015): the first wins; the loser's ring vanishes with "someone is already lifting [Name]" (#6). - Completion: host-confirmed — the ally stands at
reviveHPFraction; the ring completes, then pops (one settle beat). - Accessibility: the hold is the sole exception to "no hold timers"; a toggle-hold option (press to start, press again to cancel) is the reduced-effort alternative and must be committed in
design/accessibility-requirements.md(see Gaps).
When to Use: revive. When NOT to Use: harvesting (Rule 1: press-only, no hold-to-channel), crafting (a stance, not a hold — #11), any menu confirmation (banned).
11. Craft-Beat Progress
Category: Game · Status: Draft Used In: every station craft (craftTimeSeconds 2 s at MVP)
Description: The station work-loop read during the craft beat: a filling motion on the station, client-predicted, cancelled by the stance breaking.
Specification:
- Trigger: Craft — the Primary button (
UI.Submit) in the recipe panel only (Interactopens the station; it cannot also commit, because panel capture zeroes every Player edge exceptMenu/Inventory— ADR-0007 §3) → the panel closes on commit, the player enters the station stance, and a fill sweeps across the station's silhouette (world-space, on the station — never a HUD bar) overcraftTimeSeconds. - Cancel: any movement input or dash breaks the stance; the fill vanishes the same frame — no drain-back, no confirmation, no reason string (the true-no-op rule). Re-approaching restarts from zero.
- Commit: host-confirmed — one understated completion beat (thunk; the item appears in CARRY with a bag flash via #17). Era-first crafts get animation/VFX weight, never added duration; never a fanfare.
- Race loss (crafting EC1/EC2 — someone consumed the last input first): the fill completes, no thunk, the stance ends; the world-side read is the recipe row's affordability string on next open (#6). No modal.
- Accessibility: fill is shape/motion; reduced-motion shows a three-step discrete fill; per-material loop sounds desirable, not required.
When to Use: station crafting. When NOT to Use: anything requiring a hold (#10); equip (instant swap-in-place, no beat); demolish / placement (their own thunks).
12. Maker's-Mark Inspect
Category: Game · Status: Draft Used In: Tooltip / Inspect (#5) in bag, storage rack, equipped-gear examine
Description: The per-item provenance line — the headline fantasy's UI floor (CD A3). Read per item only; no aggregation affordance may exist anywhere.
Specification:
- Content (crafting AC-13, all three contexts): crafter name · "forged N tides ago" · tier, e.g. "Mara — forged 4 tides ago — cloth".
Nis derived fromcycleIndexat inspect time (the mark stores the forged cycle, never a timestamp). Wear state on its own line (fresh / worn). Unmarked items show no line. - Tier 1: a per-crafter visual tell at close zoom (sigil / stitch accent derived stably from the crafter name) — scope reserved, not MVP.
- Aggregation ban (schema-negative at the UI layer, CD C2): no rack, bag, or list may sort, group, filter, count, or search by
crafterName; no "made by" tab; no tally./ux-reviewfails any spec exposing such an affordance. Multiple items by one crafter are simply multiple lines read one at a time. - Style: Small text; the name at Medium weight; no icon; no colour.
- Accessibility: text; ≥ 4.5:1; localisation — "forged N tides ago" is a pluralised template (HIGH: expansion + plural rules).
When to Use: any gear item read. When NOT to Use: materials (no mark); structures (BB Rule 7 — collectively anonymous, no builder field exists); any aggregate view.
13. Storage Who-Took-What
Category: Game · Status: Draft Used In: the shared-storage panel (Panel #3 for a SharedStorage chest)
Description: The anti-double-hauling mechanism: the storage panel renders host-confirmed state only and shows, live, who just took what — Inventory Rule 7 made visible.
Specification:
- Panel: stack grid of the chest's contents (host-confirmed copy — never predicted) with the player's own bag alongside; transfer =
UI.Submit/ click on a stack (batched pertransferBatchWindow, 0 = per click). - Live feed: when another player's withdraw / deposit is confirmed, the affected stack shows a 2 s inline tag "− 12 · Alex" / "+ 6 · Jo" (name + delta, world-fact tone) and the count updates; a stack taken to zero shows the tag over the empty slot before it collapses. Tags queue per stack; never a global log.
- Withdraws are not predicted (ruled 2026-08-30): a storage → bag transfer shows in your bag only when the host confirms it (~1 RTT); predicting it would show an item you may not get — exactly the "predicted effect implying a confirmed outcome" #19 bans. Deposits (bag → storage) may be predicted on the bag side (owner rail) — the storage side is host-confirmed. Race loss (Rule 7): your click on a stack someone emptied first → the stack is gone, the tag shows who, nothing appears in your bag (#7 world-family cue). No modal, no "try again".
- Not a ledger: tags expire; nothing is stored, scrolled, or aggregated (no-contribution-comparison rule). Your own deposits show no tag.
- Accessibility: tags are text + delta sign, never colour-only; focus order row-major across the chest grid then the bag grid (#4).
When to Use: shared storage only. When NOT to Use: the personal bag (owner-predicted, no tags); any history / log view (banned).
14. Edge Indicator (Ally / Downed)
Category: Game · Status: Draft Used In: HUD (closed list: off-screen party status); combat's Downed indicator is the same system
Description: Hat-silhouette markers pinned to the screen edge toward each off-screen party member; the Downed variant reuses the system with a low prone silhouette so a downed friend is findable at a glance (Fellowship floor).
Specification:
- One marker per off-screen ally (≤ 4), clamped to the screen edge along the player→ally line; slides with 100–150 ms ease-out on clamp changes; gone while the ally is on-screen.
- Glyph: that player's hat silhouette (art bible §5.1) in ink-outline. Downed = prone silhouette variant + a slow 1 Hz outline pulse. Dead / respawning = marker absent (they are at home). No names, distances, or HP on the marker — party HP lives in the HUD row.
- Downed banner: when an ally goes Downed, one Medium-size line "[Name] is down" shows for 3 s at the top edge — a world fact, never a failure message; the pulse persists until revived or bled out.
- Under panels: always visible (Panel #3 layering rule).
- Accessibility: silhouette + pulse, never colour; reduced-motion → static prone glyph with a thicker outline and edge slides become snaps; the tide-glint (#16) uses a different edge band so the two never overlap (band geometry:
hud.mdLayout Zones Z5 / Z6 — glyphs 24–56 px, glint 0–24 px from the 16:9 frame edge).
When to Use: party tracking. When NOT to Use: enemies (never — no threat radar); structures; the ping (#15 is world-space, not edge-clamped, though it borrows the clamp when off-screen).
15. World Ping
Category: Game · Status: Draft Used In: gameplay — any player, any time
Description: A placeable world-space marker — the non-voice coordination tool. Signal-orange, never Ember Crimson (crimson means danger, exclusively — art bible §4.2).
Specification:
- Trigger: a
Pingaction — not in ADR-0007's action list; proposed binding middle mouse / gamepad right-stick click (R3), placing atPointerWorld(KB/M) or 3 u along the aim vector (gamepad). Flagged in Gaps for an ADR-0007 amendment. - Marker: a small ink-outline diamond in signal-orange with the pinger's hat silhouette inside; world-space, host-relayed (unreliable delivery acceptable); lifetime 8 s or until the same player pings again (one active ping per player); brief tone-safe chime on placement.
- Off-screen: the ping gets its own edge indicator using the diamond glyph (the #14 clamp, a distinct band).
- Rate limit: 1 per second per player (spam guard; no UI for it).
- Accessibility: shape + glyph, never colour alone; chime optional.
When to Use: pointing at a node, a threat, a build spot. When NOT to Use: as a map / waypoint system (none exists); as text chat (none); never persistent.
16. Compass Tide-Glint
Category: Game · Status: Draft Used In: HUD (closed list) — the only tide UI
Description: A per-socket screen-edge glint giving direction + temperature only for a socket the player cannot see from where they stand. No timer, no countdown, no progress — impossible by API (tide AC-27; the HUD receives TintFloats only).
Specification:
- One glint per socket (1 at MVP, 3–4 at Tier 1), clamped to the screen edge toward the socket in a band distinct from #14 / #15 (band geometry:
hud.mdLayout Zones Z6 — 0–24 px from the frame edge); shown only while that socket's shoreline is off-screen. - The only tide-coloured HUD element: Tide Amber at
amberIntensity, Tide Violet atvioletIntensity, near-invisible when both ≈ 0. Violet is held at 1.0 throughEvictionGrace(tide F7) — the glint never "goes out" early. - Shape: a soft edge-glow carrying a low shore-outline hint of the docked template, so "what" reads as well as "where"; no text, icon, number, or arrowhead (direction is the clamp position).
- Update: per frame from
ITideClock.TintFloatson the presentation clock; no events. - Explicitly not a deck-memory aid, a "which lands this round" log, or a recurrence hint — the tide GDD's open UI question is ruled no at MVP (players remember; revisit as a Tier 1 design question — see Open Questions).
- Accessibility: amber vs violet is safe for protanopia / deuteranopia but both can desaturate toward grey under tritanopia (art bible §4.6 — its backup, departure mist density, is Tier 1 polish); at MVP the tritanopia backup is the glint's position plus the land's physical presence / drift-out motion — a known gap tracked in
accessibility-requirements.mdOQ 2. Direction reads by position; no motion, so reduced-motion is moot.
When to Use: tide state while the shore is off-screen. When NOT to Use: when the shore is on-screen (the world says it); any numeric or textual tide readout; threat-land warnings (danger reads on the land, never in the announcement).
17. Carry Meter
Category: Game · Status: Draft Used In: HUD (closed list: carry capacity)
Description: The bag-fill read: a slate meter that escalates near full via fill level + icon shake — never crimson — and is glanceable without opening a menu (harvesting UI Requirements; art bible §7.1).
Specification:
- Form: bag icon (ink-outline) with a vertical slate fill = used slots /
bagSlots, and a Medium-sizen/Ncount beside it. Slate#3A3D42fill on a darker slate track; outline brightens to slate-light at ≥ 80 %. - Near-full: at ≥ 80 % the icon shakes subtly at 1 Hz (±1 px); at 100 % the outline turns solid-thick and the shake stops — full is a state, not an alarm.
- Events: bag flash on host-confirmed gain (yield, refund, craft output, storage withdraw) — a 150 ms outline brighten; bag pulse on the bag-full no-op (#7 family a) — a 150 ms scale pulse + shake. Two different motions by design.
- Equipment slots are not in the meter (equipment is not bag space — inventory Rule 4).
- Accessibility: fill + count + outline thickness carry the state, never colour; reduced-motion disables shake and pulse but keeps the thick-outline full state.
When to Use: carry capacity at a glance. When NOT to Use: storage chests (read in the panel); weight / encumbrance (none exist — schema-negative); any crimson use.
21. Stack Grid
Category: Game · Status: Draft Used In: bag panel (5×2, bagSlots), shared-storage panel (8×5, storageSlots) — design/ux/inventory.md; the equipment strip is a two-slot variant
Description: The inventory grid. Slots are fungible — a stack has no fixed position and fills the first free slot — so the grid is a container view, not a layout the player arranges. One press on a stack performs the container's single verb on the whole stack.
Specification:
- Cell: 64 px square @100 % (scales with the UI-scale option); ink-outline; icon + count (count hidden at 1); empty = outlined cell (focusable). Gear (tagged stacks,
maxStack1) never merges; materials merge tomaxStackon the owning system's rules, never in the UI. - Placement: slots are fungible — a stack fills the first free slot — except where the owning system defines a placement: the crafting AC-33 equip swap returns the outgoing item to the exact slot the incoming item vacated (amended 2026-08-31).
- Grid dimensions derive from
Configs.Inventory(bagSlots,storageSlots) — never hardcoded. - Verb:
Submit/ click = one verb per container, context-labelled in the panel footer for the focused cell — bag: deposit when a chest is open, equip (gear) when none is; chest: withdraw.UI.RightClickis a no-op (no context menus). No drag, no split, no multi-select, no modifier-click at MVP (ruled 2026-08-30; one press = one host transaction,transferBatchWindow0). Whole-stack moves are all-or-nothing; a refused move shows a World-Fact Reason String (#6) and moves nothing. - Focus (#4): no wrap anywhere; every edge defined — the screen spec carries the full adjacency table (
design/ux/inventory.md, Interaction Map). Summary: row-major within a grid; across the gutter leftmost bag column ↔ rightmost chest column (same row, clamped); bag bottom row ↓ equipment strip (columns 1–2 → Weapon, 3–5 → Armor); strip ↑ returns to the bag's bottom row; Weapon ↔ Armor; edges stop. First focus = first occupied bag slot; exactly one focused element at all times. A screen using this pattern must publish its own numbered order (#4 rule). - Authority (#19): own-bag removals/gains from own actions are predicted; chest contents and storage → bag withdraws are host-confirmed only; who-took-what tags (#13) render on the chest grid. Pending verdict: input on a stack whose transfer is awaiting the host is ignored until the outcome lands (no cue; one press = one transaction).
- Unavailable verb (e.g. empty-handed unequip with a full bag): the cell is not actionable and the footer carries a World-Fact Reason String (#6) — never a refused attempt.
- Feedback: click sound; no travel animation on a move (stack leaves one grid, appears in the other on confirm); bag flash (#17) marks a confirmed gain.
- Accessibility: fully keyboard/gamepad operable; occupied vs empty by outline + icon; gear vs material by shape; tooltip (#5) on hover or focus-dwell; reduced-motion removes the brighten only.
When to Use: any container of ItemStacks. When NOT to Use: for recipes / structures (List Picker #2); for anything that would need arranging, sorting, or filtering (banned); as a drag target.
24. Interact Prompt
Category: Game · Status: Draft Used In: HUD (design/ux/hud.md E11) — chests, stations, any Interact target
Description: A world-space prompt showing which interactable Interact will act on and what it does. One at a time, nearest wins.
Specification:
- Anchor: 24 px @100 % above the nearest interactable while within interaction range, not in build mode, no panel open. Candidates:
Interacttargets only — chests, stations, and other players (Examine, #30) — never enemies or nodes (strike is aim-driven; a prompt would imply a separate verb). Ties: among structures → lowestinstanceId; structure beats player at equal distance (the chest is the likelier intent; step closer to the friend to examine —crafting.mdOQ 8). The player prompt reads "examine" with no name (hud.md E4 — names only in event copy; the Examine card carries it). - Content: input glyph resolved from the live
Interactbinding forInputFrame.Scheme(ADR-0007 defaults E / West; never an authored character — rebinding changes it) + verb + noun in world-fact tone ("open chest", "use workbench"); Small 13–14 px with the ink outline; ≤ 20 chars English (HIGH localisation priority). - Lifetime: appears the frame a target enters range, disappears the frame it leaves or a panel opens; no fade under reduced-motion (instant in both modes anyway).
- Accessibility: text + glyph, never colour; the glyph is decorative to the text (the verb is readable without it).
When to Use: any Interact target. When NOT to Use: enemies, nodes, the revive channel (#10 has its own prompt), build mode (the ghost is the prompt).
29. Repair Action
Category: Game · Status: Draft Used In: station panel (design/ux/crafting.md)
Description: Repair is an instant, stateless station verb (crafting Rule 9: free, instant, at a station of the gear's tier or higher), presented as pinned rows — one per repairable worn item — above the recipe list. Never a mode, never a material cost, never a timer.
Specification:
- Rows appear only when the player carries or wears a worn item whose tier ≤ the station's tier; the block is absent otherwise (no empty header). Row = item icon + name + "(worn)".
Submit/ click on a row sends the repair command (the detail pane's "Repair" button is pointer-only and mirrors the focused row); on host confirm the row collapses (100 ms; instant under reduced-motion), one understated thunk plays, and the item's wear accent clears everywhere (hotbar E3, bag strip, tooltip). The panel stays open so several items can be repaired in a row.- Race (item transferred away before the host acts): the row disappears without a thunk — Honest No-Op (#7).
- Accessibility: rows are ordinary #2 rows in the focus order (first focus when present); text + shape only.
When to Use: worn gear at an eligible station. When NOT to Use: as a "repair all" button (the per-item read is the point); anywhere off-station; for a higher-tier item at a lower-tier station (no row, no explanation needed — the station header states its tier).
30. Examine Card
Category: Game · Status: Draft Used In: gameplay — any nearby player (design/ux/crafting.md, AC-13's third inspection context)
Description: A read-only world-space card showing another player's equipped gear with each item's maker's mark — so the mark can be read off the item on your friend without asking (crafting Rule 5).
Specification:
- Trigger:
Interact(E / West via the live binding) with a player as the nearest interactable (#24's rule; the prompt reads "examine" — no name in the persistent prompt, per hud.md E4; the card shows the name). One card at a time. Gap (2026-08-31): at a Downed ally the same control is the Revive channel (#10,HeldFor) — proposed: Revive owns it and Examine is unavailable until the ally is up; needs combat's assent (crafting.mdOQ 7). Structures compete for the same nearest-target slot — stated incrafting.mdOQ 8. - Content: hat glyph + player name (on-demand event copy — allowed by the HUD's names rule), then one row per equipped slot: icon · tier · wear state, with the maker's-mark line (#12) beneath; empty slots read "— · unarmed" / "— · no armor" with no mark line. No numbers, no buttons, no aggregation.
- Anchored ≈ 24 px above the target's sprite; ≈ 300 px wide @100 %; updates in place if the target swaps gear; closes on
Menu/UI.Cancel, when the target leaves range, or on disconnect. - Accessibility: text + shape; Small 13–14 px with the ink outline; no pointer needed; no motion.
When to Use: reading another player's gear. When NOT to Use: for enemies or NPCs (none exist); as a stats/compare panel (flat stats — there is nothing to compare); never persistent.
31. Repeat Placement
Category: Game · Status: Draft Used In: build mode (design/ux/build-mode.md) — BB Rule 11b's repeat-placement deliverable
Description: Holding Build.Confirm after a confirmed placement advances the ghost one cell in the last ghost-move direction and commits again, one atomic PlaceStructure per cell (Rule 11a: at most one in flight). The gesture spends none of the per-cell "event": one thunk per wall.
Specification:
- Trigger:
Confirm.Held(LMB / South) — read asButtonState.Held, not an assetHoldinteraction (ADR-0007 rule (d) intact). The next commit fires only when the previous confirms (≤ 1 RTT + tick); release ends the row. - Direction: the last direction the ghost moved — KB/M: the last cell-boundary crossing of the pointer; gamepad: the right stick's dominant axis. Moving the ghost manually resets it.
- Refusal: any gate failing stops the row on that cell — the ghost stays dashed with its reason (#6); never skips a cell; release and re-aim to continue. Out of materials stops the same way.
- Accessibility: the built-in alternative is single presses per cell (identical outcome, no timing); listed as the project's third held input in
accessibility-requirements.md. Reduced-motion: no change (each wall's squash / settle is already optional).
When to Use: rows of walls (the common case). When NOT to Use: multi-cell preview / drag-a-row (a second ghost grammar, rejected 2026-08-31); anything with a placement timer (banned); crafting (Rule 8 — one craft at a time, no repeat).
Feedback Patterns
18. Confirmed Number Flyout
Category: Feedback · Status: Draft Used In: combat damage numbers; harvesting yield flyouts — the only numeric feedback in the game
Description: A world-space number rising from the point of impact only when the host confirms the outcome. Never predicted, never for misses, never for enemy HP.
Specification:
- Trigger:
SwingLanded.DamageDealtat the hit point;DamageAbsorbed.DamageTakenon the player's own sprite;HarvestResolved.yieldat the node. World-space, so every player sees them. - Form: Medium-size digits in
#E8E6DEwith the standard ink outline; harvest flyouts prefix the material icon ("+3 fiber"); rises 24 px over 600 ms ease-out, fading over the last 200 ms; consecutive flyouts within 100 ms stack with an offset. - Timing: fires from the drain's buffered flush (~1 RTT after the predicted swing) — the number is the confirmation; a whiff / no-op produces nothing.
- Never: enemy HP, node remaining swings, percentages, crits (none exist), colour coding by magnitude.
- Accessibility: readable at the 64–96 px silhouette scale; reduced-motion → static for 600 ms, then gone.
When to Use: confirmed damage and yield. When NOT to Use: predicted feedback (swing anim, whiff — silent by design); UI-panel numbers (plain text); anything the GDDs keep hidden.
19. Predicted vs Confirmed Feedback Split
Category: Feedback — the standard every pattern inherits · Status: Draft Used In: everything
Description: The authority table for effects. A pattern that shows a host-owned outcome before the host confirms it is a correctness bug, not a polish choice. This entry consolidates the four GDD authority tables so screen specs cite one place.
Specification:
| Effect | Timing | Source |
|---|---|---|
| Swing / whiff anim + sound, blade trail, recovery lockout, dash stretch-blur | Predicted (owner, instant) | combat Rule 8 |
| Impact VFX, hitstop (local), Stagger White, knockback, damage number, death / Downed | Host-confirmed | combat Rule 8 |
| Harvest swing anim / sound | Predicted | harvesting |
| Crack stage, yield burst, yield flyout, bag flash | Host-confirmed | harvesting |
| Ghost state, grid, build-mode enter / exit cue | Predicted | base building |
| Placement thunk + squash / settle + structure; demolish sound + removal; refund | Host-confirmed | base building |
| Craft stance + fill (#11); stance-break cancel | Predicted | crafting |
| Completion thunk + item + maker's mark | Host-confirmed | crafting |
| Own CARRY changes from own actions — harvest gain, craft output, demolish refund, deposit-side removal | Predicted (owner rail) | inventory |
| Storage → bag withdraws (item appears in the bag) | Host-confirmed only — not predicted (ruled 2026-08-30, #13) | inventory |
| Storage contents, who-took-what tags, any other player's inventory | Host-confirmed only | inventory |
| Bag pulse — the bag-full no-op cue (#7 family a, #17) | Predicted (owner, instant — the client knows its own bag) | harvesting / inventory |
| Revive ring fill | Predicted, resynced from replicated state | combat / ADR-0015 |
| Revive completion, cancel-by-damage | Host-confirmed | combat |
- Rule: a predicted effect may never imply a confirmed outcome (no predicted impact flash, no predicted yield); a confirmed effect never waits on an animation.
- Latency budget: confirmed effects land ≤ RTT + 1 tick after the predicted start; the swing window (
≥ 0.18 s) masks it (ADR-0002 / ADR-0015).
When to Use: every screen spec cites this table for each effect it introduces. When NOT to Use: no exception exists.
Standard Control Patterns (continued)
20. Rebind Row
Category: Standard · Status: Draft Used In: Settings → Controls (design/ux/settings.md; ADR-0014's screen; behaviour fixed by ADR-0007 §4 as amended 2026-08-31)
Description: One row per action per scheme: action name · current binding glyph · Rebind button. Swap-on-confirm conflicts, protected actions, and a reset floor that works with no bindings at all.
Specification:
- Row: action label (Medium) · binding glyph for the selected scheme (the Scheme cycler #27 — KB/M or Gamepad) ·
Rebind(Secondary button; the row's own Submit — never pointer-only).Menushows a lock glyph and no Rebind button (protected — ADR-0007 §4); theUImap is not listed at all. Interact / Revive is one row ("Interact / Revive (hold)") — the two actions share a control by design and one override applies to both (ADR-0007 §1 / §4 amendment). On KB/MMoveshows its four composite parts as rows; on Gamepad one stick row plus Aim stick. - Rebind flow: press → the row enters a listening state ("press a key… 5" counting down; 5 s timeout → cancel — 10 s under consideration, settings OQ 8); the next input on a device of that scheme is captured (
PerformInteractiveRebindingviaAwaitable); Esc /UI.Cancelcancels the listen, not the panel — on either device (the scheme's device exclusion applies to capture only); a pointer click outside the row cancels it too (and a click on a tab / Back then applies). - Conflict (swap on confirm = exchange — amended 2026-08-31 from
design/ux/settings.md): if the new binding belongs to another action in the same map + scheme, both rows highlight (outline weight, no colour) and a one-line prompt "also bound to Dash — swap?" offers Confirm / Cancel with focus on Confirm; Confirm exchanges — the rebound action takes the captured control and the other action takes the control just vacated — so the default set (one rebindable binding per action per scheme) never produces an empty binding — a row edits the action's primary binding only; the fixed secondaries (Build.CancelEsc,PickPrev/PickNextscroll — ADR-0007 §4 amendment) are hidden and unrebindable; a refusal (#6) arises only when an action would still end unbound. A capture of a protected action's control (Menu) is refused before any prompt, regardless of who else shares that control ("Menu always stays on {glyph}" — glyph from theMenuentry ofIRebindService.Display(scheme), layout-aware). While a conflict prompt shows it is modal within the row: focus cannot leave it, Esc / East is its Cancel. When the Interact / Revive pair is the rebound row, the colliding action receives the pair's single vacated control. Shared LMB / RMB pairs are not paired for rebinding — build-mode routing follows control identity (ADR-0007 §3). (The earlier "Confirm clears the other row" was refused on every default conflict — dead as written.) - Reset: a
Reset to defaultsSecondary button on the screen (amended 2026-08-31 — the settings screen has no Primary; a bulk discard never carries the heaviest weight) and the recovery floor — holding the defaultMenucontrol (Esc / Start) for 3 s at the title screen resets all overrides. The one deliberate hold outside #10; title screen only. - Persistence:
bindings.jsonper machine, atomic write; never in the world save. - Accessibility: fully navigable via #4; the listening state is text + outline, never colour; the timeout prevents a stuck state.
When to Use: control rebinding. When NOT to Use: in-game (title / pause settings only); the UI map (Unity's reference set, not rebindable); aim-persistence or deadzone tuning (those are input.json knobs, not bindings — a deadzone slider is a Tier 1 settings item).
25. Slider
Category: Standard · Status: Draft Used In: Settings → Display (Brightness, HUD opacity), Audio (Music / SFX / UI) — design/ux/settings.md; Tier 1: deadzone / aim-sensitivity
Description: A stepped linear control with an always-visible numeric readout. The number is the read; the track is the affordance. Every step applies live.
Specification:
- Visual (260 px control column at 100 %): ink-outlined track with slate fill left of a round knob; numeric readout to the right (Medium, e.g.
80 %,+10 %) — always shown, never behind hover. Steps are fixed by the row (5 % / 5 units); knob positions are discrete. - Input: keyboard ← / → one step (key-repeat 300 → 100 ms, #4); gamepad
UI.Navigate← / → the same; mouse drag the knob or click the track (nearest step). The mouse wheel never adjusts a slider — it scrolls the list (accidental-change guard).UI.Submitdoes nothing. - Feedback: knob eases 100 ms (snaps under reduced motion); readout updates instantly; the governed effect applies the same frame; one UI click per step (on the UI bus — so the UI-volume slider is audible while moved).
- Ends: the knob stops; no wrap; the readout states the bound.
- Accessibility: the readout is the non-visual channel — the track alone never carries the value; focus per #4; 44 px row; ≥ 44 px knob hit target.
When to Use: any bounded numeric setting (volume, gamma, opacity, deadzone). When NOT to Use: for anything in-world (no gameplay value is a slider — Pillar / tuning knobs are data, not UI); for timers; where finer than the step is wanted (the step is the design); where two states suffice (#26).
26. Toggle
Category: Standard · Status: Draft Used In: Settings → Display (VSync — provisional), Audio (Mono), Accessibility (Reduced motion, Flash reduction, Revive toggle-hold) — design/ux/settings.md
Description: A two-cell switch [ OFF | ON ]. The active cell is filled and carries the word, so the state reads by word + fill — never by colour or knob position alone.
Specification:
- Visual: two 60 px cells (120 px total) in the control column; the active cell slate-filled with its word in text colour; the inactive cell hollow with its word in outline weight. Words are localised (≤ 5 chars; a longer language uses its short form).
- Input:
UI.Submit/ click either cell / ← (→ OFF) / → (→ ON) — a press toward the current state is a silent no-op. - Feedback: fill swaps 100 ms (cut under reduced motion); the governed effect applies the same frame — the reduced-motion toggle changes this panel's motion the frame it flips; one UI click.
- Accessibility: word + fill; focus per #4; 44 px row.
When to Use: any binary setting. When NOT to Use: three or more options (#27); an action (#1); any in-world state (nothing in the world is a switch); as a checkbox list (none exist).
27. Option Cycler
Category: Standard · Status: Draft Used In: Settings → Controls (Scheme), Display (Window mode, Resolution — provisional), Accessibility (UI scale) — design/ux/settings.md
Description: ◂ value ▸ over a short, fixed, ordered list. No wrap: the arrow at an end turns dashed. The project's answer to every "dropdown" — nested popups do not exist (#3: one panel at a time).
Specification:
- Visual: two 24 px arrows at the ends of the control column, the current value centred between them (Medium, ≤ 16 chars). The arrow at a list end is dashed (#1's disabled grammar) — the end state reads by shape, not colour.
- Input: ← / → (
UI.Navigate) steps;UI.Submitsteps forward when possible, otherwise a silent no-op (the dashed arrow already explains); mouse clicks either arrow; clicking the value does nothing. - Feedback: the value replaces instantly (no slide — nothing to imply direction); the governed effect applies the same frame (UI scale re-lays out next frame, no tween); one UI click. The Scheme cycler is session-only and writes nothing.
- Accessibility: the value is text; list order is stable and stated in the screen spec; focus per #4; 44 px row.
When to Use: 2–6 discrete named options whose set is known and short. When NOT to Use: more than ~6 options (a #2 list — none needed at MVP); numeric ranges (#25); binary (#26); anything that would tempt a popup / dropdown (never).
Animation & Sound Standards
Animation (art bible §7.5, binding):
- Panels / menus: 100–150 ms ease-out on open / close / selection swap; no bounce, no flourish, no curve other than ease-out.
- Never animates: anything that gates an input — no menu-open delay, no confirm-hold timers (the two deliberate holds are
Revive#10 in-world and the title-screen reset floor #20), no transition that could cost a frame in combat. - Impact budget: squash-and-stretch is reserved for impact (placement thunk, displaced-player settle, revive-ring pop) — one settle beat, reused, never new per-feature VFX.
- Reference durations: flyouts (#18) 24 px / 600 ms; pulses (#7, #17) 150 ms; edge-indicator slides (#14) 100–150 ms; Downed pulse 1 Hz.
- Reduced-motion contract: every pattern states its reduced-motion fallback (static state, discrete steps, thicker outline); the option itself is committed in
design/accessibility-requirements.md.
Sound (GDD audio rows; desirable, not MVP-blocking unless a GDD says otherwise):
- One understated click for buttons; brief non-musical cues for panel open / close and build-mode enter / exit; no per-move ticks (list rows, ghost moves — noise fatigue).
- Predicted vs confirmed applies to sound exactly as to visuals (#19): whiff / swing / craft-loop predicted; impact / thunk / completion / demolish host-confirmed, the same frame as their visual.
- Tone-safe throughout: no "denied" buzz, no failure stingers, no alarm; docking and departure stingers belong to the tide (arrival = invitation, departure = farewell — never alarm).
- Crimson is a colour, not a sound: nothing in UI audio signals danger — telegraphs do that in-world.
Gaps & Patterns Needed
| Gap | Needed by | Owner / next step |
|---|---|---|
~~Stack Grid~~ — added as #21 (2026-08-31, from design/ux/inventory.md) | — | done |
Ping action — not in ADR-0007's action list | #15 | ADR-0007 amendment (MMB / R3 proposed; must exist in both schemes) |
| Toggle-hold accessibility option for #10 | accessibility tier | design/accessibility-requirements.md (queue item ⑤) |
~~HUD composition~~ — closed by hud.md (corner clusters, centre clear) | — | done 2026-08-31 |
~~Hotbar / equipped-tool row~~ and ~~dash-cooldown glyph~~ — closed by hud.md E3 / E2 (HUD-owned elements; numbers #22–23 released, not used) | — | done 2026-08-31 |
~~Party HP row~~ — closed by hud.md E1 / E4 (pips + hat glyph) | — | done 2026-08-31 |
~~Interact prompt~~ — added as #24 (from hud.md E11, 2026-08-31) | — | done |
~~Slider / Toggle / Option cycler~~ — added as #25 / #26 / #27 (2026-08-31, from design/ux/settings.md; the ten MVP settings controls all have a pattern) | — | done |
~~Equipped-gear wear indicator~~ — closed by hud.md E3 (frayed-corner accent on the hotbar slot; HUD-owned, no separate pattern) | — | done 2026-08-31 |
| Crack-stage feedback — node depletion read on the node itself (harvesting UI Requirements: never a HUD bar); today only a #19 row | first harvesting presenter story | add as pattern #28 (world-space state read; states = swingsTotal stages + husk) |
| Dashed-outline segment:gap ratio (#8) and the ghost strip-to-black test | art-bible fix-pass | art-director |
| Deadzone / aim-sensitivity sliders in settings | Tier 1 | ADR-0014 / settings screen spec |
Character stepper — a ~32-value wrapping glyph stepper (title-screen.md's six join-code steppers). Runs well outside #27's "2–6 discrete named options … no wrap" envelope: it wraps (a character ring has no end), Submit does not step it, and six-in-a-panel is a different geometry from #27's single 260 px control | if a second screen ever needs code / serial entry | candidate; today a title-screen-local deviation from #27, declared in that spec |
Text input field — no pattern exists; the library's own standard-control list names one. Needed by: world renaming (Tier 1, title-screen.md OQ 3). Not needed by the join panel — that was deliberately built from #27 steppers (a join code) so it stays gamepad-operable | Tier 1 | /ux-design when renaming lands; if any screen ever needs free text, this must be authored first |
Screen-level notice slot — a persistent-until-next-input, latest-wins fact line owned by a screen rather than a control (title-screen.md Z3). #6 governs tone and per-cause content but its placements are all control-adjacent (ghost / rows / storage / revive) | if a second screen needs one | candidate; today a title-screen-local slot |
| Invisible timed hold — a hold with no progress feedback by design, because it must work when nothing renders (the ADR-0007 §4 reset floor). #10 assumes a visible filling read | only if a second one appears | documented in title-screen.md; #10 explicitly does not cover it |
Two-step in-place arm (label-swap confirm — pause.md's End session: press → armed label → press again; deliberate-move disarm + 5 s revert) | only if a second screen ever needs one | candidate #32; today a pause.md-local mechanism borrowing #9's principle (preview the consequence in place, never a dialog) |
Open Questions
- Player journey map not yet created. Template at
.claude/docs/templates/player-journey.md; every screen spec after this one should cite its journey phase and the player's emotional state on arrival. Run a player-journey session before the HUD spec. - ~~Accessibility tier not yet defined~~ Resolved 2026-08-29:
design/accessibility-requirements.mdcommits to Standard tier (≈ WCAG-AA for UI text) plus reduced-motion, visual audio backups, mono, and flash-reduction; toggle-hold for #10 is committed there. Remaining: the MVP tritanopia backup for the tide (its OQ 2). - Provisional values to playtest-tune: reason-string 250 ms minimum / 2-frame gate switch (#6); demolish cone 60° (#9); who-took-what tag 2 s (#13); Downed banner 3 s (#14); ping lifetime 8 s (#15); carry near-full 80 % (#17); flyout 24 px / 600 ms (#18); rebind listening timeout 5 s (#20).
- Deck-memory aid (which lands have appeared this round) — ruled no at MVP (#16); revisit as a Tier 1 design question with the creative director, not as a UI ticket.
Pingbinding — needs the ADR-0007 amendment before #15 can be implemented; confirm MMB / R3 or choose another pair.- UI Toolkit vs UGUI for world-space elements (flyouts, revive ring, ping marker, storage tags) —
architecture.mdrules UI Toolkit for screens; world-space elements are sprites or UGUI — ADR-0014 decides; this library is neutral.