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
Design Goals
- iOS-Native Feel: Use grouped list styling (CupertinoFormSection) consistent with iOS Settings app
- Clear Organization: Group related settings logically for easy scanning
- Data Control: Give users full control over their data, privacy, and account
- Accessibility: Full VoiceOver support, Dynamic Type, WCAG 2.1 AA compliance
- 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
- 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
- Default: Fully visible
- Scrolling: Becomes translucent with blur
- 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
- 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
- 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
- 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”
- Default: Photo or placeholder visible
- Uploading: Spinner overlay (32×32pt)
- Error: Error icon overlay + retry option
- Photo: “Profile photo, button, double-tap to change”
- Name: “Ryan Thompson”
- Email: “[email protected]”
- 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”
- 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 (>)
- 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)
- Example: Notifications (Daily Journal Reminder)
- Trailing: CupertinoSwitch (51×31pt, coral when active)
- Switch is tappable, label also tappable for accessibility
- 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)
- Example: Delete My Account
- Text color: errorLight (#D63031) / errorDark (#E74C3C)
- Trailing: chevron (red)
- Tapping shows destructive confirmation dialog
- Example: App Version
- Trailing: Value only (no chevron)
- Not tappable
- 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
- 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)
- Default: Red text on transparent background
- Pressed: Scale 0.98, red background at 10% opacity
- Disabled: Not applicable (always enabled)
- 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:- User taps photo → Action sheet slides up (0.3s easeOut)
- User selects “Take Photo” or “Choose from Library”
- Photo picker opens (native transition)
- Photo selected → Picker dismisses
- Upload begins:
- Circular progress indicator overlays photo (0.2s fade in)
- Progress: 0% → 100% (indeterminate spinner or determinate progress ring)
- 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)
- 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:- User taps switch → Immediate visual feedback (switch animates to on/off)
- 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.”
- 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:- User taps “Daily Reminder Time” row
- 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)
- User spins picker wheels to select time
- User taps “Save”:
- Picker dismisses (0.3s easeIn)
- Row updates to show new time immediately
- Success toast: “Reminder set for 8:00 PM”
- User taps “Cancel”:
- Picker dismisses without saving
- No validation needed (time picker ensures valid time)
- Notifications toggle must be ON to set time (otherwise show alert)
App Theme Selection
Interaction:- User taps “App Theme” row
- 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)
- User taps an option → Immediate preview (app theme changes in real-time)
- User taps “Done”:
- Modal dismisses (0.3s easeIn)
- Theme preference saved
- No toast needed (change is obvious)
Delete Account Confirmation
Sequence:- User taps “Delete My Account” (red text row)
- 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)
- 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.”
- Width: 270pt (iOS standard)
- Padding: 16pt
- Border Radius: 14pt
- Background: backgroundSurfaceLight/Dark
- Overlay: 40% black dim
- 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:- User taps “Sign Out” button
- Alert dialog appears:
- Title: “Sign Out?”
- Message: “Are you sure you want to sign out?”
- Actions:
- “Cancel” (default)
- “Sign Out” (destructive, red)
- User taps “Sign Out”:
- Dialog dismisses
- User session cleared
- Navigate to Welcome screen (authentication flow)
- Same as Delete Account dialog specs
- Simpler (no password field)
Responsive Behavior
Portrait (iPhone 12-15, 390pt width):- Full layout as shown above
- Sections stack vertically
- Horizontal margins: 16pt
- Profile photo: 64×64pt
- Same layout (vertical scroll)
- No special landscape adaptations for Settings
- 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
- 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, [email protected]”
- “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”
- All text uses iOS Dynamic Type styles
- Test at smallest (xSmall) and largest (AX5) sizes
- Rows expand to fit larger text
- No text truncation
- 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
- Tab order: Top to bottom, section by section
- Focus indicators: 2pt blue ring around focused row
- Enter key: Activates row (same as tap)
- 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.”
- 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
- 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
- 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
- Subscription Screen Behavior:
- Show “Manage Subscription” section (not purchase options)
- “View Receipt”, “Cancel Subscription”, “Contact Support”
- Link to App Store subscription management
- 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 buttonstextPrimaryLight/textPrimaryDark- Row labels, section contenttextSecondaryLight/textSecondaryDark- Email, value text, section headersbackgroundPrimaryLight/backgroundPrimaryDark- Screen backgroundbackgroundSurfaceLight/backgroundSurfaceDark- Profile section, settings rowsborderLight/borderDark- Row dividerserrorLight/errorDark- Delete Account, Sign Out (destructive actions)
headline(17pt semibold) - Navigation title, row labels, button text, user namecallout(16pt regular) - Email, value text, dialog messagesfootnote(13pt regular) - Section headers (uppercase)body(17pt regular) - Row labels
xxs(4pt) - Section header bottom paddingxs(8pt) - Section header top padding, premium badge gapsm(12pt) - Profile photo to name gap, reduced margins on iPhone SEmd(16pt) - Screen horizontal margins, section internal paddinglg(24pt) - Sign Out button top margin
md(12pt) - Profile section, settings sections, buttons
level1- Profile section (subtle elevation)
standard(300ms) - Action sheet slide up/down, dialog fade in/outfast(200ms) - Profile photo crossfade, progress indicator fade in/outinstant(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
- 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)
- 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)
- 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
- 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)
- Position: 16pt below photo, center-aligned
- Text: “Change Photo” (callout, 16pt, coral)
- Background: Transparent (text button)
- Height: 44pt (touch target)
- User taps “Change Photo”
- 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)
- 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)
- 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
- 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)
- 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
- 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)
- 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
- 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
- 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:- User taps “Save” button (top-right)
- Validation runs:
- Display name: Not empty, max 50 chars → Pass
- Bio: Max 150 chars (optional) → Pass
- 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)
- 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)
- 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.”
Cancel with Unsaved Changes
Sequence:- User taps “Cancel” (top-left)
- Check for unsaved changes:
- Compare current values with initial values
- If identical: Dismiss immediately (no dialog)
- If different: Show confirmation dialog
- 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)
- User taps “Discard”:
- Dialog dismisses
- Screen dismisses (returns to Settings)
- No changes saved
- User taps “Keep Editing”:
- Dialog dismisses
- Remains on Edit Profile screen
Photo Upload Progress
Sequence:- User selects photo from picker → Photo displays in UI (not uploaded yet)
- 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)
- Upload complete:
- Progress indicator fades out (0.2s)
- Photo remains displayed
- Continue with profile update API call
- Upload fails:
- Progress indicator replaced with error icon
- Alert: “Photo upload failed. Please try again.”
- User can retry
Responsive Behavior
Portrait (iPhone 12-15, 390pt width):- Full layout as shown
- Horizontal margins: 16pt
- Profile photo: 120×120pt
- Same layout (vertical scroll)
- No special landscape adaptations
- 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)
- 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”
- All text uses iOS Dynamic Type
- Fields expand to fit larger text
- Test at AX5 (largest size)
- 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
- Tab order: Photo → Change Photo → Display Name → Bio → Save
- Enter key: Saves profile (if valid)
- Escape key: Cancels (with confirmation if unsaved changes)
- 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
- Validation: Real-time character count
- Prevention: Text field enforces maxLength (Flutter handles)
- UI: Character counter doesn’t show (enforced at input level)
- Validation: Real-time character count
- Prevention: Text field enforces maxLength
- UI: Counter turns red at >145 chars
- 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
- 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
- Behavior: Only the most recent selection is kept
- Upload: Only upload once on Save (not on each selection)
- Behavior: All changes lost, returns to Settings
- Photo: Selected photo not uploaded, reverts to original
- 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 borderstextPrimaryLight/textPrimaryDark- Field text, labelstextSecondaryLight/textSecondaryDark- Helper text, character count, section headersbackgroundPrimaryLight/backgroundPrimaryDark- Screen backgroundbackgroundSurfaceLight/backgroundSurfaceDark- Text fieldsborderLight/borderDark- Field borderserrorLight/errorDark- Error text, Discard button
headline(17pt semibold) - Navigation title, Save buttoncallout(16pt regular) - Cancel button, Change Photo button, helper text, field valuesfootnote(13pt regular) - Section headers (uppercase), character count, helper textbody(17pt regular) - Text field input
xxs(4pt) - Helper text top margin, character count top marginxs(8pt) - Section header bottom marginsm(12pt) - Text field padding (horizontal), reduced margins on iPhone SEmd(16pt) - Screen horizontal margins, profile photo bottom margin, section vertical spacingxl(32pt) - Profile photo top margin
sm(8pt) - Text fields
thin(1pt) - Text field bordersmedium(2pt) - Focus borders
instant(100ms) - Button pressfast(200ms) - Spinner crossfade, progress indicator fadestandard(300ms) - Screen dismiss, dialog appear, field shake
Additional Specifications
Photo Upload Flow (Camera / Library)
Action Sheet Options:- Action sheet → “Take Photo” → Dismiss sheet
- Camera opens (iOS native UIImagePickerController or image_picker package)
- User takes photo → Photo captured
- Camera dismisses
- Photo displays in Edit Profile screen (cropped to square circle)
- UI marks changes as unsaved
- Action sheet → “Choose from Library” → Dismiss sheet
- Photo picker opens (PHPickerViewController or image_picker)
- User selects photo
- Picker dismisses
- Photo displays in Edit Profile screen (cropped to square)
- UI marks changes as unsaved
- Action sheet → “Remove Photo” (red) → Dismiss sheet
- Photo immediately removed (reverts to placeholder silhouette)
- UI marks changes as unsaved
- On Save: Photo URL set to null in database
- 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)
- 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)
- User taps “Daily Reminder Time” row on Settings screen
- Sheet slides up (0.3s easeOut)
- Picker displays current time (or default 8:00 PM if never set)
- User spins wheels to select time
- 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
- No validation needed (picker ensures valid time)
- Notifications must be enabled:
- If notifications OFF: Alert “Enable notifications first to set a reminder time.”
- 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
- User taps “App Theme” row on Settings screen
- Sheet slides up (0.3s easeOut)
- Segmented control displays current selection (highlighted in coral)
- 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)
- User taps “Done”: Sheet dismisses (0.3s easeIn)
- Auto: Selected (coral background), app follows system theme
- Light: Selected, app always light
- Dark: Selected, app always dark
- 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:- User taps “Export My Data” row on Settings screen
- 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”
- 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.”
- JSON File: All entries with metadata:
- CSV File (alternative): Same data in CSV format
- Photos ZIP: All photos in original resolution (organized by date)
- 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:- User taps “Delete My Account” row (red text) on Settings screen
- 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)
- User taps “Continue”
- 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)
- 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.”
- Validation:
- 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- Settings Screen (Main) - 9 sections, 14+ interactive elements
- Edit Profile Screen - Photo upload, 2 form fields, save/cancel
- ✅ 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)
Next Steps for Development
Priority Order:
- Settings Screen: Implement all rows, sections, and navigation
- Edit Profile Screen: Implement form, photo upload, validation
- Time Picker Modal: Daily reminder time selection
- Theme Picker Modal: App theme selection (auto/light/dark)
- Export Data Flow: Backend integration for data export
- Delete Account Flow: Multi-step confirmation with password verification
- 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)
- PATCH
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:- Settings screen layout (grouped lists)
- Edit Profile screen (form + validation)
- Photo upload functionality (camera/library)
- Theme selection (immediate preview)
- Notifications (permission + scheduling)
- Data export (API integration)
- Delete account (multi-step confirmation)
- 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