Files
Ferrous-Solitaire/docs/ui-redesign-2026-07.md
T
2026-07-13 15:49:15 -07:00

11 KiB
Raw Blame History

Menu UX Redesign — July 2026

Status: IN PROGRESS. A / E / C / G shipped in v0.43.0; B implemented on feat/home-hierarchy (2026-07-13); F / H and the IM backlog remain open. Visual identity (Terminal / base16-eighties) is settled and out of scope — this is about structure and interaction, not colors or type.

Diagnosis (from code survey, 2026-07-07)

Surface Today Pain
Settings One scrolling modal, 5 section labels, ~37 control rows, single Done Scroll-hunting for any toggle; endless on the folded (cover) screen; every new feature (theme store, import) stretches it further
Home Modal launcher: stats strip, draw-mode row, difficulty rows, 6 equal mode cards, Cancel No "Continue" primacy; deal options (draw/difficulty/winnable) crowd the mode choice; six cards weighted identically though Classic dominates play
HUD menu Popover with 7 destinations (Help, Modes, Stats, Achievements, Profile, Settings, Leaderboard) "Modes" duplicates Home's job; flat list, no grouping; each item opens another modal — modal-on-modal navigation
Global Everything is a modal over the felt No hierarchy; near-square unfolded Fold renders one narrow wasted column

Phase A — Settings tabs (highest pain, lowest risk)

  • Replace the single scroll with tabs: Audio · Gameplay · Appearance · Accessibility · Account.
    • Appearance = today's Cosmetic (card back, background, card theme, theme store, import).
    • Accessibility = extracted from Gameplay/Cosmetic: color-blind, high-contrast, reduce-motion, touch input mode, tooltip delay.
    • Account = Sync + Privacy merged.
  • Narrow screens: tab chips in a row under the header. Wide (aspect > ~1.2 — unfolded Fold, desktop): left rail, rows in two columns.
  • Only the active tab's rows are spawned → each tab is ≤ 8 rows and fits the cover screen without scrolling.
  • Implementation constraint: pure ui.rs re-layout + one SettingsTab resource. The SettingsButton enum, input handlers, and persistence are untouched, so the 25 settings tests and the ambiguity gate stay green.

Phase B — Home becomes a real home

  • Hierarchy, top to bottom:
    1. Continue card (only when a game is in progress) — mode, elapsed, score.
    2. Hero New Game — one tap, reuses last mode + deal options.
    3. Compact 2×3 mode grid (Classic, Daily, Zen, Challenge, Time Attack, Seed) — smaller cards, glyph + name + one-line description on wide only.
    4. Stats strip moves to the bottom (or right pane when unfolded).
  • Deal options (draw 1/3, difficulty, winnable-only) move off the top level into a disclosure on the Classic card / New Game hero — they are Classic-mode concerns, not global ones.
  • Cancel becomes Back to table and only renders when a game exists.
  • Unfolded Fold / desktop: two panes — modes left, Continue + stats + daily/weekly right.

