Files
Ferrous-Solitaire/docs/ui-mockups/desktop-adaptation.md
T
funman300 8325bf6cf7 chore: rename app from Solitaire Quest to Ferrous Solitaire
Replace all display-name occurrences across web pages, Rust source,
docs, and Cargo metadata. Update localStorage token key from sq_token
to fs_token. Tagline "Klondike Solitaire" retained as genre descriptor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:04:45 -07:00

12 KiB
Raw Blame History

Terminal — Desktop Adaptation Spec

Why this exists. The 24 mockups in this directory are mobile (390 × 844 logical, iPhone 14 Pro frame) with one exception (home-menu-desktop.html). The Stitch project that produced them is named "Ferrous Solitaire Mobile Redesign" — the mobile-first framing was deliberate when the new Android target opened, but desktop is still the primary delivery surface. Porting the mobile mockups 1:1 would land a 390-px-wide column floating in the middle of an 1800 × 1100 window. This file is the rules-based desktop companion — apply these adaptations whenever you port a Bevy plugin against a mobile mockup in this directory.

Status

  • Token system. All tokens (palette, type scale, spacing, radii, motion) in design-system.md are layout-agnostic and apply unchanged on both targets. Do not introduce desktop- specific token variants — adapt geometry, not tokens.
  • Already adapted in code. v0.20.0's port is layout-agnostic (modal scaffold, toasts, table chrome, card chrome, gameplay- feedback, splash cursor). Those surfaces already adapt correctly because their Bevy UI nodes use flex / percent / stretch sizing rather than fixed pixel widths from the mockups.
  • Not yet adapted in code. Any future plugin port that copies layout from a mobile mockup must apply the rules below.

Viewport assumptions

Range Width × height Source
Mobile target 390 × 844 iPhone 14 Pro logical, Stitch mockup canvas
Desktop minimum 1024 × 600 Smaller windows degrade to mobile rules
Desktop default ~70 % of monitor apply_smart_default_window_size (since v0.19.0)
Desktop typical 1600 × 900 to 2560 × 1440 The range we tune for
Desktop max 3840 × 2160 4K, with HiDPI scaling already applied

The "smart default" sizer means a 1080p monitor opens a ~1344 × 756 window, a 1440p monitor opens ~1792 × 1008, a 4K monitor opens ~2688 × 1512. Tune for the 16002400 width band as the centre of the distribution; below 1024 width, fall back to the mobile rules verbatim.

Universal adaptation rules

Apply these to every screen unless the per-screen section overrides them.

1. Edge margins

Mobile Desktop
margin-edge: 16px (SPACE_4) SPACE_5 (24 px) for windows < 1440 wide; SPACE_6 (32 px) for 14402400; SPACE_7 (48 px) for ≥ 2400

Engine: drive from LayoutResource based on Window size, not a constant.

2. Modal max-width

Mobile Desktop
100% - 2 × edge-margin min(720 px, 50 % of viewport)

The 720 px cap is already in ui_modal::spawn_modal. No code change needed; this rule documents why it's there.

3. Vertical content stacks

A mobile screen often stacks Header → Body → Footer vertically to fit a tall narrow column. On desktop, prefer horizontal distribution where the content allows:

  • Header rows that stack vertically on mobile (title above count above timer) → keep them in one horizontal row on desktop.
  • Two-column flex layouts (e.g. Settings rows: label left, control right) — already work on both targets; no change.
  • Cards stacking with mt-48-style fixed gaps — replace with flex / percent gaps so the layout breathes.

4. Touch-target minimums

Mobile spec mandates 48 dp minimum touch targets. Desktop has no such floor (mouse precision is finer), but don't shrink below mobile's 48 px for primary actions — keyboard / gamepad focus rings still need a visible target.

Secondary controls (chip-style toggles, hotkey hints, etc.) can shrink to TYPE_BODY (14 px) text + SPACE_3 (12 px) padding on desktop where they were larger on mobile.

