|
| 1 | +# Mobile Experience Plan — Architecture Advisor |
| 2 | + |
| 3 | +**Status:** **Implemented** 2026-07-14 (written 2026-07-13, before implementation, per the maintainer's docs-first rule) |
| 4 | +**Owner:** Faqih Pratama Muhti · **Relates to:** ADR-009 (Aurora Slate), FR-SHELL-1/2/3/11, design-spec §6.1, [`prototype-v2/preview-modern.html`](prototype-v2/preview-modern.html) |
| 5 | + |
| 6 | +## 1. Goal |
| 7 | + |
| 8 | +Make the **core Advisor and Insights** (and the Home landing) feel like a **genuinely mobile app** on |
| 9 | +phones — not a desktop layout squeezed small — while keeping the exact decision-model flow and all |
| 10 | +logic unchanged. Concretely, on phones (≤640px): |
| 11 | + |
| 12 | +- A **fixed bottom tab bar** for primary navigation (thumb-zone), the mobile convention — replacing |
| 13 | + the top tab row that currently wraps. |
| 14 | +- A **sticky bottom primary action** on the Advisor (the "main button at the bottom" pattern) that |
| 15 | + drives the process flow (factors → priorities → plan → save). |
| 16 | +- **First-class, reachable controls** for the three product toggles — **theme (light/dark)**, |
| 17 | + **language (EN/ID)**, and **Guided/Expert** — surfaced in a mobile **settings sheet** (the header |
| 18 | + control cluster is cramped on phones). |
| 19 | +- Mobile-native spacing/typography/hit-targets and safe-area handling. |
| 20 | + |
| 21 | +Desktop (≥641px) is unchanged (top nav + header controls as today). |
| 22 | + |
| 23 | +## 2. Non-negotiables |
| 24 | + |
| 25 | +- **No change to the flow or the model.** Same four steps, same scoring, same content — only the |
| 26 | + *presentation/navigation* changes on phones. All model/content guards and the 102 unit + 14 e2e |
| 27 | + tests stay green (e2e updated for the new mobile chrome where needed). |
| 28 | +- **No new runtime dependency.** Token-based (`--color-*`, `--aa-*`), `aa-`/`lp-` class conventions, |
| 29 | + no `!important`, reuse the existing `<dialog>`-based sheet pattern if present or a small one. |
| 30 | +- **Accessibility:** bottom-nav items ≥44px, `aria-current` on the active tab, `env(safe-area-inset-*)`, |
| 31 | + visible focus, `prefers-reduced-motion` respected, WCAG AA in both themes (axe-gated). |
| 32 | +- **Budget:** stay within the initial-JS/CSS and total-JS budgets (mobile chrome is small CSS + a |
| 33 | + little JS). |
| 34 | + |
| 35 | +## 3. Components & changes (planned) |
| 36 | + |
| 37 | +| Area | Change | |
| 38 | +|---|---| |
| 39 | +| **`MobileTabBar`** (new) | Fixed bottom bar, phone-only (`aa-only-phone`), 3 tabs **Home · Advisor · Insights** (icon + label), `aria-current`, safe-area padding. Drives the same `mainView` state. The top tab row gets `aa-hide-phone`. | |
| 40 | +| **`MobileSettingsSheet`** (new) | A bottom sheet (slide-up) holding **Theme**, **Language**, **Guided/Expert** as large, labelled controls. Opened from a compact header button (gear) shown on phones. Closes on backdrop/Esc; focus-trapped. | |
| 41 | +| **Header** | On phones, collapse the control cluster (⌘K/?/save already hidden via `aa-hide-phone`); keep brand + theme quick-toggle + the settings (gear) button. Desktop unchanged. | |
| 42 | +| **`AdvisorMobileBar`** (new) | Phone-only sticky bottom action bar (above the tab bar): a **contextual primary button** that advances the flow — *See what matters* → *Get your plan* → *Save & share* — by scrolling to that section (the Advisor stays one page; the bar is the mobile "next"). Uses the existing StepTracker sections as anchors. | |
| 43 | +| **Advisor layout** | Content gets bottom padding = `tabbar + actionbar + safe-area` so nothing hides behind the fixed chrome. Cards/inputs already responsive; verify one-column, comfortable tap targets. | |
| 44 | +| **Insights layout** | Already card-based/stacks; ensure bottom padding for the tab bar and that section/detail navigation is thumb-reachable. | |
| 45 | +| **Landing** | Already responsive (radar-first, reflowing bento); add bottom padding for the tab bar; CTAs unaffected. | |
| 46 | +| **CSS** | New `aa-only-phone` / `aa-hide-phone` usage; `.aa-tabbar`, `.aa-mobile-actionbar`, `.aa-sheet` under the ≤640 breakpoint; all token-based. | |
| 47 | + |
| 48 | +## 4. Interaction / flow (unchanged semantics) |
| 49 | + |
| 50 | +- **Navigation:** bottom tab bar switches `mainView` exactly like the top tabs (Home/Advisor/Insights); |
| 51 | + a shared `#s=…` link still opens the Advisor. |
| 52 | +- **Advisor bottom action (contextual):** |
| 53 | + 1. On the Factors area → primary button **"See what matters"** scrolls to Priorities. |
| 54 | + 2. Past Priorities → **"Get your plan"** scrolls to the Recommendation. |
| 55 | + 3. At the plan → **"Save & share"** opens the export/share. |
| 56 | + The determination is by scroll position / which section is in view — no state-machine change; the |
| 57 | + single-page flow is preserved (this is navigation sugar, not a wizard rewrite). |
| 58 | +- **Toggles:** the settings sheet mutates the same `theme` / `lang` / `mode` state used everywhere; |
| 59 | + changes apply instantly and persist (localStorage), identical to the header controls. |
| 60 | + |
| 61 | +## 5. Breakpoint & tokens |
| 62 | + |
| 63 | +- Phone tier: **≤640px** (canonical, design-spec §6.1). Bottom nav + action bar + settings sheet are |
| 64 | + phone-only; ≥641px keeps the current top-nav desktop layout. |
| 65 | +- Safe area: `padding-bottom: env(safe-area-inset-bottom)` on the fixed bars; the viewport already |
| 66 | + uses `viewport-fit=cover`? (verify/set in index.html). |
| 67 | + |
| 68 | +## 6. Testing (planned) |
| 69 | + |
| 70 | +- **Unit:** a small render test for `MobileSettingsSheet` (toggles call the right setters) and that |
| 71 | + the bottom tab bar switches views. |
| 72 | +- **e2e (`responsive.spec.ts`):** at the 360px viewport, assert the bottom tab bar is visible and |
| 73 | + switches Home/Advisor/Insights; the settings sheet opens and toggles theme/lang/mode; no horizontal |
| 74 | + scroll; the Advisor bottom action advances the flow. Desktop specs unchanged. |
| 75 | +- **a11y:** axe on the mobile chrome (both themes); bottom-nav roles/`aria-current`; focus trap in the |
| 76 | + sheet. |
| 77 | + |
| 78 | +## 7. Rollout |
| 79 | + |
| 80 | +Incremental, each step gate-green: (1) bottom tab bar + hide top nav on phones; (2) settings sheet + |
| 81 | +header gear; (3) Advisor sticky bottom action; (4) padding/polish for Insights & Landing; (5) e2e + |
| 82 | +a11y + screenshots (both themes), docs, PR. Documented in CHANGELOG + design-spec on completion; this |
| 83 | +plan is the pre-implementation record. |
| 84 | + |
| 85 | +## 8. Implementation notes (as built, 2026-07-14) |
| 86 | + |
| 87 | +- **`MobileChrome`** renders the bottom tab bar (**Home · Advisor · Insights · More**) and the |
| 88 | + settings sheet; the "More" tab opens the sheet with **Theme · Language · Reading mode** as |
| 89 | + segmented controls. `useTheme` was **lifted to `App`** and passed to both the header and |
| 90 | + `MobileChrome` (they share one theme state — `usePersistedState` does not sync across instances). |
| 91 | +- **`AdvisorMobileBar`** is the phone-only sticky primary action; an `IntersectionObserver` on |
| 92 | + `#adv-plan` flips it from **Get your plan** → **Save & share** as the recommendation reaches the |
| 93 | + viewport's middle band. Anchors `#adv-plan` / `#adv-save` added to the Advisor sections. |
| 94 | +- The desktop **top nav** and the header **theme/language/mode cluster** move to CSS classes |
| 95 | + (`.aa-topnav`, `.aa-desktop-controls`) that hide ≤640px — avoiding the inline-`display` footgun |
| 96 | + that beats `aa-hide-phone` (DECISIONS.md). Content clears the fixed bars via `.aa-page` / |
| 97 | + `.aa-page.has-actionbar` bottom padding + `env(safe-area-inset-bottom)`; `index.html` gained |
| 98 | + `viewport-fit=cover`. |
| 99 | +- **Tests:** e2e `responsive.spec.ts` updated to assert the bottom bar + settings sheet on phones; |
| 100 | + the two App-level a11y unit tests select the first nav match (top nav + bottom bar both render |
| 101 | + under `css:false`). Desktop specs unchanged. All gates green; budget total JS 193.2/200 kB. |
0 commit comments