Skip to content

Sprint 5A: Mobile UX + Android build + Animations#1

Merged
henfrydls merged 45 commits intomainfrom
sprint-5
Mar 19, 2026
Merged

Sprint 5A: Mobile UX + Android build + Animations#1
henfrydls merged 45 commits intomainfrom
sprint-5

Conversation

@henfrydls
Copy link
Copy Markdown
Owner

Summary

  • S5-01: 44px minimum touch targets (WCAG 2.5.5)
  • S5-02: Mobile-responsive YearView with mini-heatmap + month detail
  • S5-03: Skeleton loaders with Zustand hydration gate
  • S5-04: Mobile UX — MonthView default, Log Today bar, FAB icon, empty state, drill-up nav
  • S5-05: Animations system — useAnimatedPresence hook, BottomSheet swipe gestures, M3 easing tokens, enter/exit animations for all overlays
  • S5-06: Android build setup — first APK, Tauri mobile config, release workflow with arm64 APK
  • S5-07: Device QA on Samsung Galaxy S24+ — status bar, keyboard handling, modal clipping (createPortal fix), animation reliability (double rAF), swipe toggle, responsive pagination

Notable fixes

  • Modal/ConfirmDialog rendered via createPortal to escape BottomSheet's transform containing block
  • Double requestAnimationFrame for reliable CSS transition enter animations
  • Replaced broken Tailwind v4 animate-in/out with manual CSS transitions + M3 easing
  • interactive-widget=resizes-content + adjustResize for keyboard-aware viewport

Not included

  • S5-06b (iOS): Blocked — requires macOS with Xcode

Test plan

  • Desktop: Year/Month views, animations, activity CRUD, export/import
  • Mobile (Android APK): touch targets, swipe nav, BottomSheet, QuickLog, keyboard handling
  • Release workflow: tag push generates desktop installers + arm64 APK

henfrydls and others added 30 commits February 22, 2026 23:28
- Button.tsx base: min-h-[44px] on sm/md sizes (propagates to all buttons)
- DayCell: 44px min on mobile, restores 10px on sm: desktop
- YearView: nav buttons + month labels get 44px targets
- ColorPicker: 44px circles on mobile
- DropdownMenu: 44px menu items
- ActivityForm/QuickLog: 44px inputs and checkboxes
- Clean up redundant overrides in ActivityList, QuickLog, ConfirmDialog
- useMediaQuery hook for sm: breakpoint detection
- MiniHeatmap: compact 3x4 annual overview (visual only, ~14px cells)
- MonthHeatmapDetail: interactive monthly view with 46px touch cells
- YearView: conditional render - mobile gets hybrid layout, desktop unchanged
- 44 new tests (useMediaQuery: 7, MiniHeatmap: 13, MonthHeatmapDetail: 24)
- Store: _hasHydrated flag + onRehydrateStorage + partialize
- Skeleton primitive component (animate-pulse bg-gray-200)
- YearViewSkeleton: pixel-perfect grid replica for zero CLS
- MonthViewSkeleton: 42-cell grid skeleton
- SidebarSkeleton: ActivityList + StatsPanel placeholders
- AppSkeleton: full page assembler used as hydration gate
- Pre-React inline skeleton in index.html for WebView cold start
- 18 new tests (store hydration: 3, YearViewSkeleton: 7, AppSkeleton: 8)
… icon, empty state, drill-up nav

- Default to MonthView on mobile viewports (< 640px) for first visit
- Add Log Today bar in MonthView (mobile only, current month) showing daily progress
- Change FAB icon from hamburger to checklist (clipboard-check)
- Add empty state message when no activities exist in MonthView
- Make month title clickeable to navigate back to YearView (drill-up)
- Add BottomSheet, HeatmapLegend, MonthCard, YearProgressBar components
- Add comprehensive tests for all new features (MonthView, store viewport logic)
Integrate useAnimatedPresence hook for smooth exit animation.
The modal now fades out and zooms out over 150ms before
unmounting, with the backdrop opacity also transitioning.
…ion crossfade

