Skip to content

Commit a20dc5d

Browse files
docs(06): create carousel navigation phase plan
Phase 06: Carousel Navigation - 2 plans in 2 waves - Wave 1: Install embla-carousel-react, create MobileCarousel component (06-01) - Wave 2: Dashboard integration + human verification (06-02) - Requirements covered: NAV-01, NAV-02, NAV-03 - Ready for execution Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 7b24e45 commit a20dc5d

3 files changed

Lines changed: 298 additions & 5 deletions

File tree

.planning/ROADMAP.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ Plans:
119119
**Goal**: User can swipe between heatmap and muscle list in Instagram-style carousel
120120
**Depends on**: Phase 3, Phase 5
121121
**Requirements**: NAV-01, NAV-02, NAV-03
122-
**Plans**: TBD
122+
**Plans**: 2 plans
123123

124124
**Success Criteria** (what must be TRUE):
125125

@@ -130,8 +130,8 @@ Plans:
130130

131131
Plans:
132132

133-
- [ ] 06-01: Implement carousel container with swipe detection
134-
- [ ] 06-02: Add slide indicators and default view logic
133+
- [ ] 06-01-PLAN.md - Install embla-carousel-react, create carousel container with swipe and dot indicators
134+
- [ ] 06-02-PLAN.md - Integrate carousel into Dashboard and human-verify UX
135135

136136
### Phase 7: Detail Pop-up
137137

@@ -203,12 +203,12 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
203203
| 3. Heatmap Core | 2/2 | Complete | 2026-01-19 |
204204
| 4. Front/Back Toggle | 1/1 | Complete | 2026-01-19 |
205205
| 5. Muscle List | 2/2 | Complete | 2026-01-22 |
206-
| 6. Carousel Navigation | 0/2 | Not started | - |
206+
| 6. Carousel Navigation | 0/2 | Planned | - |
207207
| 7. Detail Pop-up | 0/2 | Not started | - |
208208
| 8. Tap Interactions | 0/3 | Not started | - |
209209
| 9. Custom Grouping | 0/3 | Not started | - |
210210

211211
---
212212

