| 1 | # Calendar UX review · 12 September 2026 |
| 2 | |
| 3 | ## CalendarWorkspace.swift |
| 4 | |
| 5 | 1. **High: keep the agenda reachable at large text sizes.** The month occupied |
| 6 | a fixed sibling above the scrolling agenda. It now shares one scrolling List |
| 7 | with the agenda. Native phone navigation uses an inline title to give the |
| 8 | calendar more room. The selected calendar name appears in the title. |
| 9 | |
| 10 | Before: `VStack { monthGrid; List { agenda } }` |
| 11 | After: `List { Section { monthHeader; monthGrid }; agenda }` |
| 12 | |
| 13 | An intermediate LazyVGrid-in-List version crashed in the iOS collection-view |
| 14 | layout pass. The final month uses an eager Grid with six GridRows; the |
| 15 | subsequent create/inspect/edit XCTest passed. The 42 date identities persist. |
| 16 | |
| 17 | 2. **Medium: distinguish today without relying on colour.** Today's date is |
| 18 | bold and outlined; VoiceOver reports Today and event count. Selection retains |
| 19 | the selected trait and uses the Calendar rose, fixing a mismatched blue. |
| 20 | Event strips use their calendar's hue and retain the calendar's written name. |
| 21 | |
| 22 | Before: `.background(selected ? Color.accentColor : .clear)` |
| 23 | After: rose selection plus today outline/bold and accessible event counts. |
| 24 | |
| 25 | 3. **Medium: let month controls adapt.** ViewThatFits moves controls below the |
| 26 | month title when horizontal space is insufficient; buttons retain 44-point |
| 27 | minimum targets. At accessibility sizes weekday initials preserve grid space, |
| 28 | while every date's accessible label contains the complete day/date. |
| 29 | |
| 30 | 4. **Medium: disclose exclusive all-day ends.** Inspection previously labelled |
| 31 | the stored exclusive date Ends, suggesting one extra day of duration. It now |
| 32 | says Ends before and explains the boundary, matching the editor. All-day |
| 33 | inspection omits the irrelevant time-zone row. Stored dates are unchanged. |
| 34 | |
| 35 | ## EventEditor.swift |
| 36 | |
| 37 | **Medium: label the editable notes field for assistive technology.** |
| 38 | |
| 39 | Before: `TextEditor(text: $event.notes)` |
| 40 | After: `TextEditor(text: $event.notes).accessibilityLabel("Notes")` |
| 41 | |
| 42 | Priority addressed: agenda reachability and layout stability, then date meaning, |
| 43 | control adaptation and editor accessibility. |
| 44 | |
| 45 | ## Method and validation |
| 46 | |
| 47 | Applied `swiftui-pro` design, accessibility and navigation references, plus |
| 48 | `swiftui-native-component-design` semantics/control/accessibility guidance. |
| 49 | Reviewed actual SwiftUI renders on Mac, iPhone and iPad; checked light mode and |
| 50 | accessibility-large Dynamic Type in dark mode on iPhone. Real forms/navigation |
| 51 | are exercised through the isolated authenticated Go fixture and XCTest. |
| 52 | No backend, auth, model retrieval or permission behavior changed. |
| 53 | |
| 54 | Independent reviewer: `/root/canary_release_review`, dispatched as `gpt-6-astra` |
| 55 | with high reasoning; runtime identity not independently verified. Screenshots |
| 56 | and source corrections were reviewed independently. Full VoiceOver walkthrough, |
| 57 | localization and every accessibility size remain hands-on acceptance work. |
| 58 | |
| 59 | ## Menu-bar agenda refinement · 13 September |
| 60 | |
| 61 | Pinned the compact month and selected-day heading above a dedicated scrolling |
| 62 | agenda. Day selection updates the existing observed model immediately and resets |
| 63 | the agenda scroll position. The header shows the selected day's event count |
| 64 | only after a successful load. Previously the month and events shared one scroll |
| 65 | region, hiding the agenda and making day selection appear ineffective. |
| 66 | |
| 67 | Mac quick-add now uses a plain native multiline field inside a padded surface |
| 68 | with a thin inset focus outline, avoiding the rounded field's clipped outer |
| 69 | focus ring. Expanded quick-add remains scrollable; panel height is bounded by |
| 70 | the screen's available height while retaining space for the day agenda. |
| 71 | |
| 72 | Validation: Mac build and signature verification; authenticated fixture renders |
| 73 | for a timed day, the following all-day event, and an empty day. The optional |
| 74 | NATIVE_REVIEW_DAY_SEQUENCE=1 review harness changes the same published selection |
| 75 | used by the month buttons, asserts each result and captures the live view without |
| 76 | reloading. This verifies reactive rendering, not physical mouse input. |
| 77 | Artifacts: /tmp/calendar-menu-dynamic.png, its .all-day.png and .empty.png suffixes. |
| 78 | |