Skip to main content

UI Specification: Settings & Profile (Phase 7 - FINAL)

Date: 2025-11-10 Version: 1.0 Author: Product Designer Status: Complete - Ready for Development Platform: iOS 15+ (Flutter/Cupertino) Phase: 7 of 7 (FINAL MVP PHASE)

Overview

This is the final phase of the MVP UI design, completing all 28 screens for the AI-Powered Photo Journaling iOS App. Phase 7 delivers the Settings & Profile functionality, giving users control over their account, preferences, and privacy. Screens Designed: 2 screens
  • Settings Screen (Main)
  • Edit Profile Screen
Total MVP Completion: 28/28 screens (100% complete)

Design Goals

  1. iOS-Native Feel: Use grouped list styling (CupertinoFormSection) consistent with iOS Settings app
  2. Clear Organization: Group related settings logically for easy scanning
  3. Data Control: Give users full control over their data, privacy, and account
  4. Accessibility: Full VoiceOver support, Dynamic Type, WCAG 2.1 AA compliance
  5. Safety First: Destructive actions require confirmation dialogs

Screen 1: Settings Screen (Main)

Purpose

Central hub for all user settings, account management, preferences, and app information.

User Flow

Entry Points:
  • Tab Bar → Profile tab (person.circle.fill icon)
  • Entry: User taps Profile icon in bottom tab bar
Exit Points:
  • Edit Profile → Edit Profile screen
  • Change Password → Password change form
  • Subscription → Subscription Options screen (from Phase 6)
  • Export Data → Email with download link
  • Delete Account → Confirmation dialog → Account deletion
  • Sign Out → Confirmation dialog → Welcome screen

Layout Structure

Component Breakdown

1. Navigation Bar

Specifications:
  • Height: 44pt (standard iOS)
  • Background: Transparent with iOS blur effect
  • Title: “Settings” (headline, 17pt semibold, center-aligned)
  • Right Action: Close button (xmark icon, 24×24pt) - dismisses settings modal (if presented modally)
  • Safe Area: Respects status bar inset
Flutter Implementation:
States:
  • Default: Fully visible
  • Scrolling: Becomes translucent with blur
Accessibility:
  • VoiceOver: “Settings, heading level 1”
  • Close button: “Close settings, button”

2. Profile Section

Specifications:
  • Container: CupertinoFormSection with 16pt padding
  • Background: backgroundSurfaceLight (#F9FAFB) / backgroundSurfaceDark (#2C3135)
  • Border Radius: 12pt (medium)
  • Shadow: Level 1 (subtle)
  • Margin: 16pt horizontal, 16pt top
  • Padding: 16pt internal
Layout:
Profile Photo:
  • Size: 64×64pt circle
  • Border: 2pt backgroundPrimaryLight/Dark (creates subtle separation)
  • Placeholder: Person silhouette icon (person.circle.fill, 64×64pt, gray)
  • Tappable: Yes - opens “Change Photo” action sheet (Camera / Library / Remove)
  • Loading State: Circular spinner overlay while uploading
Premium Badge:
  • Component: PremiumBadge from design-system.md
  • Display: Only if user is Premium subscriber
  • Specs: 24pt height, 👑 icon + “Premium” label, golden orange (#FAB1A0) at 15% opacity background
Flutter Implementation:
Interactions:
  • Tap Photo: Opens action sheet with options:
    • “Take Photo” → Opens camera
    • “Choose from Library” → Opens photo picker
    • “Remove Photo” → Confirms removal, reverts to placeholder
    • “Cancel”
States:
  • Default: Photo or placeholder visible
  • Uploading: Spinner overlay (32×32pt)
  • Error: Error icon overlay + retry option
Accessibility:
  • Photo: “Profile photo, button, double-tap to change”
  • Name: “Ryan Thompson”
  • Email: “ryan@example.com
  • Premium Badge: “Premium subscriber”

3. Settings Sections (Grouped Lists)

Section Header:
  • Font: Footnote (13pt regular, uppercase)
  • Color: textSecondaryLight (#636E72) / textSecondaryDark (#B2BEC3)
  • Padding: 16pt horizontal, 8pt top, 4pt bottom
  • Accessibility: VoiceOver announces as “Section header”
Settings Row:
  • Height: 44pt minimum (iOS touch target)
  • Padding: 16pt horizontal, 12pt vertical
  • Background: backgroundSurfaceLight / backgroundSurfaceDark
  • Divider: 0.5pt hairline (borderLight / borderDark) - inset 16pt from left
  • Layout:
    • Leading: Icon (24×24pt, optional) + Label (body, 17pt)
    • Trailing: Value text (callout, 16pt, gray) OR Toggle switch OR Chevron (>)
Row Types: A) Navigation Row (with chevron):
  • Examples: Edit Profile, Change Password, Subscription, Export Data, Help & Support, Privacy Policy, Terms of Service
  • Trailing: chevron.right (12×12pt, gray)
  • Full row is tappable (44pt height minimum)
B) Toggle Row:
  • Example: Notifications (Daily Journal Reminder)
  • Trailing: CupertinoSwitch (51×31pt, coral when active)
  • Switch is tappable, label also tappable for accessibility
C) Picker Row:
  • Examples: Daily Reminder Time, App Theme
  • Shows current value (e.g., “8:00 PM”, “Auto”)
  • Trailing: Value + chevron
  • Tapping opens picker (time picker or segmented control modal)