Phase C — HUD menu consolidation

  • Drop Modes from the popover (Home owns mode selection; HUD's New-Game path opens Home).
  • Group the remaining six: Play (Home) · You (Profile, Stats, Achievements) · Community (Leaderboard) · System (Settings, Help) — section dividers in the existing popover, not a new widget.
  • Dismissal audit: Esc / scrim-tap / Done must behave identically on every modal (most already do via ScrimDismissible; sweep the stragglers).

Phase D — Validation gates

  • Optional Stitch mockups (Terminal design-system asset assets/0f1652274fea460585d0b331a9ddbb06, project 593811793268308763) for Home + Settings before implementing Phase B. Phase A is safe to build straight in-engine.
  • Per-phase gates: full test suite, clippy -D warnings, ambiguity gate at 0, Android checklist (§15.3), on-device pass on the Fold 7 in BOTH postures.

Sequencing & risk

Phase Size Risk Depends on
A — Settings tabs ~1 session Low (layout-only)
B — Home hierarchy 12 sessions Medium (touches launch flow) D mockups if wanted
C — Menu grouping small Low B (Modes removal)

Phase E — "You" hub (reuses the Phase A tab component)

Profile, Stats, Achievements, and the replay browser are four separate modals today, reached through the HUD popover one at a time. Fold them into ONE tabbed hub — Profile · Stats · Achievements · Replays — using the same tab widget Phase A builds for Settings. Cuts the HUD popover to four destinations and makes the tab component pay for itself twice. Existing per-screen systems keep their markers/handlers; only the outer shell changes (same trick as Phase A).

Phase F — Mobile ergonomics (Fold-first)

  • Bottom action bar on touch: Undo, Hint, Draw within thumb reach at the bottom safe-area edge (via the existing SafeAreaAnchoredBottom), score-only band stays on top. Desktop keeps the current top band. Biggest one-handed-play win available; folded posture is tall and top-heavy today.
  • Hold-to-repeat Undo — press-and-hold steps back repeatedly (respecting the existing undo scoring penalty), instead of tap-tap-tap.
  • Both are additive; game stays fully playable with the top band alone (UI-first rule §3.3 satisfied).

Phase G — Post-win flow

Win summary today is a stats dump with a close. Give it an action hierarchy: Play again (same mode/options, primary) · Share replay (the upload already returns a share URL; surface it here with copy feedback) · Watch replay · quiet stats below. Rematch loop drops from 4 taps to 1.

Phase H — Feedback polish (small, bundling candidates)

  • Unify queued vs. immediate toast styling (two subtly different styles exist today) and anchor both to one position; consider a 5-item toast history on the stats screen.
  • Theme-store modal: render the preview PNGs the server already serves (v1 is text-only rows).
  • Hint: optional ghost-motion preview of the suggested move instead of a static highlight (auto-disabled under reduce-motion).

Phase I — Onboarding & discoverability

The first-run onboarding exists, but everything learned after it is invisible: the radial menu (long-press/right-click), hint cycling, tap-to-toggle HUD chrome, hotkeys. Add contextual one-time tips — fired by the situation, not a tour (first stall → hint tip; first long-press-able stack → radial tip; each shows once, stored in Settings like shown_achievement_onboarding already is). Plus a one-shot "What's new" card on first launch after a version bump — ObtainX updates are silent today, so shipped features go unnoticed (nobody will find the theme store on their own).

Phase J — Keyboard & focus completeness (desktop + web)

ui_focus already gives deterministic Esc order and tab-walk (FocusRow/Focusable), and KeyboardDragState exists — but full keyboard-only play has never been audited end to end. Deliverables: every modal reachable/dismissable without a mouse, a visible focus ring styled to the Terminal system (current focus state is subtle), and a hotkey cheat-sheet overlay (hold ?) generated from the actual bindings instead of the static Help text. Web build (Rhys) benefits most.

Phase K — UI scale & touch-target accessibility

All type sizes flow through TYPE_* tokens and layout through compute_layout — which makes a UI scale setting (90/100/115/130 %) cheap to wire and genuinely useful on the Fold's dense cover screen. Pair with: respect Android system font scale on first run (seed the setting from it), and a one-time audit that every interactive element meets the 44 px logical minimum in BOTH Fold postures (the pill buttons and picker swatches are the suspects). Lands in the Accessibility tab that Phase A creates.

Phase L — Empty & loading states, standardized

Async/empty surfaces each improvise today: leaderboard with no entries, stats with no games, replay browser with no replays, theme store loading/error (just added), sync status text. Define ONE pattern — glyph + one-line explanation + a single next-step action — as a spawn_empty_state helper in ui_modal, and sweep all five surfaces onto it. Small, mechanical, big perceived-quality win.

Phase M — Sync transparency & data stewardship

The sync layer returns ConflictReports ("data is never silently discarded") — but no UI ever shows them; players can't tell what a merge did. Add: a post-sync summary line ("merged, 2 conflicts kept newer values" → tap for detail in the Account tab), a visible last-synced timestamp (exists as SyncStatus::LastSynced, barely surfaced), and local data export/import (zip of the JSON saves) for device migrations without a server. Closes the trust loop the server work opened.

Roadmap tie-ins (already in flight elsewhere)

  • Spider mode (feat/spider-core, PR #157): Home's mode grid gains a 7th card once engine work lands — the 2×3 grid in Phase B should be designed as N-card flow from day one. Spider UI also needs positional card→entity keys (documented in spider.rs module docs).
  • Show solution (feat/solution-line, in flight): lands in the pause menu; Phase C's grouping should leave room for it under a "Game" cluster if the pause menu grows.

Sequencing (updated)

Phase Size Risk Depends on
A — Settings tabs ~1 session Low — (DECIDED: tabs)
E — "You" hub ~1 session Low A's tab component
C — Menu grouping small Low E (popover shrinks)
B — Home hierarchy 12 sessions Medium D mockups if wanted
F — Bottom action bar ~1 session Medium (input paths)
G — Post-win flow small Low
H — Feedback polish small each Low
I — Onboarding & discoverability ~1 session Low ships best after B/F land
J — Keyboard & focus completeness ~1 session Low A/E (fewer modals to audit)
K — UI scale & touch targets ~1 session Medium (layout-wide) A (Accessibility tab)
L — Empty-state standardization small Low
M — Sync transparency & export ~1 session LowMedium A (Account tab)

Suggested order: A → E → C (one arc: the tab component and the menu slimming), then B, then F/G/H as independent follow-ups.

Open decisions

  1. Settings: tabs vs. sub-pagesDECIDED 2026-07-07: tabs.
  2. Deal options: disclosure on Classic card vs. keep global rowDECIDED 2026-07-09: disclosure on the Classic card / New Game hero.
  3. Time Attack + Seed: top-level cards vs. "More" cardDECIDED 2026-07-09: top-level cards, symmetric 2×3 grid.
  4. Stitch mockups for Phase B, or iterate directly in-engineDECIDED 2026-07-09: directly in-engine.
  5. Phase F bottom bar: touch-only vs. also desktopDECIDED 2026-07-13: touch-only. Desktop keeps the current top band; the bar is additive per §3.3 so a later desktop rollout stays a settings flag away.