5. Bottom-anchored elements

Mobile mockups often anchor key controls (action bar, primary CTA, toast position) to the bottom of the viewport for thumb reach. Desktop has no thumb-reach concern:

  • Toasts — keep bottom-anchored (already done in a137607), the design language is consistent across targets and the bottom is still the least-disruptive overlay zone.
  • Action bars — top of viewport on desktop unless the per-screen section says otherwise. The HUD already sits on top.
  • Single primary CTA — modals already right-align in the actions row; no change.

6. Typography rungs unchanged

Do not shift TYPE_* tokens up a rung for desktop. The spec's 14 / 18 / 26 / 40 progression is already calibrated for the desktop reading distance (6090 cm). Mobile uses the same rungs at a closer reading distance (3040 cm); same physical angular size on the eye.

7. Hotkey hints become full strings

Mobile cells like ▌Esc — the cursor block plus key letter — can expand to [Esc] cancel style on desktop where horizontal real-estate is cheap. Drives discoverability of keyboard-only flows. Optional; only apply where horizontal space exists.

Per-screen adaptation rules

Game Table

Mockup: game-table-mobile.html (390 × 844).

Element Mobile Desktop
HUD band full width, 56 px tall full width, 48 px tall
Foundation row 4 piles centred, fan-tight 4 piles centred, gutter doubled so the row fills ~50 % of viewport width
Stock + waste left of foundations, stacked left of foundations, horizontal pair: stock on the left, waste to its immediate right (the mobile vertical pair feels cramped on a wide canvas)
Tableau row 7 columns, 4 % gutter 7 columns, 6 % gutter, total tableau block ≤ 70 % viewport width
Card aspect 2 : 3 (already in Layout::card_size) unchanged — card aspect is domain
Tableau fan TABLEAU_FAN_FRAC = 0.25 unchanged — fan is in card-height units, not viewport units
Drag-shadow offset small unchanged — pinned to 0 alpha under Terminal anyway

Engine impact: solitaire_engine/src/layout.rs::compute_layout already drives most of this from Window::size(). The mobile vs. desktop difference is the gutter percentages — bake desktop gutters when window width ≥ 1024.

Win Summary

Mockup: win-summary-mobile.html (390 × 858).

Element Mobile Desktop
Modal width 100 % 2 × edge min(720 px, 50 % viewport) (already done by ui_modal)
Score row stacked vertically (line per metric) 3-column grid: Score / Time / Moves in one row, breakdown rows below in single-line per row
Action buttons full-width stacked (Play Again, Continue, Stats) right-aligned action row — the existing spawn_modal_actions already does this on both targets

Engine impact: solitaire_engine/src/win_summary_plugin.rs. The score-breakdown-stagger animation (MOTION_SCORE_BREAKDOWN_*) is unchanged across targets.

Settings

Mockup: settings-mobile.html (390 × 4330 — long scroll).

Element Mobile Desktop
Modal width 100 % 2 × edge min(720 px, 50 % viewport)
Sections full-width labels above stacked controls section labels left, control widget right — already the engine's pattern; no change
Long page scroll the whole modal two-column layout: nav (sections list) on left ~30 %, current section on right ~70 %. Reduces scroll distance on desktop
Sliders full-width on mobile cap at 320 px on desktop

Engine impact: if a desktop port wants the two-column nav, it's a settings_plugin rewrite. Keep the existing single-column stacked-modal layout for now — it works on both targets and the two-column variant is a polish item, not a blocker.

Help & Controls

Mockup: help-mobile.html (390 × 2544).

Element Mobile Desktop
Modal width 100 % 2 × edge min(720 px, 50 % viewport)
Section list one column of Heading → 2-col rows two columns of section blocks for windows ≥ 1280 wide; halves vertical scroll distance
Hotkey rows `key description` 2-col flex

Engine impact: help_plugin. Single-column on mobile, 2-col on desktop windows ≥ 1280 wide is a flex-wrap option.

