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>
12 KiB
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.mdare 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 1600–2400 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 1440–2400; 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 (60–90 cm). Mobile uses the same
rungs at a closer reading distance (30–40 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:
- Read the mobile mockup for content + visual hierarchy.
- Apply the universal adaptation rules above.
- Apply the closest matching per-screen rule (e.g. an info modal uses the same shape as Win Summary or Stats).
- 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_sizeruns at startup and the player can resize freely. Hardcoded breakpoints in plugin code should reference the currentWindowwidth via aLayoutResourcelookup, not the launch size. WindowResizedalready 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.