- Toast: exit slide-out-to-right animation with 200ms delay before removal
- DropdownMenu: useAnimatedPresence for fade-out + slide-to-top exit animation
- App: fade-in crossfade on Year/Month view switch via key prop
- Updated tests to handle animation timing (advanceTimersByTime, waitFor)
- Conditional opener plugin loading (desktop-only via #[cfg])
- Split capabilities: default (all platforms) + desktop (opener)
- Desktop build verified, ready for Android init after SDK setup
- Conditional opener plugin (desktop-only via #[cfg])
- Split capabilities: default (all) + desktop (opener)
- Fixed unused variable TS errors (MiniHeatmap, YearViewSkeleton test)
- Android Studio + SDK + NDK configured
- tauri android init + build --debug successful
- APK output: gen/android/app/build/outputs/apk/universal/debug/
Quick fixes:
- Regenerate app icons from Daylo logo for all platforms (Android mipmap, iOS, desktop)
- Add safe-area-inset padding for Android status bar (viewport-fit=cover + CSS env())
- Fix dropdown menu pre-selecting first item on mobile (focus only on keyboard)

Medium fixes:
- Add swipe gesture hook (useSwipeGesture) for Year/Month view switching
- Add activity list pagination (max 8 visible, Show all/Show less toggle)
- Expand color palette from 8 to 16 colors (Cyan, Lime, Amber, Rose, Sky, Fuchsia, Emerald, Slate)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add windowLightStatusBar for dark icons on white header (Android)
- Add night theme status bar config for future dark mode
- Make header sticky (top-0 z-30) so it stays visible on scroll
- Lock horizontal overflow and overscroll on html/body
- Reduce activity list pagination from 8 to 5 items

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resize icon content to 62% of canvas, centered with proper margins.
Android adaptive icons mask the outer ~20% — content now stays in safe zone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refactor useMediaQuery to return boolean (cleaner API).
ActivityList uses lg breakpoint (1024px) to set maxVisible.
Update MonthView/YearView to use new hook signature.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Status bar:
- Set status bar color to emerald (#10B981) instead of transparent
- Light icons on emerald background for clear visibility

Keyboard cutoff:
- Add windowSoftInputMode="adjustResize" to AndroidManifest
- Replace vh with dvh units in Modal, QuickLog, and #root
- Add interactive-widget=resizes-content to viewport meta tag

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Increase icon content from 62% to 75% of canvas for better visibility
while staying within Android adaptive icon safe zone (66% center).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
enableEdgeToEdge() was overriding theme XML settings, forcing
transparent status bar and breaking adjustResize for keyboard.

Now sets emerald status bar color programmatically in MainActivity
and lets the system handle content positioning properly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use bg-emerald-800 behind safe-area-inset-top on header instead of
deprecated window.statusBarColor. Edge-to-edge is mandatory on API 36,
so CSS is the only reliable way to control status bar appearance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dark gray behind safe-area-inset-top gives white system icons
with good contrast. Tested on Samsung Galaxy S24+ (API 36).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ColorPicker: collapsible mode (8 colors + "+" toggle for remaining)
- ColorPicker: fix aria-pressed → role="radio" + aria-checked
- QuickLog: extract duplicated creation form, add drag handle,
  move role="dialog" to panel element, reduce empty state padding,
  hide Color label in inline form
- BottomSheet: max-h-[75vh] → max-h-[75dvh]
- index.css: body min-height 100vh → 100dvh
- colors.ts: reorder for best 8 primary colors first

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ColorPicker: autoCollapse prop calculates dots per row dynamically
  using ResizeObserver, reserves last spot for "+" toggle
- ColorPicker: p-2 padding prevents ring-offset clipping on edges
- ActivityForm: flex layout with scrollable content area and sticky
  action buttons — Cancel/Create always visible with keyboard open
- Modal: flex column layout with independent scrollable children

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Set isAppearanceLightStatusBars=true for dark icons on light background
- Remove programmatic emerald statusBarColor (let CSS handle it)
- Change header bg from bg-gray-800 to bg-white for consistent look
- Works correctly in both light and dark system themes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Modal: px-6 padding + scroll gutter (-mx-1 px-1) to prevent focus ring clipping
- ActivityForm: flatten form layout (remove nested scroll), remove scrollIntoView,
  disable autoFocus in edit mode, defer state reset to prevent "New Activity" flash
- ActivityList: delay clearing editingActivity until "+ Add" is clicked
- QuickLog: Two-Phase bottom sheet — hide activity list when creating new activity,
  remove dashed border from creation form for full-width colors
- ColorPicker: reduce mobile dots from 44px to 40px (6 colors + toggle per row)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
henfrydls and others added 15 commits March 7, 2026 19:56
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add bounce animation to QuickLog activity labels on checkbox toggle.
Uses existing check-bounce keyframe with bouncingId state to track
which item is animating, cleared via onAnimationEnd for replay support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…pdownMenu

Replace non-functional Tailwind v4 animate-in/animate-out classes with
working CSS transition-[transform,opacity] patterns using M3 easing vars.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap the isCreating two-phase block in a keyed div so React
remounts it on phase toggle, triggering the view-fade animation
for a smooth 150ms crossfade between creation form and activity list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The enter transition wasn't playing because useAnimatedPresence sets
shouldRender and isVisible simultaneously. Added hasEntered state with
requestAnimationFrame delay so the sheet renders off-screen first,
then transitions on-screen. Also converted sheetHeight from ref to
state to satisfy React 19 lint rules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Portal Modal and ConfirmDialog via createPortal to escape BottomSheet's
  transform containing block (fixes clipping behind Android keyboard)
- Double rAF in BottomSheet/Toast for reliable enter animations
- Swipe toggles between Year/Month regardless of direction
Adds build-android job that compiles, signs, and uploads
daylo-android.apk alongside desktop installers on tag push.
Build only aarch64 target to reduce APK size. Covers all Android
devices since ~2017. Added compatibility note to release body.
- Add endOfLine: "lf" to prettier, .gitattributes with eol=lf
- Exclude src-tauri and .claude from eslint globalIgnores
- Add ResizeObserver polyfill in test setup
- Fix ActivityForm tests: aria-pressed → aria-checked, reset flow
- Convert slideDirection from useRef to useState (react-hooks/refs)
@henfrydls henfrydls merged commit 3f92a9b into main Mar 19, 2026
4 checks passed
@henfrydls henfrydls deleted the sprint-5 branch March 19, 2026 03:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant