v4.28 — Mobile Settings & Coaching Refresh
Below the
lgbreakpoint,/settingsbecomes a hub of priority tiles that push into sub-pages, and Coaching becomes a one-at-a-time accordion — replacing the icon-only tab strip that used to overflow-scroll on a 390px screen.
Shipped. All five phases merged into the integration branch
gsd/v4.28-mobile-settings-coaching, and that branch has been promoted tomastervia PR #2047 (merge14f30c9b9, OBJ-2089). Thedesign_system/css/tokens.cssconflict between this branch's Phase 1 work andmaster's independently-shipped OBJ-2044 accent-accessible token was resolved by Codi's reconciliation merge (671d2f008) ahead of promotion. Master containment was independently re-verified by content (not file count) — see the milestone audit.
Summary
/settings was a single 939-line views/Settings.vue with an 8-item, icon-only tab strip that overflow-scrolled on a 390px viewport — on mobile, users saw eight unlabelled glyphs and had to guess. Every panel was stacked into one route, so the mobile experience was a horizontally-scrolling icon rail over one long vertical scroll. Josh produced a Design Canvas package with two chosen directions — Settings 1b ("priority tiles") and Coaching 2a ("accordion") — and this milestone rebuilds the mobile experience from them.
/settings below lg now renders a hub: a profile hero card (avatar, name, rank + XP), a 2×2 grid of priority tiles (Coaching / Notifications / Privacy & data / Supporter), a quick-toggles card, and a quiet "everything else" list (App & appearance / Help & feedback / Account). Tapping a tile or row pushes a sub-page built on a shared row grammar (toggle / nav / action / field / segmented / grid / swatches / chips / persona / plan). Coaching — today's personalization tab — becomes a coach-identity card plus four accordions that open one at a time: How Sage works, Voice & focus, Personality type, Goal style. No new routes were added; every sub-page is a ?tab=<slug> state of the same /settings route, and the slug map is additive-only — every legacy ?tab= value (profile, security, privacy, personalization, supporter, feedback, tours, notifications) still resolves. Desktop (lg and up) is byte-for-byte untouched throughout, verified diffable every phase via a CSS-only hidden lg:block / lg:hidden dual-layout pattern rather than a JS media query or component fork.
Almost every control in the mocks was already wired to a real composable or component — this was a re-layout, not a rebuild. Two controls had no backing and ship visibly disabled with an honest affordance and a filed backlog ticket: the Settings search pill (OBJ-2009) and two of four streak metaphor options, Sprout/Mountain/Waves (OBJ-2037; Stones and supporter-gated Flame are real). Zero backend or GraphQL changes; no PostHog flag, per Josh's explicit direct-to-master call.
Goal
Rebuild the mobile Settings and Coaching surfaces from Josh's Design Canvas package.
/settingsbecomes a hub of priority tiles that push sub-pages; the icon-only tab strip is retired on mobile only. Coaching (today'spersonalizationtab) becomes a one-at-a-time accordion. Desktop (lgand up) is untouched, all existing?tab=deep links keep resolving, and no backend changes ship.
Scope — What Shipped
All five phases are complete and promoted to master (PR #2047) — the items below are live for mobile users.
- New mobile Settings hub (
views/settings/SettingsHub.vue) — profile hero, disabled search pill, 2×2 priority tile grid, quick toggles, quiet "everything else" list, sign-out footer. - New sub-page shell + row grammar (
views/settings/SettingsSubPage.vue,components/settings/rows/—SettingsGroup.vueplus 10 row primitives: toggle, nav, action, field, segmented, grid, swatches, chips, persona, plan) and an additive?tab=slug map (views/settings/slugMap.ts). - Four restyled sub-pages (Phase 2): Profile, Account, Privacy & data, Notifications — reusing existing wired components (
PrivacyControlsCard.vue,DataExportCard.vue,ReminderPreferences.vue,WeeklyDigestSettingsSection.vue) via the CSS-only dual-layout pattern. - Three restyled/relocated sub-pages (Phase 3): App & appearance (new destination for themes, dashboard mode, and the widget row — moved off Supporter per Josh's Gate 1 ruling), Supporter (now entitlements-only, links across to appearance), Help & feedback.
- New Coaching accordion (Phase 4):
CoachingAccordion.vue+CoachingAccordionSection.vueon?tab=coaching, aliasingpersonalization— coach identity card, glance chips, four one-at-a-time accordions built from existing components (CoachTonePad.vue,CoachFrequencyCardGrid.vue,CoachFocusList.vue,CoachPersonaSummary.vue/CoachPersonaDetail.vue,EnneagramResultCard.vue/EnneagramRadarChart.vue,GoalMotivationSnapshotCard.vue). - Pinned sub-page header, no push/pop slide (Phase 5): the header moved out of the
<Transition>subtree and crossfades in place (160ms,prefers-reduced-motion-aware); thetranslateXpush/pop Phase 1 shipped was retired entirely once the motion ruling landed. - a11y sweep (Phase 5): real accessible names on hub tiles/rows, accordion ARIA, focus order + focus return on hub↔sub-page navigation, 44px minimum targets,
inert="false"fixed in two files (ReminderPreferences.vue,WeeklyDigestSettingsSection.vue),dl/dt/ddnesting fixed, a real blocking a11y test (tests/unit/views/settings/a11y.spec.ts) replacing a Cypress spec that could never fail the build. - Two AA design tokens (
--accent-accessible,--gradient-brand-deep) added todesign_system/css/tokens.cssand consumed throughout — this is the same token whose independentmasterlanding (OBJ-2044) conflicted with this branch during promotion, resolved by Codi's reconciliation merge (671d2f008). - Docs:
docs/features/settings-mobile-ia.md(this milestone's IA + slug map reference), plus root and in-appCHANGELOG.mdentries.
Phases
| Phase | Name | Status | Plans | Highlights |
|---|---|---|---|---|
| 1 | Settings hub shell (1b) + sub-page navigation + ?tab= map | Shipped | 1 | Hub, sub-page shell, 10 row-grammar primitives, additive slug map; resolved the themes-location conflict; 2 new AA tokens |
| 2 | Sub-pages A — Profile, Account, Privacy & data, Notifications | Shipped | 1 | CSS-only dual-layout pattern established (desktop diff nil by construction); Privacy value pill; 1 axe violation caught + fixed |
| 3 | Sub-pages B — App & appearance, Supporter, Help & feedback | Shipped | 1 | Physically executed the themes/dashboard-mode/widget-row move to App & appearance; native Supporter gate traced to zero web pricing; 2-real/3-disabled streak metaphor grid |
| 4 | Coaching sub-page — accordion + four panel bodies | Shipped | 1 | Two new components, chrome prop to suppress duplicate card shells inside the accordion; 190/190 tests green |
| 5 | Polish & close — a11y, tests, docs, route-drift, disabled affordances | Shipped | 1 | Pinned-header motion ruling (retired push/pop transition); a11y sweep; blocking a11y test replacing a non-enforcing gate; docs |
Key Decisions
- Hub + sub-pages over a redesigned tab strip —
/settingsstays one route; every sub-page is a?tab=<slug>state, additive-only over the legacy values. Avoids a second navigation model and keeps every existing deep link (/feedback,/checkout/cancel,/settings/blocked,/coach,/enneagram-assessment,/coaching/type) resolving untouched. - CSS-only dual layout, not a JS media query or component fork — each shared component carries both
hidden lg:block(today's desktop markup, verbatim) andlg:hidden(new row grammar) in one template. This makes the desktop diff nil by construction, not by discipline — Roy byte-verified the Profile panel identical (112/112 lines) after Phase 2, and it held through Phase 3, the one phase that moves controls between panels rather than restyling them in place. - Themes, dashboard mode, and the widget row move to App & appearance; Supporter keeps entitlements only — ruled by Josh at Phase 1's Gate 1 to resolve a genuine conflict in the design package (
SettingsDetail.dc.htmlput themes under App & appearance; today they lived under Supporter). Phase 3 performed the physical move; Phase 4 built no theme picker as a result. - The pinned sub-page header retires the push/pop transition rather than amending it — deferred through Phases 1–3 as a design call, not a code difficulty. Desi's Phase 5 ruling: pin the header (160ms crossfade, no slide); with a static header, a sliding body reads as two competing motions, so the
translateXtransition Phase 1 shipped was deleted, not patched. - Two controls with no backing ship visibly disabled, never hidden or faked — the Settings search pill (nothing exists behind it; OBJ-2009) and two of four streak metaphor options (Sprout/Mountain/Waves —
useStreakMetaphor.tsonly supportsstones/flame; OBJ-2037). Both per Josh's explicit ruling on OBJ-1993. - A non-enforcing a11y gate was replaced with a real one — the original a11y acceptance criterion rested on a Cypress spec with
skipFailures: truein a non-required CI job, so it could not fail the build under any circumstances (the same family as this repo's documented "continue-on-erroris an absent gate" gotcha). Roy's Phase 5 Round 1 review caught it; Codi replaced it with a Vitest spec inside the requireda11yjob. - The milestone ships on an integration branch and promotes to
masteras a separate, explicitly-tracked step — per this repo's stranded-milestone-branch gotcha, code-complete-on-a-branch and shipped-to-users are different claims. Orion filed OBJ-2089 rather than letting the milestone read done everywhere whilemastercarried none of it.
Requirements Coverage
This milestone has no standalone REQUIREMENTS.md — it originated from Josh's Design Canvas package rather than a requirements-driven roadmap, the same shape as v1.0/v1.3/v1.4's pre-audit-discipline gaps noted on the Project History index. A hand-authored milestone audit (passed) exists, cross-checked against the milestone ROADMAP's own per-phase Delivered / Deviations from plan / Known gaps carried forward sections (linked below) and a live source-code verification pass.
Outcomes
Mobile users get a Settings home that says what each destination is instead of eight unlabelled icons, and a Coaching surface that opens one topic at a time instead of one long scroll. Every existing deep link, redirect, and ?tab= value keeps working. Desktop users see no change at all. Two honestly-disabled affordances (search, three streak metaphors) each carry a backlog ticket instead of a silent gap.
Tech Debt
- (carried past milestone close)
OBJ-2076— the export-state live authenticated walk (does in-flightDataExportCardstate survive a hub↔sub-page navigation) is machine-proven bySettingsPanelPersistence.spec.tsbut has never been walked by a human with real backend access; five agents across three phases hit a missing-credentials wall before Phase 5 hit a more specific one (secret_key_basenot provisioned to agent checkouts). - (phase 5, deferred)
OBJ-2071—EnneagramResultCard's dominant-type pill measures 2.62:1 against a solidbg-accentbackdrop; named owner Desi, explicitly re-deferred, not part of this milestone's scope. - (phase 1, backlog)
OBJ-2009— the Settings search pill has no backing implementation; ships visibly disabled. - (phase 3, backlog)
OBJ-2037— the streak metaphor picker ships two of four options; Sprout/Mountain/Waves are visibly disabled pendinguseStreakMetaphor.tssupport. - (phase 4, unfiled)
GoalMotivationSnapshotCard.stories.tsrenders a hand-authoredStubCardmock rather than the real component — pre-existing drift, found while verifying an unrelated a11y fix, still needs a ticket filed. - (milestone close, done)
OBJ-2089— integration branch promoted tomastervia PR #2047 (merge14f30c9b9). Thedesign_system/css/tokens.cssconflict between this branch's Phase 1 tokens andmaster's independently-shipped OBJ-2044 accent-accessible work was resolved by Codi's reconciliation merge (671d2f008) ahead of promotion.
Related Artifacts
- Roadmap: v4.28-mobile-settings-coaching-ROADMAP.md
- Feature doc:
docs/features/settings-mobile-ia.md - Requirements / Milestone Audit: no standalone requirements doc — Milestone audit (
passed) — see Requirements Coverage above. - PRD: none — sourced from a Design Canvas package (
Settings-1b.dc.html,SettingsDetail.dc.html,Coaching-2a.dc.html,CoachingPanel.dc.html), not a written PRD. - Promotion: PR #2047 (OBJ-2089, Riley)
- Merge PRs (into the integration branch): #1958 · #1972 · #1992 · #2006 · #2027
Related Commits
e4c8ab97f— Phase 1: Settings hub shell, sub-page shell, row-grammar primitives (PR #1958)ab64b14df— Phase 2: Sub-pages A row grammar (PR #1972)425d78b5d— Phase 3: Sub-pages B — App & appearance, Supporter, Help & feedback (PR #1992)a244f0222— Phase 4: Coaching accordion mobile branch (PR #2006)17cf2c40e— Phase 5: pin sub-page header, retiretranslateXpush/pop transition (PR #2027)
Last updated: 2026-08-03