Skillshub axiom-ux-flow-audit
Use when auditing user journeys, checking for UX dead ends, dismiss traps, buried CTAs, missing empty/loading/error states, or broken data paths in iOS apps (SwiftUI and UIKit).
git clone https://github.com/ComeOnOliver/skillshub
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/CharlesWiltgen/Axiom/axiom-ux-flow-audit" ~/.claude/skills/comeonoliver-skillshub-axiom-ux-flow-audit && rm -rf "$T"
skills/CharlesWiltgen/Axiom/axiom-ux-flow-audit/SKILL.mdUX Flow Audit
UX issues are not polish — they're defects that cause support tickets, bad reviews, and user churn.
Axiom's code-level auditors check patterns. This skill checks what users actually experience: Can they complete their task? Can they get back? Do they know what's happening?
6 iOS UX Principles (Detection Anchors)
These principles anchor every detection category. When a principle is violated, users get stuck, confused, or frustrated.
1. Honor the Promise
What the button/title says must match what the user gets. A "Settings" button that opens a profile page breaks trust.
2. Escape Hatch
Every modal (sheet, fullScreenCover, alert) must have a way out. A sheet without a dismiss button or drag-to-dismiss traps users.
3. Primary Action Visibility
The main thing users came to do must be immediately visible and tappable. If the CTA requires scrolling or menu-diving, users won't find it.
4. Dead End Prevention
Every view must have a forward path (next step) or a completion state (success message, return to start). A view with no actions and no navigation is a dead end.
5. Progressive Disclosure
Don't overwhelm on first screen. Show essentials first, details on demand. An onboarding flow that dumps 12 settings on page one loses users.
6. Feedback Loop
Users must know what's happening during async operations. No loading state = "is it broken?" No error state = "what went wrong?" No empty state = "is this feature missing?"
Detection Categories
8 Core Defects (always check — these are UX bugs, not opinions):
- Dead-End Views (CRITICAL)
- Dismiss Traps (CRITICAL)
- Buried CTAs (HIGH)
- Promise-Scope Mismatch (HIGH)
- Deep Link Dead Ends (HIGH)
- Missing Empty States (HIGH)
- Missing Loading/Error States (HIGH)
- Accessibility Dead Ends (HIGH)
3 Contextual Checks (check when product context warrants — these involve design judgment):
- Onboarding Gaps (MEDIUM) — requires knowing the product's onboarding strategy
- Broken Data Paths (MEDIUM) — overlaps with code correctness; include only when UX-visible
- Platform Parity Gaps (MEDIUM) — depends on target device strategy
Core defects are always worth reporting. Contextual checks require product knowledge — flag them if they look wrong, but acknowledge they may be intentional decisions.
1. Dead-End Views (CRITICAL)
Views with no navigation forward, no actions, and no completion state.
Detect:
- SwiftUI: Views with no
,NavigationLink
,Button
,.sheet
,.fullScreenCover
, or dismiss action.navigationDestination - UIKit: View controllers with no
, noIBAction
, no navigation push/present calls, noaddTargetUIBarButtonItem - Check for views/VCs that are navigation destinations but offer no way to proceed or return
Common cause: Placeholder views during development that ship to production.
2. Dismiss Traps (CRITICAL)
Sheets or fullScreenCover without a dismiss path.
Detect:
- SwiftUI:
without.fullScreenCover
or explicit dismiss button;@Environment(\.dismiss)
with.sheet
without alternative dismiss; alert/confirmation dialogs missing cancel actions.interactiveDismissDisabled(true) - UIKit:
withpresent(_:animated:)
where presented VC has no dismiss/close button;modalPresentationStyle = .fullScreen
without alternative dismiss pathisModalInPresentation = true
Why critical: Users literally cannot leave the screen. The only escape is force-quitting the app.
3. Buried CTAs (HIGH)
Primary actions hidden below fold, in menus, or behind navigation.
Detect:
- Primary action buttons placed after long
contentScrollView - Important actions only in
overflow menu (.toolbar
).secondaryAction - CTAs inside expandable
sectionsDisclosureGroup - No prominent action on the main tab's root view
Not a buried CTA: Below-fold placement that is intentional — checkout confirmation ("review order then confirm"), terms acceptance ("read then agree"), or content that the user should see before acting. The test: is the below-fold placement serving the user (they need context first) or hurting them (they can't find the action)?
4. Promise-Scope Mismatch (HIGH)
NavigationTitle, button label, or tab name doesn't match the content.
Detect:
where view content is clearly about Y.navigationTitle("X")
that navigates to a profile/account viewNavigationLink("Settings")- Tab labels that don't match tab content
- Button text suggesting one action but performing another
5. Deep Link Dead Ends (HIGH)
URL opens but lands on empty or broken state.
Detect:
handlers that push a view without checking if data exists.onOpenURL- Deep link destinations that assume pre-loaded state
- Universal link handling that doesn't validate the entity ID
- No fallback when deep-linked content is unavailable
Cross-reference:
axiom-swiftui-nav covers deep link architecture. This category checks the UX outcome.
6. Missing Empty States (HIGH)
Lists, grids, or content views with no data show blank screen.
Detect:
orList
withoutForEach
orif items.isEmpty { ... }
for empty state.overlay
results displayed without empty check@Query- Search results with no "no results" view
- Filtered views that can reach zero items
7. Missing Loading/Error States (HIGH)
Async operations with no feedback.
Detect:
- SwiftUI:
or.task { }
that fetches data without a loading indicator;Task { }
without error presentation (notry await
, no error state variable); state enum missing.alert
or.loading
cases.error - UIKit:
calls withoutURLSession
or progress UI; completion handlers that don't update UI on error; missingUIActivityIndicatorView
for failure casesUIAlertController - Both: Network calls without timeout or retry UI
- Both:
blocks that onlycatch
/log inprint
with no user-visible feedback — the user sees the operation silently fail#if DEBUG
Focus on network/write operations: Skip loading indicators for fast local reads (GRDB queries, UserDefaults, cached data) that complete in under 100ms — adding spinners to these creates visual flicker. Focus on network calls, database writes, and any operation that can meaningfully fail.
Scan systematically: When you find a silent-error pattern in one file (e.g.,
catch { print(...) } without user feedback), scan ALL similar files for the same pattern. A single catch-block issue usually indicates a codebase-wide habit.
8. Accessibility Dead Ends (HIGH)
Actions only reachable via gestures or visual cues, invisible to assistive technology.
Detect:
/.onLongPressGesture
/.swipeActions
withoutDragGesture
equivalent.accessibilityAction- Custom controls without
or.accessibilityLabel.accessibilityHint - Navigation that depends on color alone (e.g., "tap the green button")
- Pull-to-refresh (
) without VoiceOver-accessible alternative (note:refreshable
is automatically accessible — check custom implementations)refreshable
Cross-reference:
axiom-accessibility-diag covers full WCAG compliance. This category specifically checks UX flow reachability from assistive technology.
9. Onboarding Gaps (MEDIUM)
First-launch flow that's incomplete or overwhelming.
Detect:
- No
-gated onboarding check@AppStorage - Onboarding flow without skip/later option
- More than 5 onboarding screens
- Onboarding that requires account creation before showing app value
10. Broken Data Paths (MEDIUM)
State/binding wiring issues that manifest as UX problems (view shows stale data, edits don't save, view appears empty when data exists).
Detect:
- Views accepting
that are initialized with@Binding
in non-preview code.constant() - Views expecting
values not provided by ancestors@Environment
models created locally when they should be injected@Observable
used where@State
should propagate changes upward@Binding
Scope note: This overlaps with general SwiftUI correctness (
axiom-swiftui-debugging). Include findings here only when the broken data path causes a visible UX problem — blank screen, stale content, edits that don't persist. Skip compiler-level or crash-level issues that belong in code review.
11. Platform Parity Gaps (MEDIUM)
iPad sidebar missing, landscape broken, Mac Catalyst issues.
Detect:
withoutNavigationStack
alternative for iPadNavigationSplitView- No
checks for adaptive layout.horizontalSizeClass - Views that break in landscape (fixed heights, no scroll)
- Missing keyboard shortcut support on iPad/Mac
Audit Process
Step 1: Map Entry Points
Find all ways users enter the app:
App struct / SceneDelegate@main
handlers (deep links).onOpenURL- Widget
destinationsLink - Notification response handlers (
)UNUserNotificationCenterDelegate - Spotlight/Siri intent handlers
Step 2: Map Navigation Containers
Find all navigation structure:
/NavigationStackNavigationSplitView
with tab structureTabView
/.sheet
presentations.fullScreenCover- Custom modal presentations
Step 3: Trace Flows
For each entry point → completion path:
- Can the user reach their goal?
- Can the user get back?
- Does the user know what's happening at each step?
Step 4: Check Data Wiring
- Are
vars actually passed from parent?@Binding - Are
objects injected via environment?@Observable - Are
results handled for empty case?@Query
Step 5: Check Platform Adaptivity
- iPad: Does sidebar/split view work?
- Landscape: Does layout adapt?
- Mac Catalyst/Designed for iPad: Do keyboard shortcuts exist?
Step 6: Check Accessibility Flows
- Can VoiceOver users complete every flow?
- Are gesture-only actions backed by accessibility actions?
Cross-Auditor Correlation
When findings overlap with other Axiom auditors, note the correlation and elevate severity:
| UX Finding | Overlapping Auditor | Compound Effect | Severity Bump |
|---|---|---|---|
| Dead end + missing NavigationPath | swiftui-nav-auditor | Programmatic fix impossible | CRITICAL |
Gesture-only action + no | accessibility-auditor | Dead end for VoiceOver users | CRITICAL |
| Missing loading state + unhandled async error | concurrency-auditor | Crash + no user feedback | CRITICAL |
| Missing empty state + @Query with no results | swiftdata-auditor | Blank screen after data migration | HIGH |
| Deep link dead end + no URL validation | swiftui-nav-auditor | Silent failure from external link | HIGH |
Output Format
Enhanced Rating Table (for CRITICAL and HIGH findings)
| Finding | Urgency | Blast Radius | Fix Effort | ROI |
|---|---|---|---|---|
| Dead-end after payment | Ship-blocker | All users | 30 min | Critical |
| Missing empty state on search | Next release | Users who search | 15 min | High |
Urgency: Ship-blocker / Next release / Backlog Blast Radius: All users / Specific flow / Edge case Fix Effort: Time estimate for the fix ROI: Computed from urgency x blast radius / effort
Navigation Reachability Score
At end of audit, output:
## Navigation Reachability - Total screens found: [N] (views with navigation presentation) - Deep-linkable screens: [N] (.onOpenURL can reach them) - Widget-reachable screens: [N] (widget Link destinations) - Notification-reachable screens: [N] (notification handlers) - Coverage: [N]% of screens are externally reachable
Fix Effort Reality Check
Most UX flow defects are fast fixes. When someone says "that's a big change," check this table:
| Defect | Typical Fix | Time |
|---|---|---|
| Dismiss trap (no close button) | Add toolbar Cancel button + | 10-15 min |
| Missing empty state | Add | 15-20 min |
| Buried CTA (placement change) | Move button from to | 20-30 min |
| Dead-end view (no forward path) | Add NavigationLink or action button | 15-30 min |
| Missing loading state | Add + ProgressView overlay | 15-20 min |
| Silent error (no user feedback) | Add presentation on catch block | 10-15 min |
| Gesture-only action | Add + visible button alternative | 15-20 min |
The cost of NOT fixing: A dismiss trap or dead end after payment generates 1-star reviews within hours of launch. Each review costs 10-20 positive reviews to offset. The 15-minute fix prevents weeks of damage control.
Anti-Rationalization
| Thought | Reality |
|---|---|
| "UX issues are just polish, we'll fix later" | UX dead ends cause 1-star reviews. They're defects, not enhancements. A 15-min fix now prevents weeks of damage control. |
| "Users will figure it out" | Users don't figure it out. They delete the app. Average user tries for 30 seconds. |
| "We'll add empty states after launch" | Empty states are the FIRST thing new users see. Launching without them means launching broken. |
| "That fix is a big design change" | Most UX fixes are placement or state changes (10-30 min). Check the Fix Effort table above. |
| "Accessibility is a separate concern" | If VoiceOver users can't complete a flow, it's a dead end. Same defect, different user. |
| "This screen is just temporary" | Temporary screens ship. Check them anyway. |
| "The dismiss gesture handles it" | fullScreenCover has no dismiss gesture. That's the trap. |
Resources
Skills: axiom-swiftui-nav, axiom-accessibility-diag, axiom-hig, axiom-swiftui-debugging
Agents: ux-flow-auditor (automated scanning), swiftui-nav-auditor (navigation architecture), accessibility-auditor (WCAG compliance)