213213
_Roadmap created: 2026-01-18_
214-
_Last updated: 2026-01-22 (Phase 5 complete)_
214+
_Last updated: 2026-01-22 (Phase 6 planned)_
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
---
2+
phase: 06-carousel-navigation
3+
plan: 01
4+
type: execute
5+
wave: 1
6+
depends_on: []
7+
files_modified:
8+
- package.json
9+
- package-lock.json
10+
- src/ui/components/mobile/MobileCarousel.tsx
11+
autonomous: true
12+
13+
must_haves:
14+
truths:
15+
- "User can swipe horizontally between heatmap and muscle list (NAV-01)"
16+
- "Dot indicators visually track current slide position (NAV-02)"
17+
- "Swipe gesture feels smooth with momentum and snap behavior"
18+
artifacts:
19+
- path: "src/ui/components/mobile/MobileCarousel.tsx"
20+
provides: "Swipeable carousel container with dot indicators"
21+
exports: ["MobileCarousel"]
22+
min_lines: 60
23+
key_links:
24+
- from: "src/ui/components/mobile/MobileCarousel.tsx"
25+
to: "embla-carousel-react"
26+
via: "useEmblaCarousel hook"
27+
pattern: "useEmblaCarousel"
28+
- from: "MobileCarousel.tsx"
29+
to: "MobileHeatmap.tsx"
30+
via: "component import"
31+
pattern: "import.*MobileHeatmap"
32+
- from: "MobileCarousel.tsx"
33+
to: "MobileMuscleList.tsx"
34+
via: "component import"
35+
pattern: "import.*MobileMuscleList"
36+
---
37+
38+
<objective>
39+
Create a swipeable carousel container that holds MobileHeatmap and MobileMuscleList as two slides with dot indicators.
40+
41+
Purpose: Enable Instagram-style horizontal swipe navigation between the two mobile views (NAV-01, NAV-02)
42+
Output: MobileCarousel component using embla-carousel-react with physics-based swipe and dot navigation
43+
</objective>
44+
45+
<execution_context>
46+
@./.claude/get-shit-done/workflows/execute-plan.md
47+
@./.claude/get-shit-done/templates/summary.md
48+
</execution_context>
49+
50+
<context>
51+
@.planning/PROJECT.md
52+
@.planning/ROADMAP.md
53+
@.planning/STATE.md
54+
@.planning/phases/06-carousel-navigation/06-RESEARCH.md
55+
56+
@src/ui/components/mobile/MobileHeatmap.tsx
57+
@src/ui/components/mobile/MobileMuscleList.tsx
58+
</context>
59+
60+
<tasks>
61+
62+
<task type="auto">
63+
<name>Task 1: Install embla-carousel-react and create MobileCarousel component</name>
64+
<files>
65+
package.json
66+
package-lock.json
67+
src/ui/components/mobile/MobileCarousel.tsx
68+
</files>
69+
<action>
70+
1. Install embla-carousel-react:
71+
```bash
72+
npm install embla-carousel-react
73+
```
74+
75+
2. Create `src/ui/components/mobile/MobileCarousel.tsx` with:
76+
- Props: `{ profileId: string | null; daysBack?: number }`
77+
- Use `useEmblaCarousel` with options:
78+
- `loop: false` (critical: loop breaks with only 2 slides)
79+
- `dragFree: false` (snap to slides)
80+
- `startIndex: 0` (NAV-03: default to heatmap)
81+
- Track selected slide index with `useState` + emblaApi `select` event
82+
- Structure:
83+
```tsx
84+
<div>
85+
{/* Carousel viewport */}
86+
<div className="overflow-hidden" ref={emblaRef}>
87+
<div className="flex touch-pan-y">
88+
{/* Slide 1: Heatmap */}
89+
<div className="flex-[0_0_100%] min-w-0">
90+
<MobileHeatmap profileId={profileId} daysBack={daysBack} />
91+
</div>
92+
{/* Slide 2: Muscle List */}
93+
<div className="flex-[0_0_100%] min-w-0">
94+
<MobileMuscleList profileId={profileId} daysBack={daysBack} />
95+
</div>
96+
</div>
97+
</div>
98+
99+
{/* Dot indicators */}
100+
<div className="flex justify-center gap-2 py-4" role="tablist">
101+
{[0, 1].map((index) => (
102+
<button
103+
key={index}
104+
onClick={() => emblaApi?.scrollTo(index)}
105+
className={/* active: w-4 bg-amber-500, inactive: w-2 bg-primary-600 */}
106+
role="tab"
107+
aria-selected={index === selectedIndex}
108+
aria-label={index === 0 ? 'Body heatmap view' : 'Muscle list view'}
109+
/>
110+
))}
111+
</div>
112+
</div>
113+
```
114+
115+
3. Critical implementation details (from RESEARCH.md):
116+
- Use `touch-pan-y` class on flex container (allows vertical scroll, prevents horizontal browser scroll)
117+
- Use `min-w-0` on slides (prevents flex overflow)
118+
- Subscribe to `emblaApi.on('select', callback)` in useEffect with cleanup
119+
- Update selectedIndex from `emblaApi.selectedScrollSnap()` in callback
120+
- Dot button styling: active dot is `w-4 bg-amber-500`, inactive is `w-2 bg-primary-600 active:bg-primary-500`
121+
- Dot transition: `transition-all duration-200 h-2 rounded-full`
122+
123+
4. Export the component as named export.
124+
</action>
125+
<verify>
126+
- `npm run build` succeeds (no TypeScript errors)
127+
- `npm run lint` passes
128+
- embla-carousel-react appears in package.json dependencies
129+
</verify>
130+
<done>
131+
- MobileCarousel.tsx exists with useEmblaCarousel setup
132+
- Component renders MobileHeatmap and MobileMuscleList as slides
133+
- Dot indicators track selected slide via emblaApi select event
134+
- Swipe gesture has smooth physics (loop: false, dragFree: false)
135+
</done>
136+
</task>
137+
138+
</tasks>
139+
140+
<verification>
141+
```bash
142+
# Build passes
143+
npm run build
144+
145+
# Lint passes
146+
npm run lint
147+
148+
# Component exists with correct structure
149+
grep -l "useEmblaCarousel" src/ui/components/mobile/MobileCarousel.tsx
150+
151+
# Dependency installed
152+
grep "embla-carousel-react" package.json
153+
```
154+
</verification>
155+
156+
<success_criteria>
157+
- embla-carousel-react is installed in package.json
158+
- MobileCarousel component created with TypeScript types
159+
- Component uses useEmblaCarousel with correct options (loop: false)
160+
- Dot indicators respond to slide changes via select event
161+
- Build and lint pass
162+
</success_criteria>
163+
164+
<output>
165+
After completion, create `.planning/phases/06-carousel-navigation/06-01-SUMMARY.md`
166+
</output>
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
---
2+
phase: 06-carousel-navigation
3+
plan: 02
4+
type: execute
5+
wave: 2
6+
depends_on: ["06-01"]
7+
files_modified:
8+
- src/ui/pages/Dashboard.tsx
9+
autonomous: false
10+
11+
must_haves:
12+
truths:
13+
- "Opening mobile view defaults to heatmap slide (NAV-03)"
14+
- "Swiping left shows muscle list, swiping right shows heatmap"
15+
- "Dot indicators correctly show which slide is active"
16+
- "Carousel replaces standalone MobileHeatmap in Dashboard"
17+
artifacts:
18+
- path: "src/ui/pages/Dashboard.tsx"
19+
provides: "Dashboard with MobileCarousel integration"
20+
contains: "MobileCarousel"
21+
key_links:
22+
- from: "src/ui/pages/Dashboard.tsx"
23+
to: "src/ui/components/mobile/MobileCarousel.tsx"
24+
via: "component import and render"
25+
pattern: "import.*MobileCarousel"
26+
---
27+
28+
<objective>
29+
Integrate MobileCarousel into Dashboard, replacing standalone MobileHeatmap for mobile users.
30+
31+
Purpose: Connect the carousel to the app and verify default-to-heatmap behavior (NAV-03)
32+
Output: Dashboard renders MobileCarousel on mobile, user can swipe between views
33+
</objective>
34+
35+
<execution_context>
36+
@./.claude/get-shit-done/workflows/execute-plan.md
37+
@./.claude/get-shit-done/templates/summary.md
38+
</execution_context>
39+
40+
<context>
41+
@.planning/PROJECT.md
42+
@.planning/ROADMAP.md
43+
@.planning/STATE.md
44+
@.planning/phases/06-carousel-navigation/06-01-SUMMARY.md
45+
46+
@src/ui/pages/Dashboard.tsx
47+
@src/ui/components/mobile/MobileCarousel.tsx
48+
</context>
49+
50+
<tasks>
51+
52+
<task type="auto">
53+
<name>Task 1: Replace MobileHeatmap with MobileCarousel in Dashboard</name>
54+
<files>src/ui/pages/Dashboard.tsx</files>
55+
<action>
56+
1. Update imports in Dashboard.tsx:
57+
- Remove: `import { MobileHeatmap } from '@ui/components/mobile/MobileHeatmap';`
58+
- Add: `import { MobileCarousel } from '@ui/components/mobile/MobileCarousel';`
59+
60+
2. Replace the mobile view render (around line 85-86):
61+
- Before: `<MobileHeatmap profileId={currentProfile.id} />`
62+
- After: `<MobileCarousel profileId={currentProfile.id} />`
63+
64+
3. No other changes needed - MobileCarousel receives same props as MobileHeatmap.
65+
</action>
66+
<verify>
67+
- `npm run build` succeeds
68+
- `npm run lint` passes
69+
- Dashboard.tsx imports MobileCarousel
70+
</verify>
71+
<done>
72+
- Dashboard renders MobileCarousel for mobile users
73+
- MobileHeatmap import removed from Dashboard
74+
</done>
75+
</task>
76+
77+
<task type="checkpoint:human-verify" gate="blocking">
78+
<what-built>
79+
Swipeable carousel navigation between heatmap and muscle list on mobile devices.
80+
</what-built>
81+
<how-to-verify>
82+
1. Start dev server: `npm run dev`
83+
2. Open http://localhost:3000 in Chrome DevTools mobile emulator (iPhone 12 or similar)
84+
3. Navigate to Dashboard (you need a profile with workout data)
85+
4. Verify NAV-03: Heatmap view is shown by default (body diagram visible)
86+
5. Verify NAV-01: Swipe left - muscle list slides in smoothly
87+
6. Verify NAV-01: Swipe right - heatmap slides back
88+
7. Verify NAV-02: Dot indicators at bottom show correct active state
89+
8. Tap the right dot - should scroll to muscle list
90+
9. Tap the left dot - should scroll to heatmap
91+
10. Verify swipe feels smooth with momentum (not jerky or abrupt)
92+
11. Verify muscle list groups expand/collapse normally within carousel
93+
12. Verify front/back toggle on heatmap still works within carousel
94+
</how-to-verify>
95+
<resume-signal>Type "approved" if all checks pass, or describe any issues</resume-signal>
96+
</task>
97+
98+
</tasks>
99+
100+
<verification>
101+
```bash
102+
# Build passes
103+
npm run build
104+
105+
# Lint passes
106+
npm run lint
107+
108+
# Dashboard imports carousel
109+
grep "MobileCarousel" src/ui/pages/Dashboard.tsx
110+
111+
# MobileHeatmap no longer directly imported in Dashboard
112+
! grep "import.*MobileHeatmap" src/ui/pages/Dashboard.tsx
113+
```
114+
</verification>
115+
116+
<success_criteria>
117+
- Dashboard.tsx renders MobileCarousel instead of MobileHeatmap for mobile
118+
- Opening dashboard on mobile shows heatmap first (NAV-03)
119+
- User can swipe horizontally between heatmap and list (NAV-01)
120+
- Dot indicators show current slide position (NAV-02)
121+
- Existing functionality preserved (toggle, expand/collapse)
122+
- Human verification confirms smooth UX
123+
</success_criteria>
124+
125+
<output>
126+
After completion, create `.planning/phases/06-carousel-navigation/06-02-SUMMARY.md`
127+
</output>

0 commit comments

Comments
 (0)