GENERATED VIEW · PRIVATE ARCHIVE · DO NOT SERVE
RobCo Industries · Archive Museum

MOBILE_DENSITY_PLAN.md



RELEASE

planning/2.8.0/plans/MOBILE_DENSITY_PLAN.md

sha256 57d97a816ebd0adf · 16842 bytes · original held in the private archive

# MOBILE_DENSITY_PLAN.md — RobCo U.O.S. > **PLANNING / AUDIT ONLY.** Nothing here is implemented or committed. Measure + propose. > Target: `dev` @ `169c9fd` (Ceremony Moments Wave 1). Widths measured LIVE: **360px** and **412px**. > Method (Protocol 27): app rendered on a **static prod-like server** (NOT Vite — Vite serves > `terminal.css` as a `text/javascript` HMR module, so a plain `<link>` never applies it and every > measurement reads UA defaults; all prior units used a static server for this reason). A richly > populated FNV campaign was seeded (lvl 24, 10 inventory items, 7 perks, 6 quests, 4 status effects, > 3 squad, 3 collectibles, factions set, 2 crippled limbs) so every board shows real content. > All px below are `getBoundingClientRect()` / `getComputedStyle()` reads, not estimates. --- ## TL;DR — the headline - **Every Protocol-17 floor is currently HELD** at 360px and 412px: no horizontal overflow on any of the six subsystems, all inputs render ≥16px, tap targets ≥28px. **Density here is pure waste-trimming, never floor-shrinking.** - **There is no spacing-token system and no mobile density pass.** `:root` holds only *colour* tokens. Every board's spacing (padding, inter-board gap, header subtitle, section rhythm, tile padding) uses its **base, desktop-shared value on mobile.** The narrow-phone blocks (`@media (max-width:480px)`) currently tune only ~8 scattered specifics (faders, one VU label, drawer min-width, curio widths, annunciator, location card). Density is therefore **per-board-random**, not uniform. - **The single biggest lever is the shared board chrome** — `details.bay-board` padding `10px 10px 12px`, the `.panel.bay-board` inter-board `margin-top: 12px`, the `.bay-part-no` subtitle `margin: 2px 0 8px`, and per-board inner section margins — multiplied across **~30 boards** app-wide. Trimming these to a single mobile standard reclaims **hundreds of vertical px** with zero behaviour or floor change. - **Measured scroll length at 412px (everything a populated campaign shows):** | Subsystem | Scroll height @412px | (excl. staging-only TEST CONSOLE ~1024px) | |---|---:|---:| | OPERATOR (stat) | **7796px** | ~6772px | | SETTINGS | 5646px | ~4622px | | OPERATIONS (cargo) | 5365px | ~4341px | | DATABANK | 4001px | ~2977px | | CHASSIS | 2655px | ~1631px | OPERATOR is **~7.4 phone-screens** tall. A uniform 12–18% density trim is a real, felt improvement. **Recommendation:** adopt the **Mobile Density Standard** (§2) and apply the **Tier-1 "safe tightening"** fix list (§3) in a single `@media (max-width:480px)` block — behaviour-identical, desktop untouched, floors honoured. Hold the **Tier-2 "design/taste" items** (§4) for owner sign-off. --- ## 1. Architecture & constraints (what a fix must honour) ### Breakpoints in play | Query | Meaning | Use for density | |---|---|---| | `min-width:1000px and (hover:hover) and (pointer:fine)` | **Desktop** (Suite 129 gate) | **NEVER touch** — desktop must stay identical | | `max-width:999px` / `999.98px` | mobile single-column layout | too broad for a phone density pass | | `max-width:480px` / `479.98px` | **narrow phone** (owner's 360/412 both land here) | **← the density gate** (matches existing convention) | | `max-width:400px` | narrowest phones | reserved for extra shrink only | **Chosen density gate: `@media (max-width: 480px)`.** Every common portrait phone (≤430px CSS) is ≤480, it matches the existing narrow-phone blocks, and it can never leak onto desktop. The 481–999px band (large-phone landscape / small tablets) intentionally keeps today's looser spacing — an acceptable gap, same call the prior MOBILE_AUDIT made, since the owner is portrait-phone-only. ### Hard floors (Protocol 17) — inviolable, density never crosses these - Focusable inputs **≥16px** font (prevents iOS/Android focus auto-zoom). - Interactive tap targets **≥28px**. - **No horizontal overflow** at 360px (`documentElement.scrollWidth === innerWidth`). - Reduced-motion / a11y unaffected (spacing only — no `@keyframes`, no `:focus-visible`, no colour-only meaning). ### Other guardrails - **Fonts are already tiny (8–13px). Density comes from SPACING, never from shrinking fonts further.** - **Do NOT `display:none` the `.bay-part-no` / `.real-label` subtitle.** It carries the real, searchable control label that the Protocol-25 Module-Bay redesign exception *requires* ride along under the diegetic name. Only its *margin* may tighten. - Protocol 22: reuse existing selectors; prefer a small token set over per-selector magic numbers. - Behaviour-identical: this is spacing/sizing only — no id/handler/markup/state change. --- ## 2. THE MOBILE DENSITY STANDARD (propose) Because the codebase has **no spacing tokens**, introduce a minimal scale and apply it **only at ≤480px**. Desktop keeps its current (base) values untouched. ### 2a. Spacing scale (new `:root` tokens — mobile-valued via the density block) ```css :root { /* desktop / base — unchanged from today's hardcoded values */ --d-board-pad-block: 10px; /* board padding top */ --d-board-pad-btm: 12px; /* board padding bottom */ --d-board-pad-inline:10px; /* board padding L/R */ --d-board-gap: 12px; /* gap between stacked boards / .bay-grid / sub-panel */ --d-section-gap: 10px; /* gap between a board's inner sections */ --d-subtitle-mb: 8px; /* .bay-part-no bottom margin */ } @media (max-width: 480px) { :root { --d-board-pad-block: 7px; --d-board-pad-btm: 8px; --d-board-pad-inline:8px; --d-board-gap: 8px; --d-section-gap: 8px; --d-subtitle-mb: 4px; } } ``` > If a full token retrofit is judged too invasive for one unit, the **equivalent minimal path** is a single > `@media (max-width:480px)` block that hard-sets the same values on the selectors in §3 (no `:root` edit, > no base-rule refactor). Both reach the identical rendered result; the token form is the Protocol-22-clean > version and makes future per-game/per-breakpoint tuning one-line. ### 2b. The seven rules every mobile board follows - **R1 — Board shell.** `details.bay-board` padding = **7px / 8px / 8px / 8px** on mobile (was `10 10 12`). Reclaims ~7px vertical + ~4px horizontal **per board × ~30 boards**. - **R2 — Inter-board gap = 8px, everywhere and identical.** `.panel.bay-board { margin-top }`, the Module-Bay `.bay-grid { gap }`, and `details.sub-panel { margin-top }` all resolve to the same `--d-board-gap` so vertical rhythm is uniform (today they're 12 / 12 / 10 — three different values). - **R3 — Header subtitle.** `.bay-part-no` margin = **1px 0 4px** (was `2px 0 8px`). Keeps the searchable real-label; only trims dead space. **Never hidden** (guardrail). - **R4 — Inner section rhythm = 8px.** A board's stacked inner sections (readouts, meters, add-forms) settle to one `--d-section-gap` instead of assorted 10/12px margins. - **R5 — Interactive tiles trim toward the floor, never past it.** Over-padded tiles (faction keycaps, status lamps, perk/slot rows, meter wraps) lose surplus vertical padding but **stay ≥28px**. - **R6 — Form fields.** Inputs keep ≥16px font (floor) and their natural height; only inter-field *gaps* trim to 6px. - **R7 — Floors are absolute** (Protocol 17). If any proposed value would cross a floor, it is not applied. --- ## 3. TIER-1 FIX LIST — SAFE TIGHTENING (apply without a taste-check) Ranked by **cumulative reclaimed px** (worst offender first). All are spacing-only, desktop-scoped-out, floor-verified. Every value below was chosen against the live measurement in the Appendix. ### F1 · Shared board shell padding + inter-board gap — **HIGHEST IMPACT (all ~30 boards)** | Selector | Line | Current | → Mobile | |---|---|---|---| | `details.bay-board` | ~4605 | `padding: 10px 10px 12px` | `padding: 7px 8px 8px` | | `.panel.bay-board` | ~4640 | `margin-top: 12px` | `margin-top: 8px` | | `.bay-grid` (Module-Bay SLOT grid) | — | `gap: 12px` | `gap: 8px` | | `details.sub-panel` | ~1708 | `margin-top: 10px` | `margin-top: 8px` | **Evidence:** board content width is 300px of 360 (60px eaten by nested padding); every board pays `10+12` vertical shell + `12` gap. **Est. reclaim: OPERATOR ~11 boards ≈ 120px shell + ~40px gaps ≈ 160px; app-wide several hundred px.** Safe: no floor involved (padding/margin only). ### F2 · Header subtitle margin — **every board** | Selector | Line | Current | → Mobile | |---|---|---|---| | `.bay-part-no` | ~4680 | `margin: 2px 0 8px` | `margin: 1px 0 4px` | **Evidence:** measured 10–30px per board; ~5px trim × ~30 boards ≈ **150px**. Subtitle stays visible (guardrail). ### F3 · Faction console keycaps + meter wrap — **FACTION STANDING (11 factions)** | Selector | Line | Current | → Mobile | Floor check | |---|---|---|---|---| | `.facon-chan` | ~7424 | `min-height: 40px` | `min-height: 34px` | 34 ≥ 28 ✓ | | `.facon-meter-wrap` | ~7437 | `padding: 10px 12px 12px` | `padding: 8px 10px 9px` | n/a | **Evidence:** keycap measured 40px with 12+12 vertical padding for a 9px-font label — largest single-tile surplus on the board. Trims ~6px/keycap and ~7px on the meter wrap. ### F4 · Status-effect lamp tiles — **STATUS EFFECTS (4 tiles, more when buffed)** | Selector | Line | Current | → Mobile | Floor check | |---|---|---|---|---| | `.stlamp-tile` | ~7258 | `padding: 8px 10px 9px` | `padding: 6px 9px 7px` | purge btn stays 28px ✓ | | `.stlamp-grid` | ~7251 | `gap: 8px` | `gap: 6px` | — | **Evidence:** tiles measured 70px each; ~5px/tile + 2px grid gap. ### F5 · Perk / skill-book / magazine slot rows — **PERK LOADOUT, SKILL BOOKS, SKILL MAGAZINES** | Selector | Line | Current | → Mobile | Floor check | |---|---|---|---|---| | `.slot-row` | ~7730 | `padding: 7px 10px; margin-bottom: 7px` | `padding: 5px 8px; margin-bottom: 5px` | inner delete-btn stays 28px ✓ | **Evidence:** slot rows measured 67px each in a bounded scroll region; the row itself is not a tap target (the ✕ inside it is, and is untouched). ~6px × up to 7 visible rows ≈ 42px. ### F6 · Skill-matrix VU rows — **SKILL MATRIX (13 channels)** | Selector | Line | Current | → Mobile | Floor check | |---|---|---|---|---| | `.vu-row` | ~7156 | `padding: 5px 0` | `padding: 3px 0` | drag track unaffected (22px, arrow-key + drag) ✓ | **Evidence:** 13 rows × 41px. ~4px/row ≈ 52px. Conservative — leaves the drag surface untouched. ### F7 · UPLINK transcript + composer — **DIRECTOR UPLINK** | Selector | Line | Current | → Mobile | |---|---|---|---| | `#chatDisplay` | ~377 (base) / ~1584 (uplink mobile) | `padding: 15px; gap: 15px` | `padding: 10px; gap: 10px` | | `.scope-wrap` (uplink mobile) | ~1596 | `padding: 5px 0 4px` | keep (already trimmed) | | `#composer` | — | `padding: 8px 12px 10px` | `padding: 6px 10px 8px` | **Evidence:** transcript padding + message gap both 15px is loose on a phone where the transcript is the star of the UPLINK self-contained view; `.composer-input` stays 16px font / 40px min-height (floors held). ### F8 · Inner readout / light rows — **board bodies app-wide** | Selector | Line | Current | → Mobile | |---|---|---|---| | `.op-light-row` | ~6519 | `margin-top: 8px; padding-top: 8px` | `margin-top: 6px; padding-top: 6px` | | `.zone-readout` and sibling first-inner-section margins | ~6983 | assorted 10px | `--d-section-gap` (8px) | **Est. Tier-1 total reclaim: ~15–20% of vertical scroll length** across the boards (roughly 1–1.5 phone-screens off OPERATOR alone), with **zero** behaviour, floor, or desktop change. --- ## 4. TIER-2 — DESIGN / TASTE (owner sign-off before applying) These change the *look* or *information model*, not just whitespace — they need a taste-check. - **D1 · Outer device-chrome padding (bezel).** The nested `body 6 + .container.machine 8 + .glass-frame 6` = 20px/side before the board's own 10px. Trimming to `4 / 6 / 5` reclaims ~11px/side (~22px width → more room per board). **DESIGN:** this is the Protocol-UI-7 device casing; tightening it changes how "framed" the terminal reads. Recommend a small trim, but it's a look call. - **D2 · The tall custom hardware controls in SETTINGS.** CAMPAIGN PROFILE (749px), RANDOMIZER INTERLOCK (638px), the SLOT-02 Sonic Processor board (914px), the phosphor tube rack, the OPERATIONAL TEMPO dial — these are deliberately large, mockup-approved hardware. They dominate SETTINGS' 5646px. Any shrink is a redesign of an owner-approved control → **DESIGN.** - **D3 · SPECIAL fader ladders / KARMA needle / VU meters.** `.fd-ladder` is 96px × 7, the karma `.kn-face` is 190×99px. These are signature instruments; shrinking them is a taste call, not tightening → **DESIGN.** - **D4 · Default-collapse more boards.** Most content boards default `open`, so a populated OPERATOR shows everything at once (7796px). Defaulting the lower-traffic boards (e.g. FIELD NOTES, SERVICE TALLY, CAMPAIGN CHRONICLE, KARMA on FNV) to collapsed would cut perceived length hard — but it changes the landing experience → **DESIGN / UX.** - **D5 · Hiding decorative chrome on mobile** (connector-strip `::after` pins, `.bay-slot-tag` corner tags). Would reclaim a little and de-clutter, but erodes the machine aesthetic → **DESIGN.** (Note: `.bay-part-no` is explicitly OFF-limits regardless — guardrail.) --- ## 5. What NOT to do (anti-recommendations) - ❌ Don't shrink fonts below their current 8–13px for density — legibility floor, and inputs must stay ≥16px. - ❌ Don't reduce tap targets below 28px (drawer buttons, purge ✕, delete ✕, faction ± keys, dial knobs). - ❌ Don't `display:none` `.bay-part-no` / `.real-label` (Protocol-25 searchable-label guardrail). - ❌ Don't put any density rule inside the `min-width:1000px … (hover:hover) and (pointer:fine)` desktop gate, or in an ungated `@media (min-width:1000px)` (Suite 129 forbids the latter). - ❌ Don't touch `@keyframes`, `:focus-visible`, `prefers-reduced-motion`, or any colour — out of scope. --- ## 6. Verification the build unit must run (Protocol 10) 1. `node tests/render-check.mjs` — 360px + 412px, assert **no horizontal overflow** and **no focus-zoom**. 2. Live re-measure (static server, populated seed) that: inputs still ≥16px, every trimmed tap target still ≥28px, and the six subsystems still report `scrollWidth === innerWidth` at 360 and 412. 3. Desktop diff = zero: confirm nothing changed at ≥1000px (all edits inside `@media (max-width:480px)`). 4. Full pre-commit gate (both runners at parity) + Protocol 1 cache bump (served `css/` file changes). --- ## Appendix — raw measurements (static server, populated FNV, @360px unless noted) ### Shared board chrome (base values, applied on mobile today — NOT tuned) | Element | Value | |---|---| | `details.bay-board` padding | `10px 10px 12px` | | `.panel.bay-board` margin-top (inter-board gap) | `12px` | | `.main-grid` column-gap | `12px` (single-column on mobile → inert) | | `.bay-part-no` | fs 9px, margin `2px 0 8px`, measured 10–30px tall | | `.bay-slot-tag` | fs 8.5px, padding `2px 7px` (absolute — no layout height) | | `.panel-substatus` | fs 10px (inside summary) | | board `summary h2` | fs 12px | | base body font (mobile) | 13px | ### Horizontal padding chain @360px (content width = 300px) `body 6+6` → `.container.machine 8+8` → `.glass-frame 6+6` → `.main-grid 0` → board `10+10 (+1 border)` = **~60px chrome / side-summed**. ### OPERATOR boards @360px (populated), height incl. shell VITAL TELEMETRY 416 · S.P.E.C.I.A.L. 350 · POSITION & MISSION CLOCK 509 · SKELETAL HARNESS 559 · SKILL MATRIX 615 · SKILL BOOKS 669 · SKILL MAGAZINES 665 · PERK LOADOUT 1548 (bounded scroll) · STATUS EFFECTS 412 · FACTION STANDING 800 · KARMA ALIGNMENT 314. ### Key interactive tiles (all ≥28px floor today) | Tile | Measured | Padding | Proposed | Post-trim | |---|---|---|---|---| | `.facon-chan` keycap | 40px | min-height 40 | min-height 34 | 34 ✓ | | `.stlamp-tile` | 70px | `8 10 9` | `6 9 7` | ~66 ✓ | | `.slot-row` | 67px | `7 10` mb7 | `5 8` mb5 | ~59 (inner ✕ = 28 ✓) | | `.vu-row` | 41px | `5 0` | `3 0` | ~37 (track 22 unchanged) | | `.drawer` (cargo filter) | 46px | `5 7` | leave (comfortable, ≥28) | — | | `.facon-meter-wrap` | pad `10 12 12` | — | `8 10 9` | — | ### UPLINK (self-contained mobile view) `#chatDisplay` padding 15 / gap 15 · `.scope-wrap` 102px (scope 66 + head/meta) · `#composer` padding `8 12 10` · `.composer-input` 40px min-height, 16px font (floor held). ### Overflow check 360px: all six subsystems `scrollWidth === 360`. 412px: all six `ok`. **No floor violations anywhere.**
STAMP · generated for RELEASE v2.8.5 commit 06e5180 (06e51801b38a) · archive input-tree hash c07fbfbdd2e1ddeb · 754 files · no wall-clock timestamp (regenerates identically when nothing changed).