Coach Builder — Onboarding Slides 6–9
Overview
The Coach Builder is not a standalone flow — it's four consecutive slides (6 through 9) inside the single 13-slide onboarding wizard. They configure the user's Coach persona, tone, focus, and check-in rhythm before the wizard reaches the goal-motivation and review slides.
For the full slide registry, the wizard's mutation contract, and the OnboardingAnswers interface, see Onboarding Flow (technical) — that page is the source of truth for the wizard as a whole. This page covers the UX and interaction detail specific to the four Coach-configuration slides.
Related docs:
- Onboarding Flow (technical) — full 13-slide registry, mutations, KPIs
- Onboarding Timeline — where the Coach builder fits in the Day 1 arc
- Meet Coach — the AI Coach feature these slides configure
- Gamification Engine — how Coach contributions feed into streaks and badges
The 4 Slides
Slide 6 — Persona (CoachPersonaSlide.vue)
The user selects one of six Coach personas (ionic_frontend/src/constants/coachPersonas.ts) — the persona shapes how the app communicates everywhere, not just onboarding.
Personas:
| ID | Name | Traits | Tagline |
|---|---|---|---|
captain | The Captain | Structured · decisive | "Clear eyes, steady course." |
spark | The Spark | Warm · contagious | "Energy is contagious — starting now." |
mirror | The Mirror | Reflective · non-directive | "The answer is usually already in your head." |
rival | The Rival | Direct · competitive | "You said you wanted this." |
sage | The Sage | Steady · long-view | "Progress is a practice, not a performance." |
analyst | The Analyst | Data-led · precise | "The data tells a story." |
Interaction:
- Grid view: 1 col mobile → 2 col
sm→ 3 collg,role="radiogroup"/role="radio"per card - Tapping a card opens a full-bleed detail view (
CoachPersonaDetail.vue) with persona icon, tagline, bio, and a "Choose [Name]" CTA — not an inline modal - Detail view back control: an arrow icon + "All Coaches" text button, which returns focus to itself when the detail view opens (
aria-live="polite"announces the view change) - Choosing a persona fires
UPDATE_COACHING_PREFERENCES_MUTATIONimmediately (before the wizard finishes) and auto-advances to the next slide on success - On mutation failure, the CTA switches to "Try again" with a retry icon and an inline
role="alert"error message; the wizard does not advance until the save succeeds - Reassurance copy in the detail view: "You can change your Coach anytime in Settings."
- No desktop split-panel/preview column on this slide — the detail view itself occupies the full width
State stored: answers.coachPersona (PersonaId)
Slide 7 — Tone (CoachToneSlide.vue)
The user sets the Coach's communication style along two independent axes via a 5×5 drag pad (CoachTonePad.vue): Tone (horizontal, Gentle ↔ Tough) and Depth (vertical, Brief ↔ Detailed). The Tone axis's right-hand label read "Demanding" until v4.47 wave 1 (OBJ-3018) — it now matches the resolved label and the guide, both of which already said "Tough."
Interaction:
role="application"pad containing arole="slider"drag dot; supports pointer drag and arrow-key nudging (Home/End jump Tone to its extremes)- Default position: Tone 3, Depth 3 ("Balanced · Medium")
- A "Reset to default" button below the pad returns both axes to 3/3
- Resolved label + explanation text (e.g. "Balanced · Medium") update live below the pad as the dot moves; a debounced
aria-liveregion announces changes for screen readers - Desktop (
lg:and up): two-column layout — pad on the left, stickyCoachPreview.vueon the right showing a sample message in the selected persona's voice, tinted with the persona's accent color when one is already chosen - Mobile: pad only, no right column
State stored: answers.coachTone (1–5), answers.coachDepth (1–5)
Slide 8 — Focus (CoachFocusSlide.vue)
The user picks what the Coach should watch — the signals it uses for check-ins and feedback.
Focus areas (ionic_frontend/src/constants/coachFocusAreas.ts):
| ID | Label | Description |
|---|---|---|
streaks | Streaks | Daily consistency, habit formation, showing up |
milestones | Milestones | Progress toward your stated goals |
reflection | Reflection | Mood, energy, and the story behind the data |
accountability | Accountability | What you said you'd do, and whether you did |
energy | Energy | When you show up best, and how to protect it |
Interaction:
- Single grid, no split panel: 1 col mobile → 2 col
sm→ 5 collg,role="group"over five toggle buttons - Multi-select: pick 1–2 areas. Selecting a first area sets it as primary; selecting a second sets it as secondary; selecting a third replaces the secondary (primary stays fixed). Deselecting the primary promotes the existing secondary into its place.
- Selected cards are tinted with the chosen persona's accent color, when a persona has already been picked on Slide 6
- Copy under the headline: "Two is the sweet spot. More, and the signal gets noisy."
State stored: answers.coachFocusPrimary, answers.coachFocusSecondary (both FocusAreaId)
Slide 9 — Rhythm (CoachRhythmSlide.vue)
The user chooses how often the Coach reaches out — the check-in cadence.
Frequency options (ionic_frontend/src/constants/coachFrequencies.ts):
| ID | Label | Description |
|---|---|---|
daily | Every day | Daily check-in prompts and insight cards |
active | When I'm active | Only on days you sign in |
when_needed | When I need it | On streak breaks, overdue milestones, or inactivity |
weekly | Weekly | A single Sunday planning + Friday review |
Interaction:
CoachFrequencyCardGrid.vue: 1 col mobile → 2 colsmradio-card grid (role="radiogroup"), one persona-tinted icon tile per option, arrow-key navigation between cards- Default:
dailypre-selected - The component itself renders a built-in example-notification preview below the grid (not a separate desktop-only column) — no split-panel layout on this slide
State stored: answers.coachFrequency (FrequencyId)
Design Decisions
Tone/Depth as Orthogonal Axes
Tone (Gentle ↔ Tough) and Depth (Brief ↔ Detailed) are independent. A tough coach can still be brief. The 5×5 grid lets users find their exact voice match rather than forcing a stereotype.
Focus Min 1, Max 2
Two areas balances personalization with signal clarity — fewer than 1 leaves the Coach with no focus (generic messages), more than 2 dilutes the signal back toward generic.
Grid↔Detail Transition Removed (PR #922)
The 200ms opacity fade between the persona grid and the detail view (<Transition mode="out-in">) was removed in PR #922. The toggle is now a plain v-if — no animation runs for any user, reduced-motion or otherwise.
Why: Under reducedMotion:reduce (enforced by the Playwright smoke suite via playwright.config.ts), Vue's out-in sequencing defers the entering vnode to the afterLeave callback. When stale ion-router-outlet pages carry will-change from a prior slide-fade-enter-active class, afterLeave never fires — showDetail flips but the detail block never renders, permanently hiding the "Choose" button for all test browsers. Removing the wrapper fixes the test deterministically. See PR #922 for the full investigation history.
Data & State Management
There is no UpdateOnboardingAnswers mutation and no User.onboarding_answers column — those do not exist in the codebase. Coach answers live client-side, keyed into the shared reactive answers map held by useOnboardingFlow.ts (an untyped reactive<Record<string, unknown>>), typed against the OnboardingAnswers interface declared in placeholderSlides.ts — see Onboarding Flow § OnboardingAnswers Interface for the full shape.
Persona selection fires UPDATE_COACHING_PREFERENCES_MUTATION immediately so the choice is written without waiting for the wizard to finish. The remaining Coach answers (tone, depth, focus, frequency) are persisted server-side at the end of the wizard, alongside the rest of the onboarding answers:
CompleteOnboardingAndCreateGoal(rails_api/app/interactions/onboarding/complete_onboarding_and_create_goal.rb) — atomic path when the user named a goal. Stores onboarding state and creates the goal in one transaction.StoreOnboardingState(rails_api/app/interactions/user_identity/store_onboarding_state.rb) — used when no goal name was given; persists the onboarding blob only, and the Done slide routes to/goals/createinstead.
Both write onboarding data as a JSON blob into UserDetail.data['onboarding'] (via deep_merge, preserving sibling keys) — there is no dedicated onboarding_answers column on User. See Onboarding Flow § Backend Mutation for the full mutation signature and transaction details.
These values configure:
- Coach initialization (
Coachcomponent receives persona, tone, depth, focus, frequency) - Check-in prompts (personalized to persona + focus)
- Notification delivery (scheduled via
coachFrequency) - Progress messaging (Coach reflects selected tone/depth in all feedback)
Accessibility
All four slides follow WCAG AA standards:
- Keyboard navigation: Tab through all interactive elements; Enter/Space to select; arrow keys drive the tone pad and the rhythm card grid
- Screen readers:
role="radiogroup"/role="radio"on the persona and rhythm selectors,role="group"on the focus grid,role="application"+role="slider"on the tone pad with a debouncedaria-livevalue announcement - Color contrast: All text pairs meet ≥4.5:1 ratio (AA standard)
- Touch targets: All interactive elements ≥44px tall
- Motion: The persona grid↔detail toggle was de-animated in PR #922 (see Design Decisions) — no animated transition runs for any user on that surface. Other animated surfaces (slide entry,
CoachPreviewupdates) respectprefers-reduced-motionindependently. - Focus management: Logical tab order; the persona detail view's back button receives focus when the detail view opens
Validation & Error Handling
| Slide | Validation Rule | Error Message | CTA Behavior |
|---|---|---|---|
| Persona | Persona required; save must succeed | (mutation failure) "Couldn't save your Coach. Check your connection and try again." | Advance blocked until UPDATE_COACHING_PREFERENCES_MUTATION resolves |
| Tone | Always valid (has default) | (none) | Always enabled |
| Focus | At least one area selected | "Pick at least one focus area." | Disabled if none selected |
| Rhythm | Always valid (has default) | (none) | Always enabled |
Related Components
SlideWizardLayout.vue— wizard chrome (progress bar, back/continue buttons, footer) shared across all 13 slidesCoachPersonaDetail.vue— full-bleed persona bio view (icon, tagline, bio, Choose CTA)CoachPersonaIcon.vue— bespoke per-persona iconCoachPreview.vue— right-column sample-message card used on the Tone slide onlyCoachTonePad.vue— 5×5 interactive drag padCoachFrequencyCardGrid.vue— rhythm selector grid with the built-in example-notification preview
Migration from PersonaSlide
The legacy PersonaSlide.vue (a 4-option Coach/Mentor/Ally/Captain picker) is not in the slide registry and is not imported anywhere in the active flow — see Onboarding Flow § Key Files for its orphaned status alongside GoalTypeSlide.vue.
Rollout & Monitoring
There is no variant_c or any other experiment gate — the Coach Builder slides run for 100% of new signups as part of the single onboarding registry. See Onboarding Flow § A/B Variant History for the retirement log of the flags that once gated earlier phases of the onboarding rewrite.
Ongoing monitoring: Sentry errors (persona save failures logged as breadcrumbs), Coach mutation success rate (UPDATE_COACHING_PREFERENCES_MUTATION, CompleteOnboardingAndCreateGoal), and user feedback in Support.
Testing & QA
See Onboarding Flow § Test Coverage for the full suite list (Vitest unit/integration/a11y, RSpec interaction + GraphQL). Coach-specific flows worth exercising:
- Persona selection: grid → detail switch (instant, no animation); detail → grid return via "All Coaches"; "Choose [Name]" saves and advances; retry path on mutation failure
- Tone dragging: pad responds to drag and arrow keys; labels update live; Reset returns to 3/3
- Focus selection: can't proceed without ≥1 area; selecting a 3rd area replaces the secondary (not the primary)
- Rhythm selection: default
dailypre-selected; example notification renders - Reduced motion / dark mode / keyboard-only across all four slides
See Also
- Onboarding Flow (technical) — full 13-slide registry, mutations, KPIs
- Onboarding Timeline — full Day 1 arc
- Meet Coach — Coach feature overview
- Goals — how Coach contributes to goal-setting
- Gamification Engine — Coach role in streaks, XP, badges
Last updated: 2026-07-08