Skip to main content

UI Specification: Photo Capture & Entry Creation Flow

Date: 2025-11-10 Version: 1.0 Author: Product Designer Status: Phase 2 - Awaiting Approval Platform: iOS-native feel (Flutter implementation) Devices: iPhone 15/16/17 (390-430pt width)

Overview

This document specifies the complete UI design for the Photo Capture & Entry Creation Flow - the core 60-second user experience that defines the product. This flow is triggered when users want to create a new journal entry and guides them from photo selection through to saving a completed memory. Design Principles for Entry Creation:
  • Effortless: 80% of entries completed in under 60 seconds
  • AI-Guided: Intelligent suggestions reduce cognitive load
  • Delightful: Micro-animations and celebrations make journaling feel rewarding
  • Forgiving: Clear error states, easy recovery, no data loss
  • Accessible: WCAG 2.1 AA compliant, Dynamic Type support, VoiceOver ready
User Flow:
Choose Photo Source → [Camera OR Library] → Photo Review/Edit → Journal Entry Screen → Entry Saved
Total Screens: 7 screens × 2 modes (Light/Dark) = 14 artboards 60-Second Target Breakdown:
  • Photo capture/selection: 10 seconds
  • AI prompts load: 10 seconds (with engaging loading state)
  • Select prompt + write text: 30 seconds
  • Tag emotions: 5 seconds
  • Save: 5 seconds

Screen 1: Entry Creation (Choose Photo Source)

Purpose

Present users with a clear choice between taking a new photo or selecting from their library. This is a decision point, not a complex screen.

User Journey

  • Entry Point:
    • Timeline → Tap FAB (+ button)
    • Timeline Empty State → Tap “Create Your First Memory”
    • Tab Bar → Tap center ”+” tab
  • Exit Point:
    • Tap “Take Photo” → In-App Camera (Screen 2)
    • Tap “Choose from Library” → Photo Picker (Screen 4)
    • Swipe down or tap “Cancel” → Return to Timeline

Layout Structure (Light Mode)

┌────────────────────────────────────────┐
│               [—]                      │ ← Handle (drag to dismiss)
│                                        │
│        Add a Photo                     │ ← Title (Title 2, 22pt bold)
│                                        │
│  ┌──────────────────────────────────┐ │
│  │                                  │ │
│  │  📷                              │ │ ← Camera icon (48×48pt)
│  │  Take Photo                      │ │ ← Option 1 label
│  │                                  │ │
│  │  Capture a new moment            │ │ ← Description
│  │                                  │ │
│  └──────────────────────────────────┘ │
│                                        │
│  ┌──────────────────────────────────┐ │
│  │                                  │ │
│  │  🖼️                               │ │ ← Library icon (48×48pt)
│  │  Choose from Library             │ │ ← Option 2 label
│  │                                  │ │
│  │  Select an existing photo        │ │ ← Description
│  │                                  │ │
│  └──────────────────────────────────┘ │
│                                        │
│          Cancel                        │ ← Cancel text button
│                                        │
│                                        │
│                                        │ ← Safe Area (Home Indicator)
└────────────────────────────────────────┘

Components Used