Pause Menu

Mockup: pause-menu-mobile.html (390 × 1768).

Already a small modal; no significant geometry change. Modal already uses ui_modal::spawn_modal which caps width and centres. No desktop-specific rule.

Home Menu

Mockup: home-menu-mobile.html and home-menu-desktop.html (both already in this directory — desktop variant is the authoritative reference).

The desktop mockup already specifies the layout. Cross-check it against the mobile version when porting; differences are deliberate (more horizontal real-estate, larger primary CTA, the secondary actions row).

Splash

Mockup: splash-mobile.html (390 × 844).

Element Mobile Desktop
Full-screen overlay inset-0 unchanged — splash always covers the viewport
Cursor block () 96 px JetBrains Mono unchanged — already done in cdcadda. The 96 px size scales fine on desktop because the splash is a brand beat, not a layout-driven element
Title RUSTY SOLITAIRE 32 px scale to 40 px (TYPE_DISPLAY) on desktop
Subtitle TERMINAL EDITION 12 px unchanged
Boot log lines 70 % width column cap at 480 px so the column doesn't stretch on a wide window
Progress bar 100 % 2 × edge cap at 720 px
Palette swatch row + version footer bottom-anchored unchanged; bottom-anchor still reads correctly on desktop

Engine impact: splash_plugin already has the cursor block (cdcadda). The boot log / progress bar / palette swatch rows are the next polish increment when option D is picked up.

Stats

Mockup: stats-mobile.html (390 × 2624).

Element Mobile Desktop
Modal width 100 % 2 × edge min(720 px, 50 % viewport)
Big-number cards 2 × 2 grid 4 × 1 row for windows ≥ 1024 wide (the four headline metrics fit in a single horizontal row at desktop scale)
Latest-win caption full-width line unchanged
Replay clip / share row full-width row unchanged

Profile / Achievements / Theme Picker / Daily Challenge

These follow the standard modal pattern (spawn_modal with header / body / actions). They already work on desktop because ui_modal handles modal-width capping. Per-screen tweaks are small and listed below; no structural changes:

  • Profile — avatar + level / streak chips can flow into a single horizontal row on desktop instead of stacking.
  • Achievements — 3 × N grid on mobile becomes 4 × N or 5 × N on desktop where windows ≥ 1280 wide.
  • Theme Picker — 2-col grid of theme cards on mobile becomes 3- or 4-col on desktop.
  • Daily Challenge — single-column scroll on both; no change.

Mockup parity gap

The 9 missing-plugin screens (splash, challenge, time-attack, weekly-goals, leaderboard, sync, level-up, replay-overlay, radial-menu) have only mobile mockups. When porting any of these plugins:

  1. Read the mobile mockup for content + visual hierarchy.
  2. Apply the universal adaptation rules above.
  3. Apply the closest matching per-screen rule (e.g. an info modal uses the same shape as Win Summary or Stats).
  4. No new layout pattern without explicit user approval. Adapting an existing pattern is in scope; inventing a desktop- specific component is design work and should be flagged as such.

Process notes

  • Smart-default sizer is the layout's source of truth. Before reading the mockup, always re-read Window::size()apply_smart_default_window_size runs at startup and the player can resize freely. Hardcoded breakpoints in plugin code should reference the current Window width via a LayoutResource lookup, not the launch size.
  • WindowResized already drives layout recomputes (CLAUDE.md §3.4). Any per-window-width adaptation in this file should hook into the existing recompute path, not a new system.
  • Mobile rules win at narrow desktop windows. A user dragging their desktop window down to 600 px width is closer to the mobile use-case than the desktop one. Below 1024 px width, apply the mobile rules verbatim.
  • Run on a 4K monitor before declaring a port done. HiDPI scaling routes through Bevy's logical sizing, but visual polish (border thickness, motion budgets at high refresh rate) is worth eyeballing.