D) Destructive Row:
  • Example: Delete My Account
  • Text color: errorLight (#D63031) / errorDark (#E74C3C)
  • Trailing: chevron (red)
  • Tapping shows destructive confirmation dialog
E) Info Row (non-interactive):
  • Example: App Version
  • Trailing: Value only (no chevron)
  • Not tappable
Flutter Implementation (Full Section):
Accessibility:
  • Each row: “Edit Profile, button” / “Notifications, switch, on” / “App Version, Version 1.0.0 (1)”
  • VoiceOver reads section headers before rows
  • Focus order: Top to bottom within each section

4. Sign Out Button

Specifications:
  • Position: Below all sections, centered, 24pt top margin
  • Width: Full-width minus 32pt margin (16pt each side)
  • Height: 50pt (standard button height)
  • Background: Transparent (no fill)
  • Border: None
  • Text: “Sign Out” (headline, 17pt semibold)
  • Color: errorLight (#D63031) / errorDark (#E74C3C)
  • Border Radius: 12pt
Flutter Implementation:
Interaction:
  • Tap: Shows confirmation dialog:
    • Title: “Sign Out?”
    • Message: “Are you sure you want to sign out?”
    • Actions: “Cancel” (default), “Sign Out” (destructive, red)
  • Confirmed: Signs user out → navigates to Welcome screen (authentication flow from Phase 5)
States:
  • Default: Red text on transparent background
  • Pressed: Scale 0.98, red background at 10% opacity
  • Disabled: Not applicable (always enabled)
Accessibility:
  • VoiceOver: “Sign Out, button, destructive action”
  • Confirmation dialog: “Alert, Sign Out?, Are you sure you want to sign out?, Cancel button, Sign Out button”

Interactive States & Animations

Profile Photo Upload

Sequence:
  1. User taps photo → Action sheet slides up (0.3s easeOut)
  2. User selects “Take Photo” or “Choose from Library”
  3. Photo picker opens (native transition)
  4. Photo selected → Picker dismisses
  5. Upload begins:
    • Circular progress indicator overlays photo (0.2s fade in)
    • Progress: 0% → 100% (indeterminate spinner or determinate progress ring)
  6. Upload complete:
    • Progress indicator fades out (0.2s)
    • New photo crossfades in (0.3s)
    • Success toast: “Profile photo updated” (top of screen, 3s auto-dismiss)
Error Handling:
  • Upload fails → Error icon overlay + “Retry” button
  • Network error → “Upload failed. Check your connection and try again.”
  • File too large → “Photo must be under 10MB.”

Toggle Switch (Notifications)

Interaction:
  1. User taps switch → Immediate visual feedback (switch animates to on/off)
  2. If enabling notifications AND permission not granted:
    • iOS system permission dialog appears
    • User grants → Switch stays on, Daily Reminder Time row becomes enabled
    • User denies → Switch reverts to off, alert appears: “Enable notifications in Settings to receive daily reminders.”
States:
  • Off: Gray background, white knob on left
  • On: Coral background, white knob on right
  • Disabled: 40% opacity (if notifications permission denied at OS level)

Time Picker (Daily Reminder Time)

Interaction:
  1. User taps “Daily Reminder Time” row
  2. Modal bottom sheet slides up (0.3s easeOut) with:
    • Title: “Daily Reminder Time”
    • CupertinoDatePicker (time mode, 12-hour format, wheel picker)
    • Initial value: Current saved time (default 8:00 PM)
    • Actions: “Cancel” (leading), “Save” (trailing, coral)
  3. User spins picker wheels to select time
  4. User taps “Save”:
    • Picker dismisses (0.3s easeIn)
    • Row updates to show new time immediately
    • Success toast: “Reminder set for 8:00 PM”
  5. User taps “Cancel”:
    • Picker dismisses without saving
Validation:
  • No validation needed (time picker ensures valid time)
  • Notifications toggle must be ON to set time (otherwise show alert)
Flutter Implementation:

App Theme Selection

Interaction:
  1. User taps “App Theme” row
  2. Modal bottom sheet slides up with:
    • Title: “App Theme”
    • CupertinoSegmentedControl with 3 options:
      • “Auto” (follows system setting)
      • “Light” (always light mode)
      • “Dark” (always dark mode)
    • Current selection highlighted in coral
    • “Done” button (coral, top-right)
  3. User taps an option → Immediate preview (app theme changes in real-time)
  4. User taps “Done”:
    • Modal dismisses (0.3s easeIn)
    • Theme preference saved
    • No toast needed (change is obvious)
Flutter Implementation:

Delete Account Confirmation

Sequence:
  1. User taps “Delete My Account” (red text row)
  2. Alert dialog appears (0.2s scale + fade in):
    • Title: “Delete Account?”
    • Message: “This will permanently delete all your entries, photos, and data. This action cannot be undone.”
    • TextField: “Enter your password to confirm” (password field, obscured)
    • Actions:
      • “Cancel” (default style, left)
      • “Delete Account” (destructive style, red, right)
  3. User enters password and taps “Delete Account”:
    • Validate password (show error if incorrect: “Incorrect password”)
    • If valid:
      • Loading spinner overlays dialog
      • API call to delete account (with 30-day soft delete grace period)
      • Success:
        • Dialog dismisses
        • User signed out
        • Navigate to Welcome screen
        • Toast (on Welcome screen): “Your account has been deleted. You have 30 days to recover it by signing in again.”
      • Error:
        • Spinner removed
        • Error alert: “Something went wrong. Please try again.”
Dialog Specifications:
  • Width: 270pt (iOS standard)
  • Padding: 16pt
  • Border Radius: 14pt
  • Background: backgroundSurfaceLight/Dark
  • Overlay: 40% black dim
Flutter Implementation:
Accessibility:
  • Alert announced immediately by VoiceOver
  • Focus moves to password field
  • VoiceOver: “Alert, Delete Account?, This will permanently delete…”
  • Password field: “Enter your password to confirm, secure text field”

Sign Out Confirmation

Sequence:
  1. User taps “Sign Out” button
  2. Alert dialog appears:
    • Title: “Sign Out?”
    • Message: “Are you sure you want to sign out?”
    • Actions:
      • “Cancel” (default)
      • “Sign Out” (destructive, red)
  3. User taps “Sign Out”:
    • Dialog dismisses
    • User session cleared
    • Navigate to Welcome screen (authentication flow)
Dialog Specifications:
  • Same as Delete Account dialog specs
  • Simpler (no password field)
Flutter Implementation:

Responsive Behavior

Portrait (iPhone 12-15, 390pt width):
  • Full layout as shown above
  • Sections stack vertically
  • Horizontal margins: 16pt
  • Profile photo: 64×64pt
Landscape (iPhone rotated):
  • Same layout (vertical scroll)
  • No special landscape adaptations for Settings
Large Text (Dynamic Type AX5):
  • Row heights increase to accommodate larger text
  • Labels may wrap to 2 lines
  • Minimum touch target: 44pt maintained
  • Profile section expands vertically
  • Section headers remain uppercase but scale with Dynamic Type
Small Devices (iPhone SE, 320pt width):
  • Horizontal margins: 12pt (reduce from 16pt)
  • Profile photo: 56×56pt (reduce from 64pt)
  • Tighter spacing where possible
  • All content remains readable

Accessibility

VoiceOver Navigation:
  • Tab to Settings → “Settings, heading level 1”
  • Swipe right through elements:
    • “Profile photo, Ryan Thompson, button, double-tap to change”
    • “Email, ryan@example.com
    • “Premium subscriber”
    • “Account, section header”
    • “Edit Profile, button, double-tap to navigate”
    • “Change Password, button, double-tap to navigate”
    • (Continue through all rows…)
    • “Sign Out, button, destructive action, double-tap to sign out”
Dynamic Type Support:
  • All text uses iOS Dynamic Type styles
  • Test at smallest (xSmall) and largest (AX5) sizes
  • Rows expand to fit larger text
  • No text truncation
Color Contrast:
  • Text on background: 13.1:1 (AAA) for primary text
  • Gray text: 5.74:1 (AA) for secondary text
  • Red text (destructive): 4.5:1 (AA) minimum
  • Switch active color (coral): 4.5:1 on white background
Keyboard Navigation (iPad, future):
  • Tab order: Top to bottom, section by section
  • Focus indicators: 2pt blue ring around focused row
  • Enter key: Activates row (same as tap)
Reduce Motion:
  • Action sheets: Fade in instead of slide up
  • Dialogs: Fade + scale down to 0.9 instead of spring animation
  • Toggle switches: Instant state change (no sliding animation)

Edge Cases & Error Handling

1. Photo Upload Failures:
  • Network Error:
    • Show error icon on photo
    • “Upload failed. Check your connection and try again.”
    • “Retry” button overlays photo
  • File Too Large (>10MB):
    • Alert: “Photo must be under 10MB. Please choose a smaller photo.”
    • Photo not uploaded, reverts to previous photo
  • Invalid Format:
    • Alert: “This file type is not supported. Please use JPG, PNG, or HEIC.”
2. Notifications Permission Denied:
  • At OS Level (user denied in iOS Settings):
    • Toggle switch: Disabled (40% opacity)
    • Tapping toggle shows alert: “Notifications are disabled. Open Settings to enable them.”
    • Alert has “Open Settings” button → deep link to iOS Settings app
  • Never Prompted:
    • Tapping toggle requests iOS permission
    • If granted: Switch turns on, Daily Reminder Time row enabled
    • If denied: Switch stays off, alert shown
3. Delete Account Failures:
  • Incorrect Password:
    • Error below password field: “Incorrect password” (red text)
    • Password field border turns red
    • “Delete Account” button remains enabled (allow retry)
  • Network Error:
    • Alert: “Something went wrong. Please check your connection and try again.”
    • Dialog remains open, user can retry
  • Server Error:
    • Alert: “We couldn’t delete your account. Please contact support.”
    • Link to support email
4. Export Data Failures:
  • Network Error:
    • Alert: “Export failed. Please check your connection and try again.”
    • “Retry” button
  • Success:
    • Alert: “Your data export is ready. We’ve sent a download link to your email.”
    • User receives email with JSON/CSV file + photos ZIP
5. Premium User Navigates to Subscription:
  • Subscription Screen Behavior:
    • Show “Manage Subscription” section (not purchase options)
    • “View Receipt”, “Cancel Subscription”, “Contact Support”
    • Link to App Store subscription management
6. No Internet Connection:
  • Settings screen loads normally (cached user data)
  • Actions requiring network (Export Data, Delete Account) show:
    • Alert: “No internet connection. Please try again when connected.”

Design Tokens Used

Colors:
  • primaryCoralLight / primaryCoralDark - Toggle switch active, Save buttons
  • textPrimaryLight / textPrimaryDark - Row labels, section content
  • textSecondaryLight / textSecondaryDark - Email, value text, section headers
  • backgroundPrimaryLight / backgroundPrimaryDark - Screen background
  • backgroundSurfaceLight / backgroundSurfaceDark - Profile section, settings rows
  • borderLight / borderDark - Row dividers
  • errorLight / errorDark - Delete Account, Sign Out (destructive actions)
Typography:
  • headline (17pt semibold) - Navigation title, row labels, button text, user name
  • callout (16pt regular) - Email, value text, dialog messages
  • footnote (13pt regular) - Section headers (uppercase)
  • body (17pt regular) - Row labels
Spacing:
  • xxs (4pt) - Section header bottom padding
  • xs (8pt) - Section header top padding, premium badge gap
  • sm (12pt) - Profile photo to name gap, reduced margins on iPhone SE
  • md (16pt) - Screen horizontal margins, section internal padding
  • lg (24pt) - Sign Out button top margin
Border Radius:
  • md (12pt) - Profile section, settings sections, buttons
Shadows:
  • level1 - Profile section (subtle elevation)
Animations:
  • standard (300ms) - Action sheet slide up/down, dialog fade in/out
  • fast (200ms) - Profile photo crossfade, progress indicator fade in/out
  • instant (100ms) - Button press, toggle switch

Screen 2: Edit Profile Screen

Purpose

Allow users to update their profile photo, display name, and bio.

User Flow

Entry Point:
  • Settings Screen → Tap “Edit Profile” row
Exit Points:
  • Save → Validates and saves changes → Returns to Settings (with success toast)
  • Cancel (with unsaved changes) → Confirmation dialog → Returns to Settings or stays on screen
  • Cancel (no changes) → Returns to Settings immediately

Layout Structure

Component Breakdown

1. Navigation Bar

Specifications:
  • Height: 44pt
  • Background: Transparent with iOS blur effect
  • Title: “Edit Profile” (headline, 17pt semibold, center)
  • Left Action: “Cancel” button (callout, 16pt, coral)
  • Right Action: “Save” button (callout, 16pt semibold, coral)
Cancel Button:
  • Text: “Cancel”
  • Color: primaryCoralLight / primaryCoralDark
  • Behavior:
    • If no changes made: Dismiss immediately
    • If unsaved changes: Show confirmation dialog:
      • Title: “Discard Changes?”
      • Message: “Your changes will be lost if you don’t save them.”
      • Actions: “Keep Editing” (default), “Discard” (destructive)
Save Button:
  • Text: “Save”
  • Color: primaryCoralLight / primaryCoralDark (enabled), gray (disabled)
  • States:
    • Disabled: No changes made OR display name empty (validation fail)
    • Enabled: Changes made AND display name valid
    • Loading: Replaced with spinner (20×20pt) while saving
Flutter Implementation:
Accessibility:
  • Cancel: “Cancel, button, double-tap to cancel editing”
  • Save: “Save, button, enabled/disabled, double-tap to save changes”
  • Title: “Edit Profile, heading level 1”

2. Profile Photo Section

Specifications:
  • Photo Size: 120×120pt circle (larger than Settings screen for better editing visibility)
  • Position: Center-aligned horizontally, 32pt from top of content area
  • Border: 2pt backgroundPrimaryLight/Dark (subtle separation)
  • Placeholder: Person silhouette icon (person.circle.fill, 120×120pt, gray)
Change Photo Button:
  • Position: 16pt below photo, center-aligned
  • Text: “Change Photo” (callout, 16pt, coral)
  • Background: Transparent (text button)
  • Height: 44pt (touch target)
Interaction:
  1. User taps “Change Photo”
  2. Action sheet slides up (0.3s easeOut):
    • Title: “Change Profile Photo”
    • Options:
      • “Take Photo” → Opens camera
      • “Choose from Library” → Opens photo picker (PHPickerViewController)
      • “Remove Photo” → Confirms removal, reverts to placeholder
      • “Cancel” (destructive appearance, but not destructive action)
  3. User selects photo:
    • Photo picker dismisses
    • Selected photo displays in circle (cropped to square if needed)
    • Photo NOT uploaded yet (upload happens on Save)
    • UI marks changes as unsaved (enables Save button)
Flutter Implementation:
States:
  • Default: Current photo or placeholder
  • Selected (not saved): New photo displayed, Save button enabled
  • Uploading: Spinner overlay (32×32pt, white on 50% black background)
  • Error: Error icon overlay + retry option
Accessibility:
  • Photo: “Profile photo, current photo: Ryan Thompson, button, double-tap to change”
  • Change Photo button: “Change Photo, button, double-tap to select new photo”

3. Display Name Field

Specifications:
  • Section Header: “Display Name” (footnote, 13pt regular, uppercase, gray)
  • Text Field:
    • Height: 44pt
    • Padding: 12pt horizontal, 8pt vertical
    • Border: 1pt solid borderLight / borderDark
    • Border Radius: 8pt (small)
    • Background: backgroundSurfaceLight / backgroundSurfaceDark
    • Font: body (17pt regular)
    • Placeholder: “Your name” (gray)
    • Max Length: 50 characters
  • Helper Text: “This name will appear on your profile.” (footnote, 13pt, gray, 4pt below field)
Validation:
  • Required: Cannot be empty
  • Max Length: 50 characters
  • Real-time Validation:
    • Empty: Save button disabled
    • Valid (1-50 chars): Save button enabled (if changes made)
    • Invalid (>50 chars): Character count turns red, Save button disabled
Flutter Implementation:
States:
  • Default: Border gray, no error
  • Focus: Border coral (2pt)
  • Error (empty after blur): Border red, error text “Name is required”
  • Valid: Border gray (green checkmark NOT shown - keep clean)
Accessibility:
  • VoiceOver: “Display Name, text field, double-tap to edit, Ryan Thompson”
  • Helper text read after label
  • Error announced immediately when validation fails

4. Bio Field (Optional)

Specifications:
  • Section Header: “Bio (Optional)” (footnote, 13pt regular, uppercase, gray)
  • Text Field (Multiline):
    • Min Height: 80pt (3-4 lines)
    • Max Height: 120pt (scrollable if longer)
    • Padding: 12pt horizontal, 8pt vertical
    • Border: 1pt solid borderLight / borderDark
    • Border Radius: 8pt
    • Background: backgroundSurfaceLight / backgroundSurfaceDark
    • Font: body (17pt regular)
    • Placeholder: “Tell us about yourself…” (gray, multiline)
    • Max Length: 150 characters
  • Character Count: “0 / 150” (footnote, 13pt, gray, 4pt below field)
    • Shows at all times (not just near limit)
    • Turns red when approaching limit (>145 chars)
    • Prevents input at 150 chars
Flutter Implementation:
States:
  • Default: Border gray, counter “0 / 150”
  • Focus: Border coral (2pt)
  • Typing: Counter updates in real-time
  • Near Limit (>145 chars): Counter text turns red
  • At Limit (150 chars): No more input allowed, counter red
Accessibility:
  • VoiceOver: “Bio, optional, text field, multiline, double-tap to edit”
  • Character count announced as “142 of 150 characters”
  • Focus: Cursor placed at end of existing text

Interactive States & Animations

Save Profile Flow

Sequence:
  1. User taps “Save” button (top-right)
  2. Validation runs:
    • Display name: Not empty, max 50 chars → Pass
    • Bio: Max 150 chars (optional) → Pass
  3. If validation passes:
    • Save button replaced with spinner (20×20pt, 0.2s crossfade)
    • API calls:
      • If photo changed: Upload photo to cloud storage (S3/GCS)
      • Update profile (name, bio, photo URL)
    • Success (within 2-3 seconds):
      • Spinner fades out (0.2s)
      • Screen dismisses with transition (0.3s slide down)
      • Settings screen updates profile section immediately
      • Success toast appears (top): “Profile updated” (3s auto-dismiss)
  4. If validation fails:
    • Shake animation on invalid field (0.3s)
    • Error text appears below field
    • Focus moves to first invalid field
    • Save button remains enabled (allow fixing and retry)
Error Handling:
  • Network Error:
    • Spinner removed
    • Alert: “Update failed. Please check your connection and try again.”
    • “Retry” and “Cancel” buttons
  • Photo Upload Fails:
    • Alert: “Photo upload failed. Your name and bio were saved.”
    • User can retry photo upload from Settings screen
  • Server Error:
    • Alert: “Something went wrong. Please try again later.”
Flutter Implementation:

Cancel with Unsaved Changes

Sequence:
  1. User taps “Cancel” (top-left)
  2. Check for unsaved changes:
    • Compare current values with initial values
    • If identical: Dismiss immediately (no dialog)
    • If different: Show confirmation dialog
  3. Dialog appears (0.2s scale + fade):
    • Title: “Discard Changes?”
    • Message: “Your changes will be lost if you don’t save them.”
    • Actions:
      • “Keep Editing” (default, left)
      • “Discard” (destructive, red, right)
  4. User taps “Discard”:
    • Dialog dismisses
    • Screen dismisses (returns to Settings)
    • No changes saved
  5. User taps “Keep Editing”:
    • Dialog dismisses
    • Remains on Edit Profile screen
Flutter Implementation:

Photo Upload Progress

Sequence:
  1. User selects photo from picker → Photo displays in UI (not uploaded yet)
  2. User taps Save:
    • Photo upload begins
    • Progress indicator overlays photo (circular spinner or progress ring)
    • Progress: 0% → 100% (if determinate, show percentage; if indeterminate, spinner)
  3. Upload complete:
    • Progress indicator fades out (0.2s)
    • Photo remains displayed
    • Continue with profile update API call
  4. Upload fails:
    • Progress indicator replaced with error icon
    • Alert: “Photo upload failed. Please try again.”
    • User can retry
Flutter Implementation:

Responsive Behavior

Portrait (iPhone 12-15, 390pt width):
  • Full layout as shown
  • Horizontal margins: 16pt
  • Profile photo: 120×120pt
Landscape (iPhone rotated):
  • Same layout (vertical scroll)
  • No special landscape adaptations
Large Text (Dynamic Type AX5):
  • Field heights increase to accommodate larger text
  • Labels scale with Dynamic Type
  • Helper text may wrap to 2 lines
  • Character counts remain inline (not wrapped)
Small Devices (iPhone SE, 320pt width):
  • Horizontal margins: 12pt
  • Profile photo: 100×100pt (reduce from 120pt)
  • Tighter spacing

Accessibility

VoiceOver Navigation:
  • Swipe right through elements:
    • “Edit Profile, heading level 1”
    • “Cancel, button, double-tap to cancel”
    • “Save, button, disabled/enabled, double-tap to save”
    • “Profile photo, Ryan Thompson, button, double-tap to change”
    • “Change Photo, button, double-tap to select new photo”
    • “Display Name, text field, Ryan Thompson, double-tap to edit”
    • “This name will appear on your profile”
    • “Bio, optional, text field, multiline, double-tap to edit”
    • “142 of 150 characters”
Dynamic Type Support:
  • All text uses iOS Dynamic Type
  • Fields expand to fit larger text
  • Test at AX5 (largest size)
Color Contrast:
  • All text meets WCAG 2.1 AA (4.5:1 minimum)
  • Error text (red): 4.5:1 on background
  • Placeholder text (gray): 4.5:1 on background
Keyboard Navigation (iPad):
  • Tab order: Photo → Change Photo → Display Name → Bio → Save
  • Enter key: Saves profile (if valid)
  • Escape key: Cancels (with confirmation if unsaved changes)
Reduce Motion:
  • Dialogs: Fade in instead of scale
  • Screen transitions: Fade instead of slide
  • Field shake: Skip animation, just show error text

Edge Cases & Error Handling

1. Empty Display Name:
  • Validation: Real-time validation after blur
  • Error: “Name is required” (red text below field)
  • Save Button: Disabled until valid
2. Display Name Too Long (>50 chars):
  • Validation: Real-time character count
  • Prevention: Text field enforces maxLength (Flutter handles)
  • UI: Character counter doesn’t show (enforced at input level)
3. Bio Too Long (>150 chars):
  • Validation: Real-time character count
  • Prevention: Text field enforces maxLength
  • UI: Counter turns red at >145 chars
4. Photo Upload Fails:
  • Error: Alert: “Photo upload failed. Your name and bio were saved.”
  • Retry: User can retry from Settings screen (Change Photo)
  • Fallback: Profile updated without new photo
5. Network Error During Save:
  • Error: Alert: “Update failed. Please check your connection and try again.”
  • Retry: “Retry” button in alert
  • Cancel: “Cancel” button dismisses alert, stays on Edit Profile
6. User Changes Photo Multiple Times:
  • Behavior: Only the most recent selection is kept
  • Upload: Only upload once on Save (not on each selection)
7. User Taps Cancel Then Discard:
  • Behavior: All changes lost, returns to Settings
  • Photo: Selected photo not uploaded, reverts to original
8. User Saves With Same Values:
  • Behavior: API call still made (in case of sync issues)
  • UI: Success toast shown
  • Optimization (future): Detect no changes, skip API call

Design Tokens Used

Colors:
  • primaryCoralLight / primaryCoralDark - Save button, Change Photo button, focus borders
  • textPrimaryLight / textPrimaryDark - Field text, labels
  • textSecondaryLight / textSecondaryDark - Helper text, character count, section headers
  • backgroundPrimaryLight / backgroundPrimaryDark - Screen background
  • backgroundSurfaceLight / backgroundSurfaceDark - Text fields
  • borderLight / borderDark - Field borders
  • errorLight / errorDark - Error text, Discard button
Typography:
  • headline (17pt semibold) - Navigation title, Save button
  • callout (16pt regular) - Cancel button, Change Photo button, helper text, field values
  • footnote (13pt regular) - Section headers (uppercase), character count, helper text
  • body (17pt regular) - Text field input
Spacing:
  • xxs (4pt) - Helper text top margin, character count top margin
  • xs (8pt) - Section header bottom margin
  • sm (12pt) - Text field padding (horizontal), reduced margins on iPhone SE
  • md (16pt) - Screen horizontal margins, profile photo bottom margin, section vertical spacing
  • xl (32pt) - Profile photo top margin
Border Radius:
  • sm (8pt) - Text fields
Border Width:
  • thin (1pt) - Text field borders
  • medium (2pt) - Focus borders
Animations:
  • instant (100ms) - Button press
  • fast (200ms) - Spinner crossfade, progress indicator fade
  • standard (300ms) - Screen dismiss, dialog appear, field shake

Additional Specifications

Photo Upload Flow (Camera / Library)

Action Sheet Options:
Camera Flow:
  1. Action sheet → “Take Photo” → Dismiss sheet
  2. Camera opens (iOS native UIImagePickerController or image_picker package)
  3. User takes photo → Photo captured
  4. Camera dismisses
  5. Photo displays in Edit Profile screen (cropped to square circle)
  6. UI marks changes as unsaved
Library Flow:
  1. Action sheet → “Choose from Library” → Dismiss sheet
  2. Photo picker opens (PHPickerViewController or image_picker)
  3. User selects photo
  4. Picker dismisses
  5. Photo displays in Edit Profile screen (cropped to square)
  6. UI marks changes as unsaved
Remove Photo Flow:
  1. Action sheet → “Remove Photo” (red) → Dismiss sheet
  2. Photo immediately removed (reverts to placeholder silhouette)
  3. UI marks changes as unsaved
  4. On Save: Photo URL set to null in database
Photo Processing:
  • Crop: Square crop (center-aligned) if aspect ratio not 1:1
  • Resize: Max 1024×1024px (reduce file size)
  • Compression: JPEG quality 85% (balance quality and size)
  • Format: JPEG (convert HEIC/PNG if needed)
  • Max Size: 10MB (before compression)
Flutter Implementation (Camera):
Permissions:
  • Camera: Request on first use via iOS system dialog
  • Photo Library: Request on first use
  • Permission Denied:
    • Camera: Alert “Camera access is required. Please enable it in Settings.”
    • Library: Alert “Photo library access is required. Please enable it in Settings.”
    • Both alerts have “Open Settings” button (deep link to iOS Settings)

Settings Screen: Additional Flows

Daily Reminder Time Picker (Detailed)

Modal Specifications:
  • Height: 300pt (fixed)
  • Background: backgroundSurfaceLight / backgroundSurfaceDark
  • Border Radius: 12pt (top corners only)
  • Position: Bottom of screen (sheet slides up from bottom)
Layout:
Interaction:
  1. User taps “Daily Reminder Time” row on Settings screen
  2. Sheet slides up (0.3s easeOut)
  3. Picker displays current time (or default 8:00 PM if never set)
  4. User spins wheels to select time
  5. Options:
    • Save: Validates time, saves to preferences, dismisses sheet (0.3s easeIn), updates Settings row, shows success toast “Reminder set for 8:00 PM”
    • Cancel: Dismisses sheet without saving
Validation:
  • No validation needed (picker ensures valid time)
  • Notifications must be enabled:
    • If notifications OFF: Alert “Enable notifications first to set a reminder time.”
Flutter Implementation:
Accessibility:
  • Header: “Daily Reminder Time, heading”
  • Picker: VoiceOver announces hours, minutes, AM/PM as user swipes
  • Save: “Save, button, double-tap to save time”
  • Cancel: “Cancel, button, double-tap to cancel”

App Theme Selection (Detailed)

Modal Specifications:
  • Height: 220pt (fixed)
  • Background: backgroundSurfaceLight / backgroundSurfaceDark
  • Border Radius: 12pt (top corners)
  • Position: Bottom sheet
Layout:
Interaction:
  1. User taps “App Theme” row on Settings screen
  2. Sheet slides up (0.3s easeOut)
  3. Segmented control displays current selection (highlighted in coral)
  4. User taps a segment:
    • Auto: App theme follows iOS system setting (light/dark mode)
    • Light: App always uses light theme
    • Dark: App always uses dark theme
    • Immediate preview: App theme changes in real-time (no Save needed)
  5. User taps “Done”: Sheet dismisses (0.3s easeIn)
Flutter Implementation:
States:
  • Auto: Selected (coral background), app follows system theme
  • Light: Selected, app always light
  • Dark: Selected, app always dark
Accessibility:
  • Segmented control: “Theme mode, segmented control, Auto selected, Light, Dark”
  • Each segment: “Auto, button, selected” / “Light, button, not selected”
  • Helper text read after selection

Export My Data Flow

Sequence:
  1. User taps “Export My Data” row on Settings screen
  2. Alert appears:
    • Title: “Export Your Data”
    • Message: “We’ll send a download link to your email with all your journal entries and photos.”
    • Actions: “Cancel”, “Export”
  3. User taps “Export”:
    • Alert dismisses
    • Loading indicator appears on “Export My Data” row (spinner, trailing)
    • API call to generate export (backend creates JSON/CSV + ZIP of photos)
    • Success (within 10-30 seconds):
      • Spinner removed
      • Success toast: “Export ready! Check your email for the download link.”
      • User receives email with:
        • Subject: “Your Journal Export is Ready”
        • Body: “Your data export is ready. Click the link below to download. This link expires in 7 days.”
        • Link: Signed download URL (expires in 7 days)
    • Error:
      • Spinner removed
      • Alert: “Export failed. Please try again.”
Export Contents:
  • JSON File: All entries with metadata:
  • CSV File (alternative): Same data in CSV format
  • Photos ZIP: All photos in original resolution (organized by date)
Flutter Implementation:
Accessibility:
  • Export row: “Export My Data, button, double-tap to export”
  • During export: “Export My Data, button, exporting, loading”
  • Alert announced immediately when displayed

Delete Account Flow (Full Sequence)

Sequence:
  1. User taps “Delete My Account” row (red text) on Settings screen
  2. First Alert (Warning):
    • Title: “Delete Account?”
    • Message: “This will permanently delete all your entries, photos, and data. This action cannot be undone.”
    • Actions: “Cancel” (default), “Continue” (destructive, red)
  3. User taps “Continue”
  4. Second Alert (Password Confirmation):
    • Title: “Enter Password”
    • Message: “To confirm deletion, please enter your password.”
    • TextField: Password field (obscured)
    • Actions: “Cancel”, “Delete Account” (destructive, red)
  5. User enters password and taps “Delete Account”:
    • Validation:
      • If password incorrect: Error below field “Incorrect password”, allow retry
      • If password correct: Proceed
    • Loading spinner overlays dialog
    • API call to delete account:
      • Backend marks account for deletion (30-day soft delete)
      • Deletes user session
    • Success:
      • Dialog dismisses
      • User signed out
      • Navigate to Welcome screen
      • Toast on Welcome screen: “Your account has been deleted. You have 30 days to recover it by signing in again.”
    • Error:
      • Spinner removed
      • Alert: “Something went wrong. Please contact support.”
Flutter Implementation:
Accessibility:
  • Both alerts announced immediately
  • Password field: “Password, secure text field, double-tap to edit”
  • Error announced when password incorrect
  • Loading state: “Deleting account, loading”

Summary: Phase 7 Complete

Screens Delivered: 2
  1. Settings Screen (Main) - 9 sections, 14+ interactive elements
  2. Edit Profile Screen - Photo upload, 2 form fields, save/cancel
Key Features:
  • ✅ Account management (edit profile, change password, delete account)
  • ✅ App settings (notifications, daily reminder time, theme selection)
  • ✅ Data & privacy (export data, delete account)
  • ✅ Help & support (FAQ, privacy policy, terms)
  • ✅ Sign out functionality
  • ✅ Profile photo upload (camera/library)
  • ✅ Form validation (name required, bio max length)
  • ✅ Unsaved changes confirmation
  • ✅ iOS-native feel (grouped lists, segmented controls, action sheets, alerts)
Total MVP UI Design: 28/28 screens (100% complete)

Next Steps for Development

Priority Order:

  1. Settings Screen: Implement all rows, sections, and navigation
  2. Edit Profile Screen: Implement form, photo upload, validation
  3. Time Picker Modal: Daily reminder time selection
  4. Theme Picker Modal: App theme selection (auto/light/dark)
  5. Export Data Flow: Backend integration for data export
  6. Delete Account Flow: Multi-step confirmation with password verification
  7. Sign Out Flow: Session management and navigation

Critical Integrations:

  • Photo Upload: Use image_picker package for camera/library access
  • Local Notifications: Use flutter_local_notifications for daily reminders
  • Theme Management: Use provider or riverpod for app-wide theme state
  • API Endpoints:
    • PATCH /api/users/profile - Update profile
    • POST /api/users/profile/photo - Upload photo
    • POST /api/users/export - Request data export
    • DELETE /api/users/account - Delete account (soft delete)

Testing Requirements:

  • ✅ All toggle switches functional
  • ✅ Time picker saves correctly and schedules notifications
  • ✅ Theme changes apply immediately
  • ✅ Photo upload/remove works on all devices
  • ✅ Form validation prevents empty name
  • ✅ Unsaved changes confirmation shows when needed
  • ✅ Delete account requires password and shows confirmations
  • ✅ Sign out clears session and returns to Welcome
  • ✅ VoiceOver navigation works for all elements
  • ✅ Dynamic Type scales properly at all sizes

END OF PHASE 7 UI SPECIFICATION Status: Ready for @frontend-developer handoff Next Agent: @software-architect (final architecture review) → @frontend-developer (implementation)

Handoff to @frontend-developer

Implementation Notes

Flutter Packages Needed:
File Structure:
Implementation Priority:
  1. Settings screen layout (grouped lists)
  2. Edit Profile screen (form + validation)
  3. Photo upload functionality (camera/library)
  4. Theme selection (immediate preview)
  5. Notifications (permission + scheduling)
  6. Data export (API integration)
  7. Delete account (multi-step confirmation)
Design QA Checklist:
  • All spacing matches design tokens
  • All colors match design tokens (light + dark mode)
  • All typography matches (Dynamic Type support)
  • All animations use correct durations and curves
  • All interactive states work (default, pressed, disabled, loading, error)
  • All edge cases handled (network errors, permission denied, validation failures)
  • VoiceOver navigation tested
  • Dynamic Type tested at AX5 (largest size)
  • Reduce Motion respected
Good luck with the final MVP implementation! 🎉