1. Bottom Sheet Container

  • Component: iOS native bottom sheet (UIModalPresentationFormSheet style in Flutter)
  • Presentation: Slide up from bottom with backdrop dim
  • Backdrop: 40% black overlay (#000000 at 0.4 opacity)
  • Height: Auto (content-driven, approximately 360pt)
  • Background:
    • Light Mode: Off-White (#F9FAFB)
    • Dark Mode: Elevated Surface (#2C3135)
  • Border Radius: 16pt (top corners only)
  • Shadow: Level 2 (raised)
    • Light Mode: rgba(0, 0, 0, 0.12), offset (0, 2pt), blur 8pt
    • Dark Mode: rgba(0, 0, 0, 0.06), offset (0, 2pt), blur 8pt
  • Padding: 24pt internal padding (all sides)
  • Safe Area: Respects bottom home indicator

2. Drag Handle

  • Component: Visual indicator for swipe-to-dismiss
  • Dimensions: 36×4pt
  • Position: Centered horizontally, 12pt from top of sheet
  • Color:
    • Light Mode: Light Gray (#DFE6E9)
    • Dark Mode: Dark Gray (#3C4347)
  • Border Radius: 2pt (pill shape)
  • Accessibility: VoiceOver “Drag to dismiss” hint
  • Interaction: Swipe down to dismiss sheet

3. Title

  • Component: Text (Title 2 style)
  • Text: “Add a Photo”
  • Typography:
    • Font: SF Pro Display, 22pt Bold
    • Line Height: 28pt
    • Letter Spacing: 0.35pt
    • Alignment: Center
  • Color:
    • Light Mode: Charcoal (#2D3436)
    • Dark Mode: Off-White (#F5F6FA)
  • Position: 32pt from top of sheet (below handle)
  • Accessibility: VoiceOver “Add a Photo, heading”

4. Option Card 1: Take Photo

Card Container:
  • Dimensions: Full-width minus padding (342pt on iPhone 15)
  • Height: 100pt
  • Border Radius: 12pt
  • Background:
    • Light Mode: White (#FFFFFF)
    • Dark Mode: Elevated Surface (#2C3135)
  • Border: 1pt solid
    • Light Mode: Light Gray (#DFE6E9)
    • Dark Mode: Dark Gray (#3C4347)
  • Shadow: None (flat in bottom sheet)
  • Position: 24pt below title
  • Layout: Horizontal (icon on left, text on right)
Camera Icon:
  • Icon: SF Symbol camera.fill or custom camera icon
  • Size: 48×48pt
  • Color: Primary Coral (#FF6B6B in Light, #FF8787 in Dark)
  • Position: 16pt from leading edge, vertically centered
  • Background: Circular container
    • Dimensions: 64×64pt circle
    • Background: Primary Coral at 10% opacity
    • Border Radius: 32pt
Label:
  • Text: “Take Photo”
  • Typography: SF Pro Text, 17pt Semibold
  • Color: Charcoal (Light) / Off-White (Dark)
  • Position: 16pt to the right of icon, vertically centered
Description:
  • Text: “Capture a new moment”
  • Typography: SF Pro Text, 15pt Regular
  • Color: Slate Gray (Light) / Light Gray (Dark)
  • Position: Below label, 4pt gap
States:
  • Default: White background, border visible
  • Hover (iPad): Background 5% darker
  • Pressed: Background 10% darker, scale 0.98 (100ms ease-in-out)
  • Focus: 2pt blue focus ring
Accessibility:
  • VoiceOver: “Take Photo, Capture a new moment, button”
  • Touch Target: Full card (100pt height)

5. Option Card 2: Choose from Library

Same specifications as Option Card 1, with differences: Library Icon:
  • Icon: SF Symbol photo.on.rectangle.fill or custom library icon
  • Color: Deep Teal (#4ECDC4 in Light, #6FE0D8 in Dark)
  • Background: Deep Teal at 10% opacity
Label:
  • Text: “Choose from Library”
Description:
  • Text: “Select an existing photo”
Position: 16pt below Option Card 1 Accessibility:
  • VoiceOver: “Choose from Library, Select an existing photo, button”

6. Cancel Button

  • Component: Text Button
  • Text: “Cancel”
  • Typography: SF Pro Text, 16pt Regular
  • Color: Primary Coral (#FF6B6B in Light, #FF8787 in Dark)
  • Position: 24pt below Option Card 2, centered
  • Touch Target: Full width, 44pt height
  • States:
    • Default: Coral text
    • Pressed: 20% darker coral, 0.8 opacity (100ms)
  • Action: Dismiss bottom sheet, return to Timeline
  • Accessibility: VoiceOver “Cancel, button”

Responsive Behavior

iPhone SE (320pt width):
  • Cards: Full-width minus 32pt margins (16pt each side)
  • Icon size: 40×40pt
  • Icon container: 56×56pt
  • Adjust padding to 16pt
iPhone 15 (390pt width) - Default:
  • Layout as shown
iPhone 15 Pro Max (430pt width):
  • Cards: Max-width 400pt (centered in sheet)
  • More generous padding (28pt)

Interactive States & Animations

Bottom Sheet Appear Animation

Duration: 300ms Curve: ease-out Sequence:
  1. Backdrop fades in (0 → 0.4 opacity)
  2. Sheet slides up from bottom (translateY: +100% → 0)
  3. Content fades in with slight delay (50ms)
Reduce Motion: Fade in only, no slide

Bottom Sheet Dismiss Animation

Duration: 250ms Curve: ease-in Triggers:
  • Swipe down on drag handle
  • Tap Cancel
  • Tap backdrop
  • Tap an option (dismiss after navigation)
Behavior: Sheet slides down, backdrop fades out simultaneously

Card Press Animation

Duration: 100ms Curve: ease-in-out Behavior: Scale 0.98, background darkens 10%

Option Selected

Duration: 250ms Sequence:
  1. Card press animation completes
  2. Sheet dismisses
  3. Navigate to next screen (camera or photo picker)

Accessibility

WCAG 2.1 AA Compliance:
  • ✅ Color Contrast: All text meets 4.5:1 minimum
  • ✅ Touch Targets: Cards 100pt height, Cancel button 44pt
  • ✅ Focus Indicators: 2pt blue ring on cards when focused
  • ✅ Dynamic Type: All text scales appropriately
VoiceOver Reading Order:
  1. “Add a Photo, heading”
  2. “Take Photo, Capture a new moment, button”
  3. “Choose from Library, Select an existing photo, button”
  4. “Cancel, button”
Gestures:
  • Swipe down: Dismiss sheet
  • Tap backdrop: Dismiss sheet
  • VoiceOver magic tap: Dismiss sheet

Content Guidelines

Title:
  • Short, clear action (2-4 words)
  • “Add” not “Upload” (warmer language)
Option Labels:
  • 20-30 characters
  • Action-oriented (verb + noun)
  • “Take” not “Capture” for camera (more casual)
Descriptions:
  • 40-60 characters
  • Benefit-focused, not feature-focused

Edge Cases

Camera Permission Denied:
  • Tapping “Take Photo” shows alert:
    • Title: “Camera Access Required”
    • Message: “To capture photos, please enable camera access in Settings.”
    • Actions: “Open Settings” (opens iOS Settings), “Cancel”
  • Alert uses iOS native style
Photo Library Permission Denied:
  • Tapping “Choose from Library” shows alert:
    • Title: “Photo Library Access Required”
    • Message: “To select photos, please enable photo library access in Settings.”
    • Actions: “Open Settings”, “Cancel”
No Permissions at All:
  • Both options are still visible (don’t hide)
  • Tapping shows appropriate permission dialog
iOS 14+ Limited Photo Access:
  • “Choose from Library” works but only shows selected photos
  • Show hint in library picker: “You’ve granted limited access. Tap to select more photos.”

Design Tokens Used

Colors:
  • colors.primary.coral (#FF6B6B / #FF8787)
  • colors.accent.teal (#4ECDC4 / #6FE0D8)
  • colors.neutral.background.surface (Off-White / Elevated Surface)
  • colors.neutral.background.primary (White / Elevated Surface)
  • colors.neutral.text.primary (Charcoal / Off-White)
  • colors.neutral.text.secondary (Slate Gray / Light Gray)
Typography:
  • typography.title2 (22pt bold)
  • typography.headline (17pt semibold)
  • typography.subheadline (15pt regular)
Spacing:
  • spacing.md (16pt)
  • spacing.lg (24pt)
  • spacing.xl (32pt)
Shadows:
  • shadows.level2 (raised)
Border Radius:
  • borderRadius.md (12pt)
  • borderRadius.lg (16pt)

Screen 2: In-App Camera

Purpose

Allow users to capture photos directly within the app with a native iOS camera feel.

User Journey

  • Entry Point: Screen 1 → Tap “Take Photo”
  • Exit Point:
    • Capture photo → Photo Review (Screen 3)
    • Tap “Close” → Return to Timeline
    • Tap “Gallery” → Photo Picker (Screen 4)

Layout Structure (Full-Screen)

┌────────────────────────────────────────┐
│  ×                                     │ ← Close button (top-left)
│                                        │
│                                        │
│                                        │
│                                        │
│                                        │
│     [Live Camera Feed Full Screen]     │ ← Camera preview
│                                        │
│                                        │
│                                        │
│                                        │
│                                        │
│  [Flash]  [Flip]      ⚪      [📷]    │ ← Camera controls
│                     Shutter   Gallery  │
│                                        │
└────────────────────────────────────────┘

Components Used

1. Camera Preview

  • Component: Native iOS camera feed (AVCaptureVideoPreviewLayer in Flutter)
  • Dimensions: Full screen (edge-to-edge)
  • Aspect Ratio: Native device aspect ratio (e.g., 16:9, 19.5:9)
  • Background: Black (#000000)
  • Focus: Tap-to-focus supported
    • Tap anywhere on preview → Yellow focus square appears (80×80pt)
    • Square fades out after 1 second
    • Haptic feedback on focus lock
  • Exposure: Auto-exposure enabled
  • Quality: High quality (1080p minimum)
  • Performance: 30fps minimum, 60fps target
Tap-to-Focus Indicator:
  • Dimensions: 80×80pt square
  • Border: 2pt yellow stroke
  • Animation: Fade in + scale from 1.2 → 1.0 (200ms), fade out after 1s
  • Position: Centered on tap location

2. Close Button

  • Component: Icon Button
  • Icon: SF Symbol xmark (X)
  • Size: 24×24pt icon
  • Touch Target: 44×44pt
  • Position: Top-left, 16pt from leading and top Safe Area edges
  • Background: 50% black blur (iOS native blur effect)
    • Dimensions: 44×44pt circle
    • Background: rgba(0, 0, 0, 0.5) with backdrop filter blur
  • Icon Color: White (#FFFFFF)
  • States:
    • Default: White icon, blur background
    • Pressed: Icon at 0.6 opacity, scale 0.95 (100ms)
  • Action: Dismiss camera, return to Timeline
  • Accessibility: VoiceOver “Close camera, button”

3. Flash Control

  • Component: Icon Button (toggle)
  • Icons:
    • Auto: SF Symbol bolt.badge.a (default)
    • On: SF Symbol bolt.fill
    • Off: SF Symbol bolt.slash
  • Size: 24×24pt icon
  • Touch Target: 44×44pt
  • Position: Bottom-left, 24pt from leading edge, 32pt from bottom Safe Area
  • Background: 50% black blur (44×44pt circle)
  • Icon Color:
    • Auto: White
    • On: Yellow (#FDCB6E)
    • Off: White with 0.6 opacity
  • States: Cycles through Auto → On → Off → Auto
  • Animation: Icon change with cross-fade (200ms)
  • Accessibility: VoiceOver “Flash mode: [auto/on/off], button”

4. Flip Camera Button

  • Component: Icon Button
  • Icon: SF Symbol arrow.triangle.2.circlepath.camera (camera flip)
  • Size: 24×24pt icon
  • Touch Target: 44×44pt
  • Position: To the right of Flash, 88pt from leading edge, 32pt from bottom
  • Background: 50% black blur (44×44pt circle)
  • Icon Color: White
  • States:
    • Default: White icon
    • Pressed: Icon at 0.6 opacity
    • Flipping: Icon rotates 180° (300ms)
  • Action: Toggle between rear and front camera
  • Animation: Icon rotates + camera feed crossfades (300ms)
  • Accessibility: VoiceOver “Flip camera, button”

5. Shutter Button

  • Component: Large circular button (primary action)
  • Dimensions: 70×70pt
  • Position: Center-bottom, 32pt from bottom Safe Area
  • Design:
    • Outer Circle: 70×70pt, white border (4pt stroke), transparent fill
    • Inner Circle: 58×58pt, white fill, centered
    • Gap: 6pt between outer and inner circles
  • States:
    • Default: White circles, no fill in outer ring
    • Pressed: Inner circle scales to 0.9 (100ms)
    • Capturing: White screen flash (200ms), inner circle scales to 0.7 then bounces back
  • Action: Capture photo → Navigate to Photo Review (Screen 3)
  • Accessibility:
    • VoiceOver: “Capture photo, button”
    • Hint: “Double-tap to take a photo”
Capture Animation:
  1. User taps shutter (0ms)
  2. Inner circle scales to 0.7 (100ms)
  3. White screen flash (full-screen white overlay, 0 → 1 → 0 opacity, 200ms)
  4. Camera shutter sound plays (native iOS sound)
  5. Haptic feedback (heavy impact)
  6. Inner circle bounces back to 1.0 (100ms, spring curve)
  7. Navigate to Photo Review with captured image

  • Component: Icon Button with thumbnail
  • Dimensions: 44×44pt
  • Position: Bottom-right, 24pt from trailing edge, 32pt from bottom Safe Area
  • Content:
    • Thumbnail of most recent photo in library (if available)
    • If no photos: SF Symbol photo.on.rectangle (24×24pt)
  • Border: 2pt white stroke
  • Border Radius: 8pt
  • Background:
    • With thumbnail: Photo thumbnail (cropped to fill)
    • Without thumbnail: 50% black blur
  • States:
    • Default: Thumbnail visible
    • Pressed: Scale 0.95 (100ms)
  • Action: Navigate to Photo Picker (Screen 4)
  • Accessibility: VoiceOver “Photo library, button”

Responsive Behavior

iPhone SE (320pt width):
  • Shutter button: 64×64pt (slightly smaller)
  • Control buttons: 40×40pt touch targets
  • Adjust bottom spacing to 24pt
iPhone 15 (390pt width) - Default:
  • Layout as shown
iPhone 15 Pro Max (430pt width):
  • Shutter button: 76×76pt (slightly larger)
  • More generous spacing
Orientation:
  • Portrait: Layout as shown
  • Landscape: Shutter button moves to right edge, controls rotate to right side

Interactive States & Animations

Camera Open Animation

Duration: 300ms Curve: ease-out Behavior: Fade in from black (camera feed appears)

Capture Animation (Detailed)

Total Duration: 500ms Sequence:
  1. Tap shutter (0ms)
  2. Shutter button inner circle scales to 0.7 (0-100ms, ease-in)
  3. White flash overlay appears (100ms, fade in)
  4. Camera captures still image (100ms)
  5. White flash fades out (100-300ms, fade out)
  6. Shutter button bounces back (100-200ms, spring)
  7. Haptic heavy impact (100ms)
  8. Camera shutter sound (100ms)
  9. Freeze frame of captured photo (300-500ms)
  10. Transition to Photo Review (500ms)

Focus Lock Animation

Duration: 200ms Behavior: Yellow square appears at tap location, scales from 1.2 → 1.0, fades out after 1s

Flip Camera Animation

Duration: 300ms Behavior: Camera feed crossfades, flip icon rotates 180°

Accessibility

WCAG 2.1 AA Compliance:
  • ✅ Touch Targets: All buttons 44×44pt minimum (shutter 70×70pt)
  • ✅ Color Contrast: White on black meets AAA (high contrast)
  • ✅ Focus Indicators: All buttons have visible pressed states
  • ✅ VoiceOver: All controls clearly labeled
VoiceOver Reading Order:
  1. “Close camera, button”
  2. “Flash mode: auto, button”
  3. “Flip camera, button”
  4. “Capture photo, button”
  5. “Photo library, button”
Accessibility Alternatives:
  • Users who can’t use camera can tap “Gallery” to select existing photos
  • Camera permission denied: Show alert with “Open Settings” option
Dynamic Type: Not applicable (camera UI uses fixed sizes for consistency)

Content Guidelines

No text content on this screen - all icon-based UI for visual clarity.

Edge Cases

Camera Permission Denied (First Time):
  • Camera opens → iOS system permission dialog appears immediately
  • Dialog: “App would like to access the camera”
  • If Allow: Camera feed starts
  • If Don’t Allow: Alert appears
    • Title: “Camera Access Required”
    • Message: “To capture photos, enable camera access in Settings.”
    • Actions: “Open Settings”, “Cancel”
Camera Permission Denied (Previously):
  • Tapping “Take Photo” on Screen 1 shows alert before opening camera
  • User must manually enable in Settings
Camera Hardware Failure:
  • Show error overlay on black screen:
    • Icon: SF Symbol exclamationmark.triangle (48×48pt, white)
    • Message: “Camera unavailable. Please try again.”
    • Button: “Close” (returns to Timeline)
Front Camera No Flash:
  • Flash control automatically disabled (grayed out) when front camera active
  • Accessibility hint: “Flash not available with front camera”
Low Light:
  • iOS Night Mode activates automatically if available (iPhone 11+)
  • No manual control in MVP
Storage Full:
  • After capture, if device storage full:
    • Alert: “Not enough storage to save photo. Free up space and try again.”
    • Action: “OK” (returns to camera, photo discarded)

Design Tokens Used

Colors:
  • colors.system.white (#FFFFFF)
  • colors.system.black (#000000)
  • colors.system.yellow (#FDCB6E) - Flash on state
Spacing:
  • spacing.md (16pt)
  • spacing.lg (24pt)
  • spacing.xl (32pt)
Shadows/Blur:
  • Custom: 50% black blur for controls
Components:
  • Native iOS camera (AVCaptureSession)
  • Custom shutter button design

Screen 3: Photo Review

Purpose

Show the captured photo and allow users to accept it or retake.

User Journey

  • Entry Point: Screen 2 → Capture photo
  • Exit Point:
    • Tap “Use Photo” → Journal Entry Screen (Screen 6)
    • Tap “Retake” → Return to Camera (Screen 2)

Layout Structure (Full-Screen)

┌────────────────────────────────────────┐
│                                        │
│                                        │
│                                        │
│                                        │
│         [Captured Photo Preview]       │ ← Full-screen photo
│         (Full Resolution)              │
│                                        │
│                                        │
│                                        │
│                                        │
│                                        │
│  ┌──────────┐            ┌──────────┐ │
│  │  Retake  │            │Use Photo │ │ ← Action buttons
│  └──────────┘            └──────────┘ │
│                                        │
└────────────────────────────────────────┘

Components Used

1. Photo Preview

  • Component: Full-screen image view
  • Dimensions: Full screen, aspect-fill (photo fills entire screen)
  • Aspect Ratio: Original photo aspect ratio maintained, cropped to fit screen
  • Background: Black (#000000) for letterboxing if needed
  • Quality: High resolution (not thumbnail)
  • Zoom: Pinch-to-zoom enabled
    • Min scale: 1.0 (fit screen)
    • Max scale: 3.0 (3x zoom)
    • Double-tap to zoom in/out
  • Performance: Smooth pinch gestures, 60fps
Zoom Indicators:
  • No visible zoom UI (native iOS behavior)
  • Scale automatically resets to 1.0 when navigating away

2. Retake Button

  • Component: Secondary Button
  • Text: “Retake”
  • Dimensions:
    • Width: 140pt
    • Height: 50pt
    • Border Radius: 12pt
  • Position: Bottom-left, 24pt from leading edge, 32pt from bottom Safe Area
  • Typography: SF Pro Text, 17pt Semibold
  • Colors:
    • Background: 50% black blur (iOS native)
    • Text: White (#FFFFFF)
    • Border: 1pt white stroke
  • Shadow: None (blur provides depth)
  • States:
    • Default: Blur background, white text
    • Hover: Background 10% brighter
    • Pressed: Background 20% brighter, scale 0.98 (100ms)
    • Focus: 2pt blue ring
  • Action: Discard photo, return to Camera (Screen 2)
  • Accessibility:
    • VoiceOver: “Retake photo, button”
    • Hint: “Double-tap to retake the photo”

3. Use Photo Button

  • Component: Primary Button
  • Text: “Use Photo”
  • Dimensions:
    • Width: 140pt
    • Height: 50pt
    • Border Radius: 12pt
  • Position: Bottom-right, 24pt from trailing edge, 32pt from bottom Safe Area
  • Typography: SF Pro Text, 17pt Semibold
  • Colors:
    • Background: Primary Coral (#FF6B6B in Light, #FF8787 in Dark)
    • Text: White (#FFFFFF)
  • Shadow: Level 2 (raised)
    • Shadow: rgba(0, 0, 0, 0.16), offset (0, 4pt), blur 16pt
  • States:
    • Default: Coral background, shadow visible
    • Hover: 10% darker coral
    • Pressed: 20% darker coral, scale 0.98 (100ms)
    • Focus: 2pt blue ring
  • Action: Accept photo, navigate to Journal Entry Screen (Screen 6)
  • Accessibility:
    • VoiceOver: “Use Photo, button”
    • Hint: “Double-tap to use this photo for your journal entry”

Responsive Behavior

iPhone SE (320pt width):
  • Button widths: 120pt each
  • Buttons may stack vertically if needed (edge case)
  • Spacing: 16pt from edges
iPhone 15 (390pt width) - Default:
  • Layout as shown
iPhone 15 Pro Max (430pt width):
  • Button widths: 160pt each
  • More generous spacing (32pt from edges)
Orientation:
  • Portrait: Buttons at bottom as shown
  • Landscape: Buttons may move to right edge vertically stacked

Interactive States & Animations

Photo Review Appear Animation

Duration: 300ms Curve: ease-out Behavior:
  1. Photo fades in from black (camera view)
  2. Buttons slide up from bottom (translateY: +50pt → 0)
  3. Buttons fade in (0 → 1 opacity)
Reduce Motion: All elements fade in instantly

Button Press Animation

Duration: 100ms Curve: ease-in-out Behavior: Scale 0.98, background darkens

Use Photo Transition

Duration: 300ms Curve: ease-out Behavior:
  1. Photo zooms out slightly (scale 1.0 → 0.95)
  2. Fade to white
  3. Journal Entry Screen fades in

Retake Transition

Duration: 250ms Curve: ease-in Behavior: Fade to black, camera reopens

Accessibility

WCAG 2.1 AA Compliance:
  • ✅ Touch Targets: Both buttons 50pt height, 140pt width
  • ✅ Color Contrast: White text on black/coral backgrounds (high contrast)
  • ✅ Focus Indicators: 2pt blue ring on buttons
VoiceOver Reading Order:
  1. “Photo preview, image”
  2. “Retake photo, button”
  3. “Use Photo, button”
VoiceOver Photo Description:
  • “Recently captured photo” (generic)
  • Future: AI-generated alt text
Dynamic Type: Button text scales with user preferences

Content Guidelines

Button Text:
  • “Retake” not “Redo” or “Try Again” (concise)
  • “Use Photo” not “Accept” or “Continue” (clear intent)

Edge Cases

Photo Capture Failed (Rare):
  • If camera capture fails technically:
    • Show alert: “Photo capture failed. Please try again.”
    • Action: “OK” (returns to camera)
Photo Too Large:
  • Compress photo automatically (max 25MB)
  • No user-visible error unless compression fails
Out of Memory:
  • Graceful degradation: Show lower resolution preview
  • Alert if unable to process: “Photo is too large. Please try a different photo.”

Design Tokens Used

Colors:
  • colors.system.black (#000000)
  • colors.system.white (#FFFFFF)
  • colors.primary.coral (#FF6B6B / #FF8787)
Typography:
  • typography.headline (17pt semibold)
Spacing:
  • spacing.lg (24pt)
  • spacing.xl (32pt)
Shadows:
  • shadows.level2 (raised)
Border Radius:
  • borderRadius.md (12pt)

Screen 4: Photo Picker (Library)

Purpose

Allow users to select existing photos from their device library using the native iOS photo picker.

User Journey

  • Entry Point:
    • Screen 1 → Tap “Choose from Library”
    • Screen 2 → Tap “Gallery” button
  • Exit Point:
    • Select photo → Journal Entry Screen (Screen 6)
    • Tap “Cancel” → Return to previous screen

Layout Structure (Native iOS)

┌────────────────────────────────────────┐
│  Photos                          Cancel │ ← Navigation bar
├────────────────────────────────────────┤
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐         │
│  │    │ │    │ │    │ │    │         │ ← Photo grid
│  └────┘ └────┘ └────┘ └────┘         │   (4 columns)
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐         │
│  │    │ │    │ │    │ │    │         │
│  └────┘ └────┘ └────┘ └────┘         │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐         │
│  │    │ │    │ │    │ │    │         │
│  └────┘ └────┘ └────┘ └────┘         │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐         │
│  │    │ │    │ │    │ │    │         │
│  └────┘ └────┘ └────┘ └────┘         │
│                                        │
│  (Scrollable)                          │
│                                        │
└────────────────────────────────────────┘

Implementation

Native iOS Photo Picker:
  • Component: PHPickerViewController (iOS 14+)
  • Presentation: Modal sheet (slide up from bottom)
  • Configuration:
    • Selection limit: 1 photo (single selection only in MVP)
    • Media types: Images only (no videos, no Live Photos)
    • Filter: All photos and screenshots
    • Suggested selections: Show “Recents” album by default
  • Grid: 4 columns on portrait, 5 columns on landscape
  • Gutter: 2pt between thumbnails
  • Thumbnail Quality: Standard iOS quality (optimized for speed)
Why Native Picker:
  • Best performance and UX (Apple-designed)
  • Handles permissions automatically
  • Users are familiar with the interface
  • Respects iOS 14+ Limited Photo Access
  • No custom UI needed (saves development time)

Components Used

1. Navigation Bar (iOS Native)

  • Component: iOS native navigation bar
  • Title: “Photos”
  • Title Style: Headline (17pt semibold)
  • Left Button: None
  • Right Button: “Cancel”
    • Text: “Cancel”
    • Color: Primary Coral
    • Action: Dismiss picker, return to previous screen
  • Background: iOS system background (blur effect)
  • Height: 44pt

2. Photo Grid (iOS Native)

  • Component: iOS native grid view (collectionView)
  • Layout: 4 columns (portrait), 5 columns (landscape)
  • Gutter: 2pt between items
  • Margins: 0pt (edge-to-edge)
  • Thumbnail Size: Square, dynamic based on screen width
    • iPhone SE: ~78pt per thumbnail
    • iPhone 15: ~95pt per thumbnail
    • iPhone 15 Pro Max: ~105pt per thumbnail
  • Thumbnail Crop: Center crop (aspect fill)
  • Scroll: Vertical infinite scroll
  • Performance: Smooth 60fps scrolling, image caching
Selection Indicator:
  • Component: iOS native checkmark overlay
  • Position: Bottom-right corner of thumbnail
  • Checkmark: White checkmark in Primary Coral circle
  • Circle Size: 28×28pt
  • Border: 2pt white stroke
  • Animation: Scale in (0.8 → 1.0, 150ms, spring curve)

3. Album Selector (iOS Native)

  • Component: iOS native album picker (dropdown from title)
  • Access: Tap “Photos” title to see album list
  • Albums Shown: Recents, Favorites, All Photos, + user albums
  • Default: Recents album
  • Presentation: Sheet from top

Responsive Behavior

iPhone SE (320pt width):
  • Grid: 4 columns, ~76pt per thumbnail
  • Gutter: 1pt
iPhone 15 (390pt width) - Default:
  • Grid: 4 columns, ~95pt per thumbnail
  • Gutter: 2pt
iPhone 15 Pro Max (430pt width):
  • Grid: 4 columns, ~105pt per thumbnail
  • Gutter: 2pt
Orientation:
  • Portrait: 4 columns
  • Landscape: 5 columns

Interactive States & Animations

Photo Picker Appear Animation

Duration: 300ms (iOS standard) Curve: ease-out Behavior: Modal sheet slides up from bottom

Photo Selection

Duration: 150ms Behavior:
  1. User taps photo
  2. Checkmark scales in (0.8 → 1.0, spring)
  3. Haptic feedback (light impact)
  4. Photo highlight border appears (Primary Coral, 3pt)

Photo Deselection (If User Taps Selected Photo)

Duration: 150ms Behavior: Checkmark scales out (1.0 → 0.8), fade out

Picker Dismiss (After Selection)

Duration: 300ms Behavior:
  1. Sheet slides down
  2. Selected photo transitions to Journal Entry Screen
  3. Photo zooms from thumbnail position to full-screen

Accessibility

WCAG 2.1 AA Compliance:
  • ✅ Touch Targets: Each thumbnail is tappable (dynamic size, minimum 44×44pt)
  • ✅ VoiceOver: All photos have VoiceOver labels
  • ✅ Keyboard Navigation: Support for external keyboard (iPad, future)
VoiceOver:
  • Photo Labels: “Photo, [Date], [Time]” or “Screenshot, [Date], [Time]”
  • Selected Photo: “Photo, selected, [Date], [Time]”
  • Navigation: “Photos, heading, button” (tapping opens album selector)
  • Cancel: “Cancel, button”
iOS 14+ Limited Photo Access:
  • If user previously selected “Select Photos” (not “Allow All”):
    • Picker only shows selected photos
    • Banner at top: “You’ve granted limited access. Tap to select more photos.”
    • Tapping banner opens iOS Settings to add more photos

Content Guidelines

No custom content - all native iOS strings.

Edge Cases

Photo Library Permission Denied:
  • PHPickerViewController handles this automatically
  • If denied previously: Alert shown before picker opens (see Screen 1 edge cases)
iOS 14+ Limited Photo Access:
  • Picker shows only selected photos
  • Banner prompts user to select more
  • User can manage in iOS Settings
No Photos in Library:
  • iOS shows empty state: “No Photos”
  • Unlikely edge case (most users have photos)
Photo Selection Failure:
  • If import fails (rare):
    • Alert: “Couldn’t import photo. Please try again.”
    • User remains in picker
Large Photo (>25MB):
  • Picker imports photo at lower resolution automatically
  • iOS handles compression
Network Photos (iCloud):
  • Photos not downloaded yet show cloud icon
  • Tapping downloads automatically (progress indicator)
  • User waits for download, then selection completes

Design Tokens Used

Colors:
  • colors.primary.coral (#FF6B6B / #FF8787) - Checkmark, Cancel button
  • iOS system colors for everything else
Typography:
  • iOS system typography (native picker)
Components:
  • PHPickerViewController (iOS 14+)

Screen 5: Photo Edit (Optional)

Purpose

Allow users to crop and rotate photos before journaling. This screen is optional - users can skip to Journal Entry Screen.

User Journey

  • Entry Point:
    • Screen 3 → Tap “Edit” icon (added to Photo Review screen)
    • Screen 4 → After selecting photo, tap “Edit” (optional)
    • Screen 6 → Tap photo, tap “Edit”
  • Exit Point:
    • Tap “Done” → Return to Journal Entry Screen (Screen 6) with edited photo
    • Tap “Cancel” → Discard edits, return to previous screen
Note: This screen is accessed via an optional “Edit” flow, not a required step.

Layout Structure (Full-Screen)

┌────────────────────────────────────────┐
│  Cancel                          Done  │ ← Navigation bar
├────────────────────────────────────────┤
│                                        │
│                                        │
│         [Photo with Crop Grid]         │ ← Photo editor (interactive)
│         (Drag to adjust crop)          │
│                                        │
│                                        │
│                                        │
├────────────────────────────────────────┤
│  [◻]  [1:1]  [4:3]  [16:9]  [⤴️]      │ ← Editing tools
│  Free  Square  4:3    16:9    Rotate  │
└────────────────────────────────────────┘

Components Used

1. Navigation Bar

  • Component: iOS native navigation bar
  • Height: 44pt
  • Background: iOS system background (blur)
  • Left Button: “Cancel”
    • Text: “Cancel”
    • Color: Primary Coral
    • Action: Discard edits, return to previous screen
  • Right Button: “Done”
    • Text: “Done”
    • Color: Primary Coral
    • Action: Apply edits, return to Journal Entry Screen
  • Accessibility:
    • Cancel: VoiceOver “Cancel, button, discards changes”
    • Done: VoiceOver “Done, button, applies changes”

2. Photo Editor (Crop View)

  • Component: Interactive crop view (CropViewController in Flutter/iOS)
  • Dimensions: Full-width, maintains aspect ratio
  • Photo: Centered, fit to screen with margins
  • Crop Grid:
    • 3×3 grid overlay (rule of thirds)
    • Grid lines: 1pt white stroke at 50% opacity
    • Corner handles: 8×8pt circles, white fill
    • Edge handles: 6pt wide, white fill
  • Behavior:
    • Pinch to zoom in/out
    • Drag photo to reposition within crop area
    • Drag handles to adjust crop region
    • Double-tap to reset to original
  • Constraints: Crop area cannot be smaller than 200×200pt
Crop Grid Appearance:
  • Always visible while editing
  • Fades to 30% opacity when not interacting (2s delay)
  • Reappears at 100% opacity when user touches screen
Visual Dimming:
  • Area outside crop: 40% black overlay (shows what will be cropped out)

3. Aspect Ratio Tools (Bottom Toolbar)

Toolbar Container:
  • Height: 88pt (includes safe area)
  • Background: iOS blur (light blur in Light Mode, dark blur in Dark Mode)
  • Padding: 16pt horizontal
  • Layout: Horizontal row, evenly spaced
Tool Buttons (5 total):
  1. Free Crop (default selected)
  2. 1:1 Square
  3. 4:3 Standard
  4. 16:9 Widescreen
  5. Rotate (separate from aspect ratios)
Button Specifications (each):
  • Touch Target: 60×60pt
  • Icon Container: 48×48pt
  • Icon: 24×24pt
  • Label: Below icon, 11pt regular
  • Spacing: Evenly distributed across toolbar width
Free Crop Button:
  • Icon: SF Symbol rectangle.dashed (free-form)
  • Label: “Free”
  • Action: Allow any aspect ratio (default)
1:1 Square Button:
  • Icon: SF Symbol square (square)
  • Label: “Square”
  • Action: Lock crop to 1:1 aspect ratio
4:3 Standard Button:
  • Icon: Custom rectangle icon (4:3 ratio)
  • Label: “4:3”
  • Action: Lock crop to 4:3 aspect ratio
16:9 Widescreen Button:
  • Icon: Custom rectangle icon (16:9 ratio)
  • Label: “16:9”
  • Action: Lock crop to 16:9 aspect ratio
Rotate Button:
  • Icon: SF Symbol rotate.right (clockwise arrow)
  • Label: “Rotate”
  • Action: Rotate photo 90° clockwise
  • Position: Separated from aspect ratio buttons (right side)
Selected State:
  • Background: Primary Coral at 15% opacity
  • Icon Color: Primary Coral
  • Label Color: Primary Coral
  • Border: 2pt Primary Coral stroke
  • Border Radius: 8pt
Default State:
  • Background: Transparent
  • Icon Color: Charcoal (Light) / Off-White (Dark)
  • Label Color: Charcoal (Light) / Off-White (Dark)
Pressed State:
  • Scale: 0.95 (100ms)
  • Background: 10% gray

Responsive Behavior

iPhone SE (320pt width):
  • Photo editor: Smaller margins (12pt)
  • Tool buttons: 52×52pt touch targets
  • Labels: 10pt font size
iPhone 15 (390pt width) - Default:
  • Layout as shown
iPhone 15 Pro Max (430pt width):
  • Photo editor: More generous margins (24pt)
  • Tool buttons: 64×64pt touch targets
Orientation:
  • Portrait: Layout as shown
  • Landscape: Toolbar moves to right edge (vertical), photo editor left

Interactive States & Animations

Edit Screen Appear

Duration: 300ms Curve: ease-out Behavior: Fade in from previous screen

Aspect Ratio Change

Duration: 300ms Curve: spring (response: 0.3, dampingFraction: 0.7) Behavior:
  1. User taps aspect ratio button
  2. Selected state animates in (background + icon color change)
  3. Crop grid animates to new aspect ratio
  4. Photo scales/repositions to fit new crop area
  5. Haptic feedback (medium impact)

Rotate Animation

Duration: 400ms Curve: spring (response: 0.4, dampingFraction: 0.75) Behavior:
  1. User taps Rotate button
  2. Photo rotates 90° clockwise (smooth rotation)
  3. Crop grid adjusts to rotated dimensions
  4. Haptic feedback (medium impact)

Done (Apply Edits)

Duration: 300ms Behavior:
  1. Crop view fades out
  2. Edited photo transitions to Journal Entry Screen
  3. Loading indicator if processing takes >500ms

Cancel (Discard Edits)

Duration: 250ms Behavior: Fade out, return to previous screen

Accessibility

WCAG 2.1 AA Compliance:
  • ✅ Touch Targets: All tool buttons 60×60pt
  • ✅ Color Contrast: Labels and icons meet 4.5:1 minimum
  • ✅ Focus Indicators: Selected state is clearly visible
  • ✅ VoiceOver: All tools clearly labeled
VoiceOver Reading Order:
  1. “Cancel, button”
  2. “Done, button”
  3. “Photo editor, image, adjust crop by dragging”
  4. “Free crop, button”
  5. “Square crop, button”
  6. “4:3 crop, button”
  7. “16:9 crop, button”
  8. “Rotate photo, button”
VoiceOver Hints:
  • Photo editor: “Swipe to adjust crop. Double-tap to reset.”
  • Aspect ratio buttons: “Double-tap to change aspect ratio”
  • Rotate: “Double-tap to rotate photo 90 degrees”
Dynamic Type: Tool labels scale with user preferences

Content Guidelines

Button Labels:
  • Short, clear (1-2 words)
  • “Free” not “Free Crop” (concise)
  • “Rotate” not “Rotate 90°” (implied)

Edge Cases

Photo Too Large to Edit:
  • Downscale photo temporarily for editing (use preview resolution)
  • Apply edits to full-resolution photo on save
  • Show loading indicator if processing >1s
Out of Memory:
  • Alert: “Photo is too large to edit. Please try a different photo.”
  • Action: “OK” (returns to previous screen)
Crop Area Too Small:
  • Minimum crop area: 200×200pt
  • Handles resist shrinking below minimum
  • Haptic feedback (error vibration) if user tries to crop smaller
Rotate on Square Photos:
  • Rotation still works (even though visually appears the same)
  • Crop grid rotates, photo maintains orientation
Multiple Rotations:
  • User can rotate multiple times (0°, 90°, 180°, 270°)
  • Edits are applied cumulatively on save

Design Tokens Used

Colors:
  • colors.primary.coral (#FF6B6B / #FF8787)
  • colors.neutral.text.primary (Charcoal / Off-White)
  • colors.system.white (#FFFFFF)
  • colors.system.black (#000000)
Typography:
  • typography.caption1 (11pt regular) - Tool labels
  • typography.callout (16pt regular) - Navigation buttons
Spacing:
  • spacing.md (16pt)
  • spacing.lg (24pt)
Components:
  • Custom crop view (or library like TOCropViewController)

Screen 6: Journal Entry Screen (MOST CRITICAL)

Purpose

The core screen where users create a journal entry: view their photo, receive AI-generated prompts, write text, and tag emotions. This is the heart of the 60-second experience.

User Journey

  • Entry Point:
    • Screen 3 → Tap “Use Photo”
    • Screen 4 → Select photo
    • Screen 5 → Tap “Done” (if edited)
  • Exit Point:
    • Tap “Save Entry” → Entry Saved Confirmation (Screen 7)
    • Tap “Cancel” → Confirmation dialog, return to Timeline

Layout Structure (Light Mode, Scrollable)

┌────────────────────────────────────────┐
│  ×                              Save   │ ← Navigation bar
├────────────────────────────────────────┤
│                                        │
│                                        │
│         [Selected Photo]               │ ← Photo (300pt height, tappable)
│         (Tap to view full-screen)      │
│                                        │
│                                        │
├────────────────────────────────────────┤
│  ⟳ Thinking about your photo...       │ ← AI Loading State (8-15s)
│                                        │
│  ┌──────────────────────────────────┐ │
│  │ ▢  Shimmer animation             │ │ ← Prompt placeholder 1
│  └──────────────────────────────────┘ │
│  ┌──────────────────────────────────┐ │
│  │ ▢  Shimmer animation             │ │ ← Prompt placeholder 2
│  └──────────────────────────────────┘ │
│  ┌──────────────────────────────────┐ │
│  │ ▢  Shimmer animation             │ │ ← Prompt placeholder 3
│  └──────────────────────────────────┘ │
│                                        │
│  ─────── AI Prompts Loaded ────────   │
│                                        │
│  ┌──────────────────────────────────┐ │
│  │ What made this moment special?   │ │ ← Prompt 1 (tappable card)
│  └──────────────────────────────────┘ │
│  ┌──────────────────────────────────┐ │
│  │ Who were you with and what did   │ │ ← Prompt 2
│  │ you talk about?                  │ │
│  └──────────────────────────────────┘ │
│  ┌──────────────────────────────────┐ │
│  │ How did this moment make you     │ │ ← Prompt 3
│  │ feel?                            │ │
│  └──────────────────────────────────┘ │
│                                        │
│  🔄 Refresh prompts                   │ ← Refresh button (text link)
│                                        │
│  ─────────────────────────────────────│
│                                        │
│  How did you feel?                    │ ← Emotion section header
│                                        │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐         │
│  │😊  │ │❤️  │ │🎉  │ │😌  │         │ ← Emotion chips row 1
│  │Hpy │ │Grt │ │Exc │ │Pce │         │   (Positive emotions)
│  └────┘ └────┘ └────┘ └────┘         │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐         │
│  │🤔  │ │😢  │ │😊  │ │😍  │         │ ← Emotion chips row 2
│  │Tht │ │Nos │ │Prd │ │Lov │         │   (Reflective emotions)
│  └────┘ └────┘ └────┘ └────┘         │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐         │
│  │😔  │ │😰  │ │😤  │ │😌  │         │ ← Emotion chips row 3
│  │Sad │ │Anx │ │Frs │ │Hpf │         │   (Challenging emotions)
│  └────┘ └────┘ └────┘ └────┘         │
│                                        │
│  ─────────────────────────────────────│
│                                        │
│  What do you want to remember?        │ ← Text field label
│                                        │
│  ┌──────────────────────────────────┐ │
│  │ [User types here...]             │ │ ← Multi-line text field
│  │                                  │ │   (min 3 lines)
│  │                                  │ │
│  │                                  │ │
│  │                                  │ │
│  │                         450/500 ◀─── Character count (free tier)
│  └──────────────────────────────────┘ │
│                                        │
│  ┌──────────────────────────────────┐ │
│  │         Save Entry               │ │ ← Primary CTA (disabled until valid)
│  └──────────────────────────────────┘ │
│                                        │
│                                        │ ← Safe Area
└────────────────────────────────────────┘

Components Used

1. Navigation Bar

  • Component: iOS navigation bar
  • Height: 44pt
  • Background: iOS system background (blur)
  • Left Button: Close (×)
    • Icon: SF Symbol xmark, 20×20pt
    • Color: Charcoal (Light) / Off-White (Dark)
    • Touch Target: 44×44pt
    • Action: Show confirmation dialog → Return to Timeline
  • Right Button: “Save” (text button)
    • Text: “Save”
    • Color: Primary Coral (enabled), Light Gray (disabled)
    • Touch Target: 60×44pt
    • Action: Save entry → Screen 7
    • States: Disabled until photo + text/emotion added
  • Accessibility:
    • Close: VoiceOver “Close, button, shows confirmation dialog”
    • Save: VoiceOver “Save entry, button, [disabled/enabled]“

2. Photo Display

  • Component: Image view (tappable)
  • Dimensions: Full-width, 300pt fixed height
  • Aspect Ratio: Original aspect ratio maintained, cropped to fit (aspect-fill)
  • Border Radius: 0pt (full-bleed)
  • Background: Black for letterboxing
  • Quality: High resolution (not thumbnail)
  • Tap Action: Expand to full-screen view (pinch-to-zoom enabled)
  • Edit Button: Small “Edit” icon button overlaid on top-right corner
    • Icon: SF Symbol crop.rotate, 16×16pt
    • Background: 50% black blur, 32×32pt circle
    • Position: 8pt from top-right corner
    • Action: Navigate to Photo Edit screen (Screen 5)
    • Accessibility: VoiceOver “Edit photo, button”
Full-Screen Photo View (on tap):
  • Background: Black
  • Photo: Centered, pinch-to-zoom (1x to 3x)
  • Close: Tap photo or swipe down to dismiss
  • Duration: 300ms fade in/out

3. AI Loading State (8-15 seconds)

Loading Header:
  • Component: Text with animated icon
  • Icon: SF Symbol arrow.clockwise.circle (spinner), 20×20pt, rotating
  • Text: “Thinking about your photo…”
  • Typography: SF Pro Text, 16pt Regular
  • Color: Slate Gray (Light) / Light Gray (Dark)
  • Position: 24pt below photo
  • Animation: Icon rotates continuously (360° every 1s, linear)
Prompt Placeholders (3 cards):
  • Component: Skeleton loading cards
  • Dimensions: Full-width minus 32pt margins, 80pt height
  • Border Radius: 12pt
  • Background: Light Gray (#E0E0E0) with shimmer gradient overlay
  • Spacing: 12pt vertical gap between cards
  • Shimmer Animation:
    • Gradient: Linear, 45° angle, white highlight (0 → 100% → 0 opacity)
    • Duration: 1.5s continuous loop
    • Direction: Left to right
Fallback States: Taking Longer Than Expected (>15s):
  • Message: “Taking longer than expected. Try manual entry?”
  • Text Link: “Skip AI prompts” (Primary Coral)
  • Action: Hide loading state, show empty prompt cards with “Write your own entry” message
AI Error State (API failure):
  • Message: “AI couldn’t generate prompts. Write your own entry.”
  • Icon: SF Symbol exclamationmark.circle, Warning Amber
  • Action: Show empty prompt cards (user writes freely)
  • Fallback Prompts: Show 3 generic fallback prompts:
    1. “What happened in this moment?”
    2. “How did this make you feel?”
    3. “What do you want to remember about today?“

4. AI Prompt Cards (After Loading)

Success State (3 prompt cards): Prompt Card Specifications (each):
  • Dimensions: Full-width minus 32pt margins, variable height (60-80pt)
  • Border Radius: 12pt
  • Background:
    • Light Mode: Off-White (#F9FAFB)
    • Dark Mode: Elevated Surface (#2C3135)
  • Border: 1pt solid
    • Light Mode: Light Gray (#DFE6E9)
    • Dark Mode: Dark Gray (#3C4347)
    • Selected: 2pt Primary Coral
  • Padding: 16pt internal padding
  • Typography: SF Pro Text, 17pt Regular
  • Color: Charcoal (Light) / Off-White (Dark)
  • Max Length: 15 words (approximately 60-90 characters)
  • Spacing: 12pt vertical gap between cards
States:
  • Default: Light background, thin border
  • Hover (iPad): Background 5% darker
  • Pressed: Scale 0.98 (100ms)
  • Selected: 2pt Primary Coral border, coral text color
  • Focus: 2pt blue ring
Interaction:
  • Tap: Select prompt → Insert into text field
  • Long Press: Show options: “Use this prompt”, “Copy”, “Refresh prompts”
Prompt Examples:
  1. “What made this moment special?”
  2. “Who were you with and what did you talk about?”
  3. “How did this moment make you feel?”
Accessibility:
  • VoiceOver: “[Prompt text], button, tap to use this prompt”

5. Refresh Prompts Button

  • Component: Text link button
  • Icon: SF Symbol arrow.clockwise, 16×16pt (leading icon)
  • Text: “Refresh prompts”
  • Typography: SF Pro Text, 16pt Regular
  • Color: Primary Coral
  • Position: 16pt below last prompt card, leading-aligned
  • Touch Target: Full width, 44pt height
  • Action: Request 3 new AI prompts (show loading state again)
  • Limit: Maximum 5 refreshes per session (prevent API abuse)
  • Accessibility: VoiceOver “Refresh prompts, button”

6. Emotion Selector Section

Section Header:
  • Text: “How did you feel?”
  • Typography: SF Pro Text, 17pt Semibold
  • Color: Charcoal (Light) / Off-White (Dark)
  • Position: 32pt below Refresh button
  • Accessibility: VoiceOver “How did you feel, heading”
Emotion Chips Grid:
  • Layout: 4 columns × 3 rows = 12 emotions
  • Spacing: 8pt horizontal gap, 12pt vertical gap
  • Margins: 16pt horizontal
Emotion Chip Specifications (each):
  • Dimensions: Variable width (flex), 44pt height
  • Border Radius: 22pt (pill shape)
  • Padding: 10pt horizontal
  • Layout: Horizontal (icon + label)
  • Icon Size: 24×24pt emoji
  • Gap: 6pt between icon and label
Typography:
  • Font: SF Pro Text, 14pt Regular
  • Label: Abbreviated (e.g., “Happy” → “Hpy”, “Grateful” → “Grt”)
  • Color:
    • Unselected: Emotion color at 100%
    • Selected: White (#FFFFFF)
Colors (Unselected - 15% opacity background):
  • Positive Emotions:
    • Happy: Warm Coral (#FF6B6B)
    • Grateful: Soft Pink (#FF8FA3)
    • Excited: Bright Yellow (#FDCB6E)
    • Peaceful: Light Teal (#81ECEC)
  • Reflective Emotions:
    • Thoughtful: Cool Blue (#74B9FF)
    • Nostalgic: Muted Purple (#A29BFE)
    • Proud: Golden Orange (#FAB1A0)
    • Loving: Rose Pink (#FD79A8)
  • Challenging Emotions:
    • Sad: Muted Blue (#6C5CE7)
    • Anxious: Gray-Blue (#636E72)
    • Frustrated: Muted Red (#E17055)
    • Hopeful: Soft Teal (#55EFC4)
Colors (Selected - 100% opacity background):
  • Background: Emotion color at 100%
  • Text: White
  • Icon: White
  • Checkmark: SF Symbol checkmark, 16×16pt, white, trailing
States:
  • Unselected: Light background (15% opacity), colored text
  • Selected: Full color background, white text + checkmark
  • Pressed: Scale 0.98 (100ms), haptic feedback (light impact)
  • Hover (iPad): Background opacity 25%
Multi-Select Behavior:
  • Users can select 0 to 12 emotions (no limit)
  • Encourage 1-3 emotions (show hint if >5 selected)
  • Hint (if >5): “Focusing on your strongest feelings makes your entries more meaningful.”
Accessibility:
  • VoiceOver: “[Emotion name], [category], [selected/unselected], button”
  • Example: “Happy, positive emotion, selected, button”

7. Text Entry Field

Field Label:
  • Text: “What do you want to remember?”
  • Typography: SF Pro Text, 17pt Semibold
  • Color: Charcoal (Light) / Off-White (Dark)
  • Position: 32pt below emotion grid
  • Accessibility: VoiceOver “What do you want to remember, label”
Text Field:
  • Component: Multi-line text area (iOS UITextView / Flutter TextField)
  • Dimensions: Full-width minus 32pt margins, min 120pt height (3 lines)
  • Max Height: 300pt (scrollable if text exceeds)
  • Border Radius: 12pt
  • Padding: 16pt internal padding
  • Background:
    • Light Mode: Off-White (#F9FAFB)
    • Dark Mode: Elevated Surface (#2C3135)
  • Border: 1pt solid
    • Light Mode: Light Gray (#DFE6E9)
    • Dark Mode: Dark Gray (#3C4347)
    • Focus: 2pt Primary Coral
  • Typography: SF Pro Text, 17pt Regular
  • Line Height: 22pt
  • Color: Charcoal (Light) / Off-White (Dark)
  • Placeholder: “Tap an AI prompt above or write freely…”
  • Placeholder Color: Light Gray at 60% opacity
Character Count:
  • Position: Bottom-right corner of text field (inside padding)
  • Typography: SF Pro Text, 13pt Regular
  • Color:
    • 0-450 chars: Light Gray (not visible)
    • 451-500 chars: Slate Gray (visible)
    • 500 chars: Error Red (limit reached)
  • Format: “450 / 500” or “500 / 500” (free tier limit)
Free Tier Limit:
  • Limit: 500 characters per entry
  • Behavior: Text field becomes non-editable at 500 chars
  • Upgrade CTA: “Upgrade to Premium for unlimited characters” (appears below field at limit)
Premium Tier:
  • Limit: Unlimited characters
  • Character count: Not shown (or shows “No limit”)
States:
  • Default: Light background, thin border
  • Focus: Coral border, keyboard opens
  • Typing: Character count updates in real-time
  • At Limit: Error red count, slight red tint to border
  • Error: Red border if invalid (edge case)
Auto-Save:
  • Drafts auto-save every 10 seconds
  • Draft preserved on app backgrounding
  • Draft cleared on successful save
Accessibility:
  • VoiceOver: “Journal entry text, text area, [X] characters”
  • Dynamic Type: Text size scales appropriately

8. Save Entry Button

  • Component: Primary Button
  • Text: “Save Entry”
  • Dimensions: Full-width minus 32pt margins, 56pt height (taller for emphasis)
  • Border Radius: 12pt
  • Typography: SF Pro Text, 17pt Semibold
  • Position: 32pt below text field, 32pt above bottom Safe Area
  • Background:
    • Enabled: Primary Coral (#FF6B6B / #FF8787)
    • Disabled: Light Gray (#DFE6E9 in Light, #3C4347 in Dark)
  • Text Color: White
  • Shadow: Level 2 (when enabled)
  • States:
    • Disabled: Gray background, no shadow, no interaction
    • Enabled: Coral background, shadow visible
    • Hover: 10% darker coral
    • Pressed: 20% darker coral, scale 0.98 (100ms)
    • Saving: Loading spinner replaces text (white spinner)
Enabled Logic:
  • Entry is valid if:
    • Photo is attached AND
    • (Text is not empty OR at least 1 emotion is selected)
  • Button is disabled until both conditions met
Save Action:
  1. Validate entry (photo + text/emotion)
  2. Show loading spinner on button
  3. Upload photo (if not already uploaded)
  4. Save entry to database
  5. Navigate to Screen 7 (Entry Saved Confirmation)
Accessibility:
  • VoiceOver: “Save Entry, button, [enabled/disabled]”
  • Hint: “Double-tap to save your journal entry”

Responsive Behavior

iPhone SE (320pt width):
  • Photo height: 240pt
  • Margins: 24pt (reduced from 32pt)
  • Emotion chips: 3 columns (instead of 4)
  • Text field: Min height 100pt
iPhone 15 (390pt width) - Default:
  • Layout as shown
iPhone 15 Pro Max (430pt width):
  • Photo height: 320pt
  • Margins: 32pt
  • Emotion chips: 4 columns (as shown)
  • Text field: Min height 140pt
Scrolling:
  • Entire screen scrollable (except navigation bar)
  • Scroll-to-top on tapping photo
  • Keyboard pushes text field into view when focused

Interactive States & Animations

Screen Appear Animation

Duration: 300ms Curve: ease-out Sequence:
  1. Photo fades in (0s)
  2. AI loading state appears (100ms delay)
  3. Emotion section fades in (200ms delay)
  4. Text field fades in (300ms delay)
  5. Save button fades in (400ms delay)
Reduce Motion: All elements fade in instantly

AI Prompts Loading → Loaded Transition

Duration: 500ms Curve: spring (response: 0.3, dampingFraction: 0.7) Sequence:
  1. Loading header fades out
  2. Shimmer placeholders fade out
  3. Actual prompt cards fade in + slide up 10pt
  4. Stagger each prompt by 100ms (1, 2, 3)
  5. Refresh button fades in
Celebration Micro-Animation (when prompts load):
  • Subtle scale pulse on each prompt card (1.0 → 1.02 → 1.0, 300ms)
  • Haptic feedback (light impact)

Prompt Selection Animation

Duration: 200ms Behavior:
  1. User taps prompt card
  2. Card border changes to coral (2pt)
  3. Card text changes to coral
  4. Prompt text copies to clipboard
  5. Text field focuses (keyboard opens)
  6. Prompt text appears in field with typing animation (optional)
  7. Haptic feedback (medium impact)
Typing Animation (optional, delightful):
  • Prompt text appears letter-by-letter in text field
  • Duration: 1s (fast enough to not annoy)
  • Skippable: User can start typing immediately to interrupt

Emotion Selection Animation

Duration: 150ms Behavior:
  1. User taps emotion chip
  2. Background color fills (15% → 100% opacity, 150ms)
  3. Text color changes to white
  4. Checkmark scales in (0.8 → 1.0, spring)
  5. Haptic feedback (light impact)
Deselection: Reverse animation Multi-Select Hint (if >5 selected):
  • Toast notification appears: “Focusing on your strongest feelings makes your entries more meaningful.”
  • Duration: 3s, auto-dismiss
  • Position: Top of screen (below nav bar)
  • Dismiss: Swipe up or auto-dismiss

Text Field Focus Animation

Duration: 200ms Behavior:
  1. User taps text field
  2. Border color changes to coral (1pt → 2pt, 200ms)
  3. Keyboard slides up from bottom (iOS native animation)
  4. Field scrolls into view (if needed)

Save Entry Animation

Duration: Variable (depends on upload speed) Sequence:
  1. User taps Save Entry
  2. Button text fades out (100ms)
  3. Loading spinner fades in (100ms)
  4. Button background pulses subtly (0.95 → 1.0 scale, 1s loop)
  5. Upload progress (if photo not uploaded yet)
  6. On success: Checkmark replaces spinner (200ms)
  7. Navigate to Screen 7
Upload Progress (if photo not uploaded):
  • Small progress indicator below Save button
  • Text: “Uploading photo… 45%”
  • Linear progress bar (coral color)

Cancel / Close Confirmation

Trigger: User taps Close (×) button with unsaved changes Confirmation Dialog:
  • Title: “Discard Entry?”
  • Message: “Your entry will be lost if you don’t save it.”
  • Actions:
    • “Discard” (destructive, red text)
    • “Keep Editing” (default, bold)
  • Behavior: If user has unsaved changes, show dialog. If no changes, dismiss immediately.
Unsaved Changes Detection:
  • Photo added OR
  • Text field not empty OR
  • At least 1 emotion selected

Accessibility

WCAG 2.1 AA Compliance:
  • ✅ Color Contrast: All text meets 4.5:1 minimum
  • ✅ Touch Targets: All interactive elements 44×44pt minimum (Save button 56pt)
  • ✅ Focus Indicators: Coral border on focused elements
  • ✅ Dynamic Type: All text scales appropriately
  • ✅ VoiceOver: Logical reading order, clear labels
VoiceOver Reading Order:
  1. “Close, button”
  2. “Save, button, disabled”
  3. “Photo, image, tap to view full-screen”
  4. “Edit photo, button”
  5. “Thinking about your photo, loading”
  6. [After loading] “AI prompt 1: [text], button”
  7. “AI prompt 2: [text], button”
  8. “AI prompt 3: [text], button”
  9. “Refresh prompts, button”
  10. “How did you feel, heading”
  11. [For each emotion] “[Emotion name], [category], [selected/unselected], button”
  12. “What do you want to remember, label”
  13. “Journal entry text, text area”
  14. “Save Entry, button”
VoiceOver Hints:
  • Photo: “Double-tap to view full-screen”
  • Prompt cards: “Double-tap to use this prompt in your entry”
  • Emotion chips: “Double-tap to select this emotion”
  • Save button: “Double-tap to save your journal entry”
Dynamic Type Scaling:
  • At AX5 (largest size):
    • Photo height: 240pt (reduced)
    • Prompt cards: Line breaks allowed, variable height
    • Emotion chips: Stack vertically if needed (2 columns)
    • Text field: Min height 160pt
    • All content scrollable

Content Guidelines

AI Prompt Quality:
  • Length: 8-15 words (approximately 40-75 characters)
  • Tone: Thoughtful, reflective questions
  • Specificity: Reference photo content when possible
  • Examples:
    • Good: “What made this sunset moment special?”
    • Bad: “Write about this.” (too generic)
    • Good: “Who were you with and what were you celebrating?”
    • Bad: “Describe the people in the photo.” (too clinical)
Fallback Prompts (if AI fails):
  1. “What happened in this moment?”
  2. “How did this make you feel?”
  3. “What do you want to remember about today?”
Text Field Placeholder:
  • “Tap an AI prompt above or write freely…”
  • Clear, helpful, not prescriptive
Section Headers:
  • Short, conversational questions
  • “How did you feel?” not “Select Emotions” (warmer)

Edge Cases

AI Generation Timeout (>15s):
  • Show message: “Taking longer than expected. Try manual entry?”
  • Show “Skip AI prompts” link
  • User can write freely without AI
AI Generation Error (API failure):
  • Show error message: “AI couldn’t generate prompts. Write your own entry.”
  • Icon: Warning amber
  • Show 3 generic fallback prompts
  • Log error for debugging (no user-facing technical details)
Network Error During Save:
  • Queue entry for retry
  • Show toast: “Entry saved locally. Will sync when online.”
  • Entry appears in timeline immediately (optimistic UI)
  • Background sync when network available
Photo Upload Failure:
  • Retry upload 3 times
  • If all retries fail:
    • Alert: “Photo upload failed. Entry saved without photo.”
    • Options: “Retry Upload”, “Save Without Photo”, “Cancel”
  • Entry with photo placeholder + text/emotions saved
Text Field at 500 Character Limit (Free Tier):
  • Character count turns red
  • Border tints red slightly
  • Keyboard continues working but no new characters accepted
  • Show upgrade CTA: “Upgrade to Premium for unlimited characters”
No Emotions Selected, No Text Entered:
  • Save button remains disabled
  • No error message (implied by disabled button)
  • User must add either text or emotions to save
User Closes App Mid-Entry:
  • Draft auto-saves
  • On app relaunch: “You have an unsaved entry. Continue editing?”
  • Options: “Continue”, “Discard”
Prompt Refresh Limit Reached (5 refreshes):
  • “Refresh prompts” button becomes disabled
  • Message: “You’ve reached the refresh limit. Write freely!”
  • Prevents API abuse
Emoji in Text Field:
  • Fully supported (no character encoding issues)
  • Emoji count as 2 characters for free tier limit
Voice Dictation:
  • Supported via iOS keyboard
  • No character limit applied during dictation (applies after user stops)

Design Tokens Used

Colors:
  • colors.primary.coral (#FF6B6B / #FF8787)
  • colors.neutral.text.primary (Charcoal / Off-White)
  • colors.neutral.text.secondary (Slate Gray / Light Gray)
  • colors.neutral.background.surface (Off-White / Elevated Surface)
  • colors.neutral.background.primary (White / Elevated Surface)
  • colors.neutral.border (Light Gray / Dark Gray)
  • colors.system.error (#D63031)
  • colors.system.success (#00B894)
  • All 12 emotion colors
Typography:
  • typography.headline (17pt semibold) - Section headers
  • typography.body (17pt regular) - Prompt cards, text field
  • typography.subheadline (15pt regular) - AI loading message
  • typography.footnote (13pt regular) - Character count
  • typography.caption1 (12pt regular) - Hints
Spacing:
  • spacing.xs (8pt)
  • spacing.sm (12pt)
  • spacing.md (16pt)
  • spacing.lg (24pt)
  • spacing.xl (32pt)
Shadows:
  • shadows.level2 (raised)
Border Radius:
  • borderRadius.md (12pt)
  • borderRadius.full (22pt for emotion chips)

Screen 7: Entry Saved Confirmation

Purpose

Celebrate the user’s entry creation and confirm success. Brief moment of delight before returning to timeline.

User Journey

  • Entry Point: Screen 6 → Tap “Save Entry” (successful save)
  • Exit Point:
    • Auto-dismiss to Timeline after 3 seconds
    • Tap “View Entry” → Entry Detail View
    • Swipe down → Timeline (early dismiss)

Layout Structure (Full-Screen Overlay)

┌────────────────────────────────────────┐
│                                        │
│                                        │
│                                        │
│               ✓                        │ ← Success checkmark (animated)
│          (120×120pt)                   │
│                                        │
│         Entry saved!                   │ ← Success message
│                                        │
│  Your memory has been added to your    │ ← Subtext
│  timeline.                             │
│                                        │
│                                        │
│  ┌──────────────────────────────────┐ │
│  │       View Entry                 │ │ ← Primary CTA
│  └──────────────────────────────────┘ │
│                                        │
│  Returning to timeline in 3s...       │ ← Auto-dismiss countdown
│                                        │
│                                        │
└────────────────────────────────────────┘

Components Used

1. Background Overlay

  • Component: Full-screen overlay
  • Background:
    • Light Mode: White (#FFFFFF) at 95% opacity
    • Dark Mode: Background Black (#1A1D1F) at 95% opacity
  • Backdrop Blur: iOS native blur effect (light/dark)
  • Dismissible: Swipe down to dismiss early

2. Success Checkmark Icon

  • Component: Animated icon
  • Icon: SF Symbol checkmark.circle.fill
  • Size: 120×120pt
  • Color: Success Green (#00B894)
  • Position: Centered horizontally, 30% from top
  • Animation (on appear):
    • Duration: 600ms
    • Sequence:
      1. Icon scales from 0 → 1.2 (300ms, ease-out)
      2. Icon bounces to 1.0 (300ms, spring curve)
      3. Subtle rotation wiggle: -5° → +5° → 0° (200ms)
    • Haptic: Success haptic feedback (notification type)
  • Reduce Motion: Fade in only, no scale/bounce
Confetti Animation (Optional, Delightful):
  • Particle effect: Small colored dots burst from checkmark
  • Duration: 1s (fades out)
  • Colors: Primary Coral, Deep Teal, Soft Peach
  • Reduce Motion: Skip confetti entirely

3. Success Message (Headline)

  • Component: Text (Title 2 style)
  • Text: “Entry saved!”
  • Typography: SF Pro Display, 28pt Bold
  • Color: Charcoal (Light) / Off-White (Dark)
  • Position: 32pt below checkmark icon
  • Alignment: Center
  • Accessibility: VoiceOver “Entry saved! heading”

4. Subtext

  • Component: Text (Body style)
  • Text: “Your memory has been added to your timeline.”
  • Typography: SF Pro Text, 17pt Regular
  • Color: Slate Gray (Light) / Light Gray (Dark)
  • Position: 12pt below headline
  • Alignment: Center
  • Max Width: 300pt
  • Accessibility: VoiceOver reads after headline

5. View Entry Button

  • Component: Primary Button
  • Text: “View Entry”
  • Dimensions: Full-width minus 48pt margins, 50pt height
  • Border Radius: 12pt
  • Typography: SF Pro Text, 17pt Semibold
  • Background: Primary Coral (#FF6B6B / #FF8787)
  • Text Color: White
  • Shadow: Level 2 (raised)
  • Position: 48pt below subtext
  • Action: Navigate to Entry Detail View (full view of saved entry)
  • States:
    • Default: Coral background
    • Hover: 10% darker
    • Pressed: 20% darker, scale 0.98 (100ms)
  • Accessibility: VoiceOver “View Entry, button”

6. Auto-Dismiss Countdown

  • Component: Text (Footnote style)
  • Text: “Returning to timeline in 3s…”
  • Typography: SF Pro Text, 13pt Regular
  • Color: Slate Gray (Light) / Light Gray (Dark)
  • Position: 24pt below View Entry button, centered
  • Animation:
    • Countdown: “3s” → “2s” → “1s” (updates every second)
    • Fade out when dismissed
  • Accessibility: VoiceOver “Returning to timeline in 3 seconds”
Countdown Logic:
  • Timer starts on screen appear
  • Pauses if user taps View Entry (prevents early dismiss)
  • User can swipe down to dismiss early

Responsive Behavior

iPhone SE (320pt width):
  • Checkmark: 100×100pt
  • Headline: 24pt
  • Margins: 32pt horizontal
iPhone 15 (390pt width) - Default:
  • Layout as shown
iPhone 15 Pro Max (430pt width):
  • Checkmark: 140×140pt
  • More generous vertical spacing

Interactive States & Animations

Success Screen Appear Animation

Duration: 600ms Curve: spring (response: 0.4, dampingFraction: 0.7) Sequence:
  1. Backdrop fades in (0-200ms)
  2. Checkmark animates in (200-800ms):
    • Scale from 0 → 1.2 (200-500ms, ease-out)
    • Bounce to 1.0 (500-800ms, spring)
    • Rotate wiggle: -5° → +5° → 0° (600-800ms)
  3. Confetti burst (if enabled) (500ms)
  4. Headline fades in + slide up 10pt (400ms delay)
  5. Subtext fades in + slide up 10pt (500ms delay)
  6. View Entry button fades in + slide up 10pt (600ms delay)
  7. Countdown fades in (700ms delay)
  8. Haptic feedback: Success notification (at 500ms)
Reduce Motion: All elements fade in instantly, no animations

Auto-Dismiss Transition

Duration: 300ms Trigger: 3-second countdown completes Behavior:
  1. Entire success screen fades out
  2. Timeline fades in with newly saved entry at top
  3. New entry has subtle highlight pulse (1.0 → 1.02 → 1.0 scale, 500ms)

View Entry Button Press

Duration: 300ms Behavior:
  1. Button press animation (scale 0.98)
  2. Success screen fades out
  3. Entry Detail View fades in (full entry with photo, text, emotions)

Early Dismiss (Swipe Down)

Duration: 250ms Trigger: User swipes down on overlay Behavior: Success screen slides down + fades out, return to Timeline

Accessibility

WCAG 2.1 AA Compliance:
  • ✅ Color Contrast: All text meets 4.5:1 minimum
  • ✅ Touch Targets: View Entry button 50pt height
  • ✅ VoiceOver: Clear success announcement
  • ✅ Reduce Motion: Animations simplified or skipped
VoiceOver Reading Order:
  1. “Entry saved! heading”
  2. “Your memory has been added to your timeline”
  3. “View Entry, button”
  4. “Returning to timeline in 3 seconds”
VoiceOver Announcement (on screen appear):
  • Immediate announcement: “Entry saved successfully”
  • Interrupts current reading (high priority)
Reduce Motion:
  • Skip checkmark bounce/wiggle
  • Skip confetti
  • Fade in only, no slides/scales
Dynamic Type: All text scales appropriately

Content Guidelines

Headline:
  • Celebratory, concise (1-3 words)
  • “Entry saved!” > “Success” (more personal)
  • Exclamation point adds warmth
Subtext:
  • 100-150 characters
  • Explains what happened
  • “Your memory” not “Your entry” (warmer language)
Button Text:
  • Action-oriented (verb + noun)
  • “View Entry” > “See Entry” (clearer)
Countdown:
  • Clear timing (“3s” not “3 seconds”)
  • Non-intrusive color (gray, not bold)

Edge Cases

Save Succeeded, But Entry Not Immediately Visible:
  • Rare: Database write delay
  • Timeline shows entry after 1-2s (acceptable)
  • No error shown (optimistic UI)
Network Error During Save (Handled Earlier):
  • This screen only shows if save succeeded
  • If save failed, user sees error on Screen 6 (not Screen 7)
User Backgrounds App During Countdown:
  • Countdown pauses
  • On foreground: Resume countdown
  • If countdown expired: Dismiss and return to Timeline
User Quickly Creates Another Entry:
  • Success screen dismisses immediately
  • Entry creation flow starts fresh

Design Tokens Used

Colors:
  • colors.system.success (#00B894) - Checkmark
  • colors.primary.coral (#FF6B6B / #FF8787) - View Entry button
  • colors.neutral.text.primary (Charcoal / Off-White)
  • colors.neutral.text.secondary (Slate Gray / Light Gray)
  • colors.neutral.background.primary (White / Black) - Overlay
Typography:
  • typography.title2 (28pt bold) - Headline
  • typography.body (17pt regular) - Subtext
  • typography.headline (17pt semibold) - Button
  • typography.footnote (13pt regular) - Countdown
Spacing:
  • spacing.sm (12pt)
  • spacing.lg (24pt)
  • spacing.xl (32pt)
  • spacing.xxl (48pt)
Shadows:
  • shadows.level2 (raised) - Button
Border Radius:
  • borderRadius.md (12pt)

Dark Mode Specifications

All 7 screens have full Dark Mode variants. Key adjustments from Light Mode:

Color Adjustments

Backgrounds:
  • Light Mode: White (#FFFFFF) / Off-White (#F9FAFB)
  • Dark Mode: Background Black (#1A1D1F) / Elevated Surface (#2C3135)
Text:
  • Primary: Charcoal (#2D3436) → Off-White (#F5F6FA)
  • Secondary: Slate Gray (#636E72) → Light Gray (#B2BEC3)
Primary Coral:
  • Light Mode: #FF6B6B
  • Dark Mode: #FF8787 (10% brighter for dark background legibility)
Borders:
  • Light Mode: Light Gray (#DFE6E9)
  • Dark Mode: Dark Gray (#3C4347)
Shadows:
  • Light Mode: Full opacity (rgba(0, 0, 0, 0.08-0.16))
  • Dark Mode: 50% reduced opacity (rgba(0, 0, 0, 0.04-0.08))
Emotion Colors:
  • All emotion colors slightly adjusted (5-10% brighter) for dark mode legibility
  • Maintain same relative differences between colors

Animation Specifications Summary

Global Animation Principles

  • Respect Reduce Motion: All animations have fade-only fallbacks
  • Timing: Use iOS-standard spring curves for organic feel (response: 0.3, dampingFraction: 0.7)
  • Performance: 60fps minimum (120fps on ProMotion devices)
  • Purpose: Every animation guides, informs, or delights (never decorative)
  • Haptics: Pair animations with appropriate haptic feedback

Spring Curve (Default for Most Animations)

// SwiftUI
.animation(.spring(response: 0.3, dampingFraction: 0.7))

// Flutter
Curves.easeOutCubic (closest equivalent)

Durations

  • Instant: 100ms (button press)
  • Fast: 150-200ms (micro-interactions, selection feedback)
  • Standard: 300ms (page transitions, modals)
  • Deliberate: 500-600ms (celebration animations)
  • Continuous: 1-1.5s (loading states, shimmers)

Common Animations

Modal/Bottom Sheet Appear:
  • Duration: 300ms
  • Transform: Slide up from bottom (translateY: +100% → 0)
  • Opacity: 0 → 1
  • Backdrop: Fade in 40% black (0 → 0.4)
  • Curve: ease-out
Button Press:
  • Duration: 100ms
  • Scale: 1.0 → 0.98 → 1.0
  • Curve: ease-in-out
  • Haptic: Light impact
Card Selection:
  • Duration: 150ms
  • Border: 1pt → 2pt, color change
  • Scale: 1.0 → 0.98 (press)
  • Haptic: Light impact
Loading Shimmer:
  • Duration: 1.5s continuous loop
  • Gradient: White highlight sweeps left to right
  • Curve: linear (continuous)
Success Celebration:
  • Duration: 600ms
  • Checkmark: Scale 0 → 1.2 → 1.0 (bounce)
  • Rotate: -5° → +5° → 0° (wiggle)
  • Confetti: Particle burst (optional)
  • Haptic: Success notification

Flutter Implementation Notes

Design System Compatibility

All components designed for Flutter with Cupertino widgets (iOS-native feel): Key Flutter Widgets Used:
  • CupertinoButton: Primary/Secondary buttons
  • CupertinoNavigationBar: Navigation bar
  • CupertinoTabBar: Not used in this flow (Timeline uses it)
  • CupertinoTextField: Text input fields
  • CupertinoActionSheet: Bottom sheets (Screen 1)
  • CupertinoAlertDialog: Confirmation dialogs
  • CupertinoActivityIndicator: Loading spinners
  • PageView: Not used in this flow (but available for swipe gestures)
Custom Components:
  • Emotion Chips: Custom Flutter widgets (circular badges with icons)
  • Prompt Cards: Custom Container with BoxDecoration
  • Photo Editor: Use plugin like image_cropper or crop_your_image
  • Camera: Use camera plugin for native camera integration
  • Photo Picker: Use image_picker plugin (native PHPickerViewController on iOS)

Camera Implementation (Screen 2)

// Use camera plugin
import 'package:camera/camera.dart';

// Initialize camera
final cameras = await availableCameras();
final camera = cameras.first; // Rear camera

// Camera controller
final controller = CameraController(
  camera,
  ResolutionPreset.high,
  enableAudio: false,
);

// Take photo
final image = await controller.takePicture();

Photo Picker Implementation (Screen 4)

// Use image_picker plugin (wraps PHPickerViewController)
import 'package:image_picker/image_picker.dart';

final picker = ImagePicker();
final image = await picker.pickImage(
  source: ImageSource.gallery,
  maxWidth: 2000,
  maxHeight: 2000,
);

AI Prompt Loading Animation (Screen 6)

// Shimmer animation
class ShimmerPlaceholder extends StatefulWidget {
  @override
  _ShimmerPlaceholderState createState() => _ShimmerPlaceholderState();
}

class _ShimmerPlaceholderState extends State<ShimmerPlaceholder>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: Duration(milliseconds: 1500),
      vsync: this,
    )..repeat();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) {
        return Container(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              colors: [
                Colors.grey[300]!,
                Colors.grey[100]!,
                Colors.grey[300]!,
              ],
              stops: [
                _controller.value - 0.3,
                _controller.value,
                _controller.value + 0.3,
              ],
            ),
          ),
        );
      },
    );
  }
}

Design Tokens in Dart

// lib/theme/colors.dart
class AppColors {
  // Primary
  static const primaryCoralLight = Color(0xFFFF6B6B);
  static const primaryCoralDark = Color(0xFFFF8787);

  // Emotions (12 colors)
  static const emotionHappy = Color(0xFFFF6B6B);
  static const emotionGrateful = Color(0xFFFF8FA3);
  // ... all 12 emotion colors

  // Neutral
  static const textPrimaryLight = Color(0xFF2D3436);
  static const textPrimaryDark = Color(0xFFF5F6FA);
  // ... all neutral colors
}

// lib/theme/typography.dart
class AppTypography {
  static const largeTitle = TextStyle(
    fontFamily: 'SF Pro Display',
    fontSize: 34,
    fontWeight: FontWeight.bold,
    letterSpacing: 0.37,
  );

  static const title2 = TextStyle(
    fontFamily: 'SF Pro Display',
    fontSize: 22,
    fontWeight: FontWeight.bold,
    letterSpacing: 0.35,
  );

  // ... all text styles
}

// lib/theme/spacing.dart
class AppSpacing {
  static const xs = 8.0;
  static const sm = 12.0;
  static const md = 16.0;
  static const lg = 24.0;
  static const xl = 32.0;
  static const xxl = 48.0;
}

Responsive Layout in Flutter

// Use MediaQuery for breakpoints
final width = MediaQuery.of(context).size.width;

if (width < 360) {
  // iPhone SE layout
} else if (width > 410) {
  // iPhone Pro Max layout
} else {
  // Default (iPhone 15)
}

Dark Mode in Flutter

// Automatic dark mode switching
final brightness = Theme.of(context).brightness;
final backgroundColor = brightness == Brightness.dark
    ? AppColors.backgroundPrimaryDark
    : AppColors.backgroundPrimaryLight;

Asset Export Requirements

For @frontend-developer

Illustrations:
  • None required (using native camera, photo picker)
Icons:
  • Use SF Symbols where possible (Flutter plugin: cupertino_icons or flutter_icons)
  • Custom icons: Export as SVG or icon font
  • Sizes needed: 16×16pt, 20×20pt, 24×24pt, 32×32pt, 48×48pt
Emotion Icons (12 total, from design system):
  • Format: Emoji (built-in Unicode) OR custom SVG
  • Recommendation: Use emoji for simplicity (😊❤️🎉😌🤔😢😊😍😔😰😤😌)
  • Sizes: 20×20pt, 24×24pt, 32×32pt
  • Files (if custom): emotion-happy.svg, emotion-grateful.svg, etc.
Camera/Library Icons:
  • Use SF Symbols: camera.fill, photo.on.rectangle.fill
  • Flutter: CupertinoIcons.camera_fill, CupertinoIcons.photo_fill_on_rectangle_fill
No Custom Illustrations Needed:
  • This flow uses native components (camera, photo picker)
  • Success checkmark uses SF Symbol checkmark.circle.fill

Design Handoff Checklist

Deliverables for Approval

  • ✅ This document: Complete UI specifications for 7 screens
  • Figma file: High-fidelity mockups (14 artboards: 7 screens × Light/Dark)
  • Interactive prototype: Clickable flow (Figma Prototype mode)
  • Animation specs: Detailed timing and easing curves (included above)
  • Flutter design tokens: Dart file with colors, typography, spacing
  • Edge case documentation: All error states designed

Review Checklist

  • All 7 screens designed for iPhone 15/16/17 (390-430pt width)
  • Light and Dark mode variants for all screens
  • iOS-native feel maintained (Cupertino widgets, iOS patterns)
  • 60-second target achievable (verified in prototype)
  • AI loading states engaging (shimmer, progress indicators)
  • Emotion selector intuitive (12 emotions, multi-select)
  • Accessibility: VoiceOver labels, Dynamic Type support, WCAG AA contrast
  • Responsive behavior documented for all breakpoints
  • Edge cases handled (permissions denied, AI failure, network errors)
  • Animations specified with timing and easing curves
  • Flutter implementation notes provided
  • Design tokens exported in Dart format

Next Steps After Approval

Phase 3: Timeline & Entry Detail View

After approval of Photo Capture & Entry Creation Flow, design remaining screens: Screens to design (5-6 screens):
  1. Timeline (Grid View) - Main browsing experience
  2. Timeline (Empty State) - First-time user guidance
  3. Entry Detail View - Full entry with photo, text, emotions, metadata
  4. Entry Edit Mode - Edit existing entries
  5. Search & Filter - Keyword search, date filter, emotion filter
  6. Settings - Account, notifications, privacy, help
Estimated timeline: 1 week for design + review

Appendix: Design System Cross-Reference

This UI specification uses the following components from /docs/design/ux/design-system.md: Navigation Components:
  • 1.2 Navigation Bar (Top Bar) - Screen 6
Input Components:
  • 2.1 Text Field (Journal Entry) - Screen 6
  • 2.2 Camera Capture - Screen 2
  • 2.3 Photo Picker (Library) - Screen 4
  • 2.4 Emotion Selector - Screen 6
Action Components:
  • 3.1 Primary Button - All screens
  • 3.2 Secondary Button - Screen 3
  • 3.3 Text Button (Link) - Screen 6 (Refresh prompts)
Content Components:
  • 4.3 AI Prompt Card - Screen 6
  • 4.4 Loading State (Skeleton Screen) - Screen 6
  • 4.6 Error State - Screen 6 (AI failure)
Feedback Components:
  • 5.1 Toast Notification - Screen 6 (multi-select hint)
  • 5.3 Progress Indicator - Screen 6 (AI loading, photo upload)
Custom Components:
  • Bottom Sheet (Screen 1) - Entry Creation source selector
  • Photo Review (Screen 3) - Full-screen photo with action buttons
  • Photo Editor (Screen 5) - Crop and rotate tools
  • Success Celebration (Screen 7) - Animated checkmark with confetti

Approval

Stakeholders:
  • Product Designer (self) - Design completeness, quality
  • UX Designer - Style guide adherence, accessibility
  • Senior Product Manager - PRD alignment, user stories coverage (Stories 1.1-2.4, 3.2-3.4)
  • Frontend Developer (iOS/Flutter) - Technical feasibility, implementability
  • Backend Developer - API requirements (AI prompts, photo upload)
  • Chief Product Officer - Brand alignment, strategic fit
Approval Status: ⏳ Awaiting Review

Revision History

VersionDateChangesAuthor
1.02025-11-10Initial UI specification for Photo Capture & Entry Creation Flow (Phase 2: 7 screens)Product Designer

END OF PHOTO CAPTURE & ENTRY CREATION FLOW UI SPECIFICATION Related Documents: Next Phase: After approval, proceed to Phase 3: Timeline & Entry Detail View (5-6 screens) This is the most important flow in the app - it makes or breaks the 60-second target and the entire product experience. Every detail has been designed to feel effortless and delightful.