mobile-app-ux-auditor-skill 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,253 @@
1
+ # Mobile UX Audit Reference
2
+
3
+ Use this reference when auditing or improving mobile apps across Flutter, React Native, Swift/iOS, Kotlin, Java, Android Views, or Jetpack Compose.
4
+
5
+ ## Current Research Anchors
6
+
7
+ Prefer current official docs when the user's request depends on latest platform behavior.
8
+
9
+ - Apple Human Interface Guidelines: navigation, layout, tab bars, accessibility, toolbars, search, Dynamic Type, VoiceOver, platform adaptation.
10
+ - Android app quality: core quality, adaptive quality, Material 3 layout/navigation pairings, accessibility principles, TalkBack, Switch Access, Android vitals.
11
+ - Flutter docs: adaptive/responsive design, `SafeArea`, `MediaQuery`, `LayoutBuilder`, accessibility testing, `Semantics`.
12
+ - React Native docs: `accessible`, `accessibilityLabel`, `accessibilityHint`, `accessibilityRole`, `accessibilityState`, `AccessibilityInfo`, focus order.
13
+ - W3C WCAG 2.2: mobile-relevant criteria including target size, dragging alternatives, redundant entry, focus visibility, consistent help, accessible authentication.
14
+ - Product quality and retention: treat retention as a result of clear value, trust, speed, and useful re-entry. Avoid dark patterns.
15
+ - Public agent-skill patterns such as UI/UX Pro Max and Taste Skill show a useful structure: concise `SKILL.md`, deeper reference files, search/scanner scripts, anti-generic design constraints, and explicit quality gates. Use the structure, not copied prose or persona imitation.
16
+
17
+ ## Cross-Agent Compatibility
18
+
19
+ This skill is portable when it stays inside the open skill shape:
20
+
21
+ - `SKILL.md` with YAML frontmatter and Markdown instructions.
22
+ - Optional `references/`, `scripts/`, and `assets/`.
23
+ - Optional `agents/openai.yaml` for Codex UI metadata; Claude Code ignores it.
24
+ - Install locations:
25
+ - Codex: `~/.codex/skills/mobile-app-ux-auditor/`
26
+ - Claude Code personal: `~/.claude/skills/mobile-app-ux-auditor/`
27
+ - Project-local Claude Code: `<repo>/.claude/skills/mobile-app-ux-auditor/`
28
+
29
+ Avoid relying on platform-specific frontmatter unless the skill is intentionally platform-specific. Put platform-specific execution notes in the body.
30
+
31
+ ## UX Engineer Operating Model
32
+
33
+ Use this order before proposing changes:
34
+
35
+ 1. **Design read**: category, audience, job, usage environment, platform mix, trust level, density, maturity, visual register, constraints.
36
+ 2. **User journey**: first value, primary task, recovery, re-entry, expansion, cancellation/delete.
37
+ 3. **Evidence capture**: code scan, screen map, screenshots, simulator/device walkthrough, screen-reader walkthrough, performance/jank signals.
38
+ 4. **Friction diagnosis**: confusion, too many choices, too many fields, hidden state, slow feedback, inaccessible control, broken recovery, trust gap.
39
+ 5. **Design response**: remove, reorder, relabel, disclose progressively, add feedback, make state visible, improve defaults, strengthen affordance.
40
+ 6. **Implementation response**: smallest code changes that improve completion and preserve platform conventions.
41
+ 7. **Verification**: prove the new flow works with at least one realistic path and one failure/interruption path.
42
+
43
+ Do not invent a celebrity-designer persona. Adopt the discipline: evidence, taste, consistency, accessibility, and verification.
44
+
45
+ ## Scorecard
46
+
47
+ Score each area from 0 to 5. A production app should have no area below 3 and critical flows should average 4+.
48
+
49
+ | Area | 0-1 | 3 | 5 |
50
+ | --- | --- | --- | --- |
51
+ | Task success | User cannot finish or loses data | Main path works with friction | Main, edge, interruption, and recovery paths are clear |
52
+ | Orientation | User cannot tell where they are | Titles/nav mostly work | Location, state, and next action are always obvious |
53
+ | Navigation | Back/close/deep links are broken | Common paths work | Platform-native, predictable, resumable, and deep-linkable |
54
+ | Forms/input | Placeholder labels, late errors, keyboard issues | Basic labels/errors | Autofill, correct keyboard, persistence, inline recovery |
55
+ | Accessibility | Mouse/touch-only or unlabeled controls | Basic labels | Screen reader, focus order, targets, contrast, dynamic type verified |
56
+ | Adaptive layout | Breaks on devices or keyboard | Common phone sizes pass | Phones, tablets, foldables, landscape, RTL/long text considered |
57
+ | Performance | Slow launch, janky scroll, blocked gestures | Acceptable local feel | Launch, transition, image, render, battery, and gesture latency optimized |
58
+ | System states | Only happy path designed | Loading/error exist | Empty, loading, error, offline, partial, disabled, success are complete |
59
+ | Design consistency | Random styles and components | Repeated components | Tokens, spacing, radius, type, color, motion, and platform fit are coherent |
60
+ | Trust and retention | Dark patterns or unclear consequences | Honest but minimal | Clear value, privacy, pricing, cancellation, and useful return loops |
61
+
62
+ ## Design Consistency Gates
63
+
64
+ Before calling a redesign good, check:
65
+
66
+ - **Platform fit**: iOS feels like iOS and Android feels like Android unless the product intentionally owns a cross-platform system.
67
+ - **Typography**: scalable type, readable line lengths, clear hierarchy, no clipping under dynamic type/font scaling.
68
+ - **Spacing**: repeated rhythm and density; controls do not crowd gesture areas, keyboards, or system UI.
69
+ - **Color**: semantic palette with accessible contrast; color is not the only signal.
70
+ - **Shape**: one radius system; buttons, inputs, cards, sheets, tabs, and tags follow a rule.
71
+ - **Elevation**: shadows, surfaces, sheets, and overlays communicate hierarchy rather than decoration.
72
+ - **Iconography**: one icon family and consistent stroke/fill logic; ambiguous icons have labels.
73
+ - **Motion**: purposeful transitions with reduced-motion fallback; no animation that delays task completion.
74
+ - **Copy**: labels are specific, buttons say what happens, errors tell users how to fix the problem.
75
+ - **Aesthetic fit**: style follows audience and product job, not generic AI gradients, repeated cards, or trend imitation.
76
+
77
+ ## Discovery Pass
78
+
79
+ Inspect before judging. Search for:
80
+
81
+ ```bash
82
+ rg -n "MaterialApp|CupertinoApp|go_router|AutoRoute|Navigator|NavigationBar|BottomNavigationBar|Drawer|TabBar|Semantics|Tooltip|SafeArea|MediaQuery|LayoutBuilder" .
83
+ rg -n "NavigationContainer|createNativeStackNavigator|createBottomTabNavigator|accessibilityLabel|accessibilityHint|accessibilityRole|accessibilityState|SafeAreaView|KeyboardAvoidingView" .
84
+ rg -n "NavigationStack|NavigationView|TabView|toolbar|sheet|accessibilityLabel|accessibilityHint|accessibilityValue|DynamicType|isAccessibilityElement|UIAccessibility|UITabBarController|UINavigationController" .
85
+ rg -n "NavHost|NavController|NavigationBar|NavigationRail|ModalNavigationDrawer|Scaffold|semantics|contentDescription|stateDescription|Role\\.|importantForAccessibility|labelFor" .
86
+ rg -n "onboarding|signup|sign-up|login|auth|permission|notification|paywall|subscribe|checkout|empty|error|loading|offline|analytics|track" .
87
+ ```
88
+
89
+ Run the bundled scanner when available:
90
+
91
+ ```bash
92
+ python scripts/mobile_ux_static_scan.py .
93
+ ```
94
+
95
+ Then produce:
96
+
97
+ - App type and likely primary user job.
98
+ - Screen inventory and top-level destinations.
99
+ - First-run flow from install/open to first value.
100
+ - Primary task flow with tap count, waits, account gates, permissions, and error paths.
101
+ - Return flow: push/deep link/open from recents/resume state.
102
+
103
+ ## Audit Checklist
104
+
105
+ ### First Launch and Onboarding
106
+
107
+ - Show value before asking for account creation, payment, contacts, location, camera, photos, notifications, or tracking.
108
+ - Ask permissions only at the moment they support a user action; use a clear pre-permission rationale only when it reduces confusion.
109
+ - Replace static carousel onboarding with interactive setup when possible.
110
+ - Let users skip nonessential setup and return later.
111
+ - Preserve progress if auth, payment, permissions, or network interrupts the flow.
112
+ - Keep copy concrete: what the user can do now, what happens next, and why information is needed.
113
+
114
+ ### Navigation and Information Architecture
115
+
116
+ - Use bottom navigation/tab bars for a small set of persistent top-level destinations. Keep labels short and destinations stable.
117
+ - Avoid hiding primary destinations behind a drawer on phones when there are only a few key areas.
118
+ - Do not mix multiple primary navigation systems unless one clearly adapts by screen size.
119
+ - Keep the current section visible during lateral navigation.
120
+ - Use platform-native back/up/close behavior: reverse chronological back for Android, hierarchy-aware back/close on iOS modals.
121
+ - Every screen needs an obvious next action, back path, and recovery path.
122
+ - Deep links and notifications should land users at the relevant content with context, not just the home screen.
123
+
124
+ ### Core Task Flow
125
+
126
+ - Identify the user's main job and count unnecessary taps, repeated fields, modal interruptions, waits, and context switches.
127
+ - Make one primary action visually dominant per state.
128
+ - Use sensible defaults, remembered choices, autofill, recent items, and progressive disclosure.
129
+ - Avoid blocking the task with education, upsells, ratings, or permissions before value.
130
+ - Keep destructive, purchase, and privacy-sensitive actions explicit and reversible where possible.
131
+
132
+ ### Forms, Input, and Errors
133
+
134
+ - Use visible labels, not placeholder-only labels.
135
+ - Set correct keyboard/input types, autofill hints, capitalization, validation, and password manager compatibility.
136
+ - Validate inline after the user can reasonably correct the field; do not fail only after submit.
137
+ - Keep error messages close to fields, specific, and action-oriented.
138
+ - Avoid redundant entry. Reuse known profile, shipping, billing, and account data when appropriate.
139
+ - On mobile checkout or subscription flows, reduce fields before reducing steps; long unnecessary forms are high-friction.
140
+
141
+ ### Feedback, Loading, Empty, and Offline States
142
+
143
+ - Show immediate feedback for taps, form submission, saving, syncing, and long-running actions.
144
+ - Use skeletons/progress only when they reflect actual state; avoid fake delays.
145
+ - Empty states should explain what is missing and offer a next action.
146
+ - Error states should say what happened, what the user can do, and whether data is safe.
147
+ - Offline states should preserve read/write where feasible and sync later.
148
+ - Confirm critical success states; do not leave users guessing after payment, booking, upload, or submission.
149
+
150
+ ### Visual Hierarchy and Interaction
151
+
152
+ - Design for scan order: the most important content/action appears where the platform and reading direction make it easiest to find.
153
+ - Keep text legible under Dynamic Type/font scaling and avoid clipped labels.
154
+ - Maintain safe areas around notches, system bars, home indicators, gesture regions, keyboards, and fold/hinge areas.
155
+ - Use spacing to group related controls and separate unrelated actions.
156
+ - Use familiar iconography and always label ambiguous icons.
157
+ - Motion should clarify state changes, not slow task completion. Respect Reduce Motion.
158
+ - Use haptics sparingly for confirmation, warning, or physical-feeling controls.
159
+
160
+ ### Accessibility
161
+
162
+ - Test with VoiceOver and TalkBack for all critical flows.
163
+ - Every interactive custom control needs role, name/label, value/state, hint when needed, and action.
164
+ - Focus order must match visual/task order and must not trap users.
165
+ - Touch targets should meet platform expectations: Flutter's guideline API checks 48 by 48 for Android and 44 by 44 for iOS; WCAG 2.2 adds target-size criteria for broader accessibility review.
166
+ - Do not communicate meaning with color alone. Add text, icons, position, shape, pattern, audio, or haptic cues.
167
+ - Support dynamic text, bold text, high contrast, reduced motion, screen readers, switch access, keyboard/external input where relevant, captions/transcripts for media, and accessible authentication.
168
+ - Decorative images should be hidden from assistive technologies; meaningful images need useful labels.
169
+
170
+ ### Adaptive Layout
171
+
172
+ - Audit compact phone, large phone, tablet, foldable, landscape, split-screen, and keyboard-open states when supported.
173
+ - Switch navigation patterns by size class where appropriate: bottom navigation on compact phones, navigation rail or sidebar/drawer on larger surfaces.
174
+ - Avoid stretching phone layouts across tablets. Use extra width for supporting panes, sidebars, multi-column content, or detail views.
175
+ - Preserve state across rotation, resize, split-screen, process death, and app resume.
176
+ - Test RTL languages and long localized strings if the app is localized.
177
+
178
+ ### Ethical Retention and Engagement
179
+
180
+ - Define the valuable return reason: unfinished task, fresh content, useful reminder, social response, saved progress, personal insight, or recurring real-world need.
181
+ - Make the home screen answer "what should I do now?" within a few seconds.
182
+ - Use personalization to reduce effort, not to bury controls or create anxiety.
183
+ - Ask for notification permission after the user sees value; provide granular controls and quiet defaults.
184
+ - Use streaks, rewards, and progress indicators only when they support the user's goal. Add forgiveness, pause, and recovery.
185
+ - Make settings, privacy, cancellation, logout, and delete paths findable.
186
+ - Instrument friction: first value time, task completion, abandon points, retry loops, error rates, permission acceptance, uninstall causes, DAU/MAU only when meaningful for the app category.
187
+
188
+ ## Framework-Specific Fix Map
189
+
190
+ ### Flutter
191
+
192
+ - Navigation: prefer `NavigationBar`, `NavigationRail`, `TabBar`, `Navigator`, `go_router`, or existing app routing. Use `Drawer` only when it fits the hierarchy.
193
+ - Adaptation: use `SafeArea`, `MediaQuery`, `LayoutBuilder`, `OrientationBuilder`, breakpoints, and Material/Cupertino widgets intentionally.
194
+ - Accessibility: add `Semantics`, `MergeSemantics`, `ExcludeSemantics`, `Tooltip` for icon-only controls, semantic labels for meaningful images, and accessibility guideline widget tests.
195
+ - Verification: run widget tests using `meetsGuideline(androidTapTargetGuideline)`, `meetsGuideline(iOSTapTargetGuideline)`, `labeledTapTargetGuideline`, and `textContrastGuideline` when test setup exists.
196
+
197
+ ### React Native
198
+
199
+ - Navigation: inspect React Navigation/native stack/bottom tabs/drawer usage. Preserve native-stack behavior where possible.
200
+ - Layout: use safe-area handling, `KeyboardAvoidingView` or platform-specific keyboard strategy, responsive spacing, and platform conditionals only where behavior differs.
201
+ - Accessibility: use `accessible`, `accessibilityLabel`, `accessibilityHint`, `accessibilityRole`, `accessibilityState`, `accessibilityActions`, and `importantForAccessibility`. Avoid production reliance on experimental APIs unless the project already accepted that risk.
202
+ - Verification: use simulator/device walkthroughs, VoiceOver/TalkBack, Detox/Appium/accessibility IDs where present.
203
+
204
+ ### Swift, SwiftUI, and UIKit
205
+
206
+ - Navigation: prefer `NavigationStack`, `TabView`, `sheet`, `toolbar`, `UINavigationController`, `UITabBarController`, and standard Back/Close behavior.
207
+ - Platform fit: use SF Symbols, Dynamic Type, safe areas, system controls, native sheets, confirmation dialogs, and standard search patterns before custom UI.
208
+ - Accessibility: use SwiftUI accessibility modifiers (`accessibilityLabel`, `accessibilityValue`, `accessibilityHint`, actions) or UIKit `UIAccessibility` properties (`isAccessibilityElement`, `accessibilityLabel`, `accessibilityValue`, `accessibilityHint`, traits).
209
+ - Verification: use Accessibility Inspector, VoiceOver, Dynamic Type sizes, Reduce Motion, high contrast, and different device classes.
210
+
211
+ ### Kotlin, Java, Android Views, and Jetpack Compose
212
+
213
+ - Navigation: inspect Navigation Component, Compose Navigation, `NavHost`, `NavController`, `NavigationBar`, `NavigationRail`, `ModalNavigationDrawer`, `Scaffold`, and XML navigation graphs.
214
+ - Platform fit: use Material 3 components, Android back behavior, edge-to-edge/safe insets, keyboard handling, and adaptive layouts for tablets/foldables.
215
+ - Accessibility in Compose: rely on built-in Material semantics where possible; add `Modifier.semantics`, `contentDescription`, `stateDescription`, `role`, and custom actions for custom components.
216
+ - Accessibility in Views: use `android:contentDescription`, `android:labelFor`, `importantForAccessibility`, proper focus order, and concise labels.
217
+ - Verification: use TalkBack, Accessibility Scanner, Layout Inspector semantics, UIAutomator/Espresso/Compose UI tests where available.
218
+
219
+ ## Report Template
220
+
221
+ ```markdown
222
+ ## Findings
223
+
224
+ | Severity | Flow/Screen | Evidence | User Impact | Fix |
225
+ | --- | --- | --- | --- | --- |
226
+ | P1 | Onboarding step 2 | `path/file.ext:42`, screenshot, or route name | Users must grant notifications before seeing value | Move notification ask after first successful action; add in-app reminder settings |
227
+
228
+ ## Flow Map
229
+
230
+ Current: Open -> carousel -> permission -> sign in -> blank home
231
+ Recommended: Open -> value preview -> optional setup -> first action -> account save prompt
232
+
233
+ ## Implementation Notes
234
+
235
+ - Framework-specific files/components to change.
236
+ - Native component or accessibility API to use.
237
+ - Analytics/events to add or inspect.
238
+
239
+ ## Verification
240
+
241
+ - What was run.
242
+ - What could not be run.
243
+ - Remaining UX risks.
244
+ ```
245
+
246
+ ## Common Failure Modes
247
+
248
+ - Auditing only visuals and missing task completion, permissions, errors, empty states, and return flows.
249
+ - Copying iOS patterns into Android or Material patterns into iOS without adapting navigation, gestures, and accessibility.
250
+ - Recommending more screens instead of removing decisions and fields.
251
+ - Treating retention as addiction instead of repeated successful value.
252
+ - Adding custom buttons, tabs, switches, sliders, or sheets without semantics, focus order, hit targets, and native behavior.
253
+ - Ignoring large text, screen readers, keyboard overlap, safe areas, tablets, foldables, RTL, and slow networks.
@@ -0,0 +1,318 @@
1
+ #!/usr/bin/env python3
2
+ """Static mobile UX signal scanner.
3
+
4
+ This script detects review signals in Flutter, React Native, Swift/iOS, and
5
+ Android projects. It is evidence gathering, not a full accessibility audit.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import json
12
+ import re
13
+ from dataclasses import dataclass
14
+ from pathlib import Path
15
+ from typing import Iterable
16
+
17
+
18
+ EXCLUDE_DIRS = {
19
+ ".git",
20
+ "node_modules",
21
+ "Pods",
22
+ "DerivedData",
23
+ ".gradle",
24
+ "build",
25
+ ".dart_tool",
26
+ ".expo",
27
+ "coverage",
28
+ "ios/Pods",
29
+ "android/build",
30
+ }
31
+
32
+ EXTENSIONS = {
33
+ ".dart",
34
+ ".jsx",
35
+ ".tsx",
36
+ ".js",
37
+ ".ts",
38
+ ".swift",
39
+ ".kt",
40
+ ".kts",
41
+ ".java",
42
+ ".xml",
43
+ }
44
+
45
+
46
+ @dataclass
47
+ class Finding:
48
+ severity: str
49
+ platform: str
50
+ category: str
51
+ title: str
52
+ path: str
53
+ line: int
54
+ evidence: str
55
+ fix: str
56
+
57
+
58
+ PATTERNS = [
59
+ (
60
+ "P1",
61
+ "React Native",
62
+ "Accessibility",
63
+ "Touchable/Pressable likely missing role or label",
64
+ re.compile(r"<(?:Pressable|TouchableOpacity|TouchableHighlight|TouchableWithoutFeedback)\b(?![^>\n]*accessibility(?:Role|Label))", re.I),
65
+ "Add accessibilityRole, accessibilityLabel, accessibilityState, and keyboard/screen-reader behavior.",
66
+ ),
67
+ (
68
+ "P2",
69
+ "React Native",
70
+ "Forms",
71
+ "TextInput placeholder-label risk",
72
+ re.compile(r"<TextInput\b[^>\n]*placeholder\s*=", re.I),
73
+ "Verify a persistent visible label and accessibilityLabel are present.",
74
+ ),
75
+ (
76
+ "P1",
77
+ "React Native",
78
+ "Images",
79
+ "Image likely missing accessibility label",
80
+ re.compile(r"<Image\b(?![^>\n]*(?:accessibilityLabel|alt)\s*=)", re.I),
81
+ "Add accessibilityLabel for meaningful images or mark decorative images inaccessible.",
82
+ ),
83
+ (
84
+ "P2",
85
+ "Flutter",
86
+ "Accessibility",
87
+ "IconButton likely missing tooltip",
88
+ re.compile(r"\bIconButton\s*\(", re.I),
89
+ "Verify tooltip/semanticLabel exists so screen readers and long-press hints are useful.",
90
+ ),
91
+ (
92
+ "P1",
93
+ "Flutter",
94
+ "Images",
95
+ "Image likely missing semantic label",
96
+ re.compile(r"\bImage\.(?:asset|network|file|memory)\s*\((?![^;\n]*semanticLabel\s*:)", re.I),
97
+ "Add semanticLabel for meaningful images or exclude decorative images from semantics.",
98
+ ),
99
+ (
100
+ "P2",
101
+ "Flutter",
102
+ "Forms",
103
+ "Text field needs persistent labeling",
104
+ re.compile(r"\bText(?:Form)?Field\s*\(", re.I),
105
+ "Verify InputDecoration has labelText/semantic labeling, helper/error text, and keyboard/autofill hints.",
106
+ ),
107
+ (
108
+ "P1",
109
+ "Swift/iOS",
110
+ "Accessibility",
111
+ "Image likely missing accessibility label",
112
+ re.compile(r"\bImage\s*\([^)]+\)(?![^\n]*accessibilityLabel)", re.I),
113
+ "Add accessibilityLabel for meaningful images or mark decorative images hidden.",
114
+ ),
115
+ (
116
+ "P2",
117
+ "Swift/iOS",
118
+ "Layout",
119
+ "Broad ignoresSafeArea risk",
120
+ re.compile(r"\.ignoresSafeArea\s*\(\s*\)", re.I),
121
+ "Constrain ignored safe areas to intentional background layers, not interactive content.",
122
+ ),
123
+ (
124
+ "P2",
125
+ "Swift/iOS",
126
+ "Permissions",
127
+ "Permission request needs value-first timing",
128
+ re.compile(r"requestAuthorization|requestWhenInUseAuthorization|requestAlwaysAuthorization", re.I),
129
+ "Verify permission is requested at the moment of user intent and has a clear rationale.",
130
+ ),
131
+ (
132
+ "P1",
133
+ "Android Compose",
134
+ "Accessibility",
135
+ "Icon likely missing contentDescription",
136
+ re.compile(r"\bIcon\s*\((?![^)\n]*contentDescription\s*=)", re.I),
137
+ "Set contentDescription for meaningful icons or null for decorative icons.",
138
+ ),
139
+ (
140
+ "P1",
141
+ "Android Compose",
142
+ "Accessibility",
143
+ "Clickable modifier may need role/state semantics",
144
+ re.compile(r"\.clickable\s*\(", re.I),
145
+ "Verify role, stateDescription, custom actions, and target size for custom clickable UI.",
146
+ ),
147
+ (
148
+ "P2",
149
+ "Android Compose",
150
+ "Forms",
151
+ "TextField needs explicit label/error support",
152
+ re.compile(r"\b(?:OutlinedTextField|TextField)\s*\(", re.I),
153
+ "Verify label, supportingText/error state, keyboardOptions, and autofill behavior.",
154
+ ),
155
+ (
156
+ "P1",
157
+ "Android Views",
158
+ "Accessibility",
159
+ "ImageView likely missing contentDescription",
160
+ re.compile(r"<ImageView\b(?![^>\n]*android:contentDescription\s*=)", re.I),
161
+ "Add android:contentDescription for meaningful images or @null for decorative images.",
162
+ ),
163
+ (
164
+ "P2",
165
+ "Android Views",
166
+ "Forms",
167
+ "EditText likely missing hint/label relationship",
168
+ re.compile(r"<EditText\b(?![^>\n]*android:hint\s*=)", re.I),
169
+ "Use hint/labelFor or Material TextInputLayout with clear errors and keyboard input type.",
170
+ ),
171
+ (
172
+ "P2",
173
+ "All",
174
+ "Retention",
175
+ "Notification or permission ask found",
176
+ re.compile(r"requestPermissions?|POST_NOTIFICATIONS|UNUserNotificationCenter|notifications?\b", re.I),
177
+ "Check this is value-timed, optional when possible, and paired with granular settings.",
178
+ ),
179
+ ]
180
+
181
+
182
+ def iter_files(root: Path) -> Iterable[Path]:
183
+ for path in root.rglob("*"):
184
+ if path.is_dir():
185
+ continue
186
+ if any(part in EXCLUDE_DIRS for part in path.parts):
187
+ continue
188
+ if path.suffix.lower() not in EXTENSIONS:
189
+ continue
190
+ if path.stat().st_size > 1_000_000:
191
+ continue
192
+ yield path
193
+
194
+
195
+ def detect_stack(root: Path) -> list[str]:
196
+ stack: list[str] = []
197
+ if (root / "pubspec.yaml").exists():
198
+ stack.append("Flutter")
199
+ package_json = root / "package.json"
200
+ if package_json.exists():
201
+ try:
202
+ data = json.loads(package_json.read_text(encoding="utf-8"))
203
+ deps = " ".join(
204
+ list((data.get("dependencies") or {}).keys())
205
+ + list((data.get("devDependencies") or {}).keys())
206
+ )
207
+ if "react-native" in deps:
208
+ stack.append("React Native")
209
+ if "expo" in deps:
210
+ stack.append("Expo")
211
+ except Exception:
212
+ stack.append("package.json present, unreadable")
213
+ if list(root.rglob("*.swift")):
214
+ stack.append("Swift/iOS")
215
+ if list(root.rglob("*.kt")) or list(root.rglob("*.java")):
216
+ stack.append("Android Kotlin/Java")
217
+ if (root / "android").exists():
218
+ stack.append("Android project")
219
+ if (root / "ios").exists():
220
+ stack.append("iOS project")
221
+ return sorted(set(stack)) or ["Unknown mobile stack"]
222
+
223
+
224
+ def global_findings(root: Path, stack: list[str], all_text: str) -> list[Finding]:
225
+ findings: list[Finding] = []
226
+ if "Flutter" in stack and "SafeArea" not in all_text:
227
+ findings.append(
228
+ Finding("P2", "Flutter", "Layout", "SafeArea not found", ".", 0, "No SafeArea token found", "Verify content avoids notches, system bars, keyboards, and gesture areas.")
229
+ )
230
+ if "React Native" in stack and "SafeAreaView" not in all_text and "useSafeAreaInsets" not in all_text:
231
+ findings.append(
232
+ Finding("P2", "React Native", "Layout", "Safe-area handling not found", ".", 0, "No SafeAreaView/useSafeAreaInsets token found", "Verify content avoids notches, system bars, and gesture areas.")
233
+ )
234
+ if ("Android Kotlin/Java" in stack or "Android project" in stack) and "WindowInsets" not in all_text and "safeDrawing" not in all_text:
235
+ findings.append(
236
+ Finding("P2", "Android", "Adaptive layout", "Inset handling not found", ".", 0, "No WindowInsets/safeDrawing token found", "Verify edge-to-edge layouts account for system bars and IME.")
237
+ )
238
+ return findings
239
+
240
+
241
+ def scan(root: Path) -> tuple[list[str], list[Finding], int]:
242
+ stack = detect_stack(root)
243
+ findings: list[Finding] = []
244
+ files = list(iter_files(root))
245
+ snippets: list[str] = []
246
+ for file_path in files:
247
+ rel = file_path.relative_to(root).as_posix()
248
+ try:
249
+ lines = file_path.read_text(encoding="utf-8", errors="ignore").splitlines()
250
+ except Exception:
251
+ continue
252
+ snippets.extend(lines[:2000])
253
+ for idx, line in enumerate(lines, start=1):
254
+ stripped = line.strip()
255
+ if not stripped:
256
+ continue
257
+ for severity, platform, category, title, pattern, fix in PATTERNS:
258
+ if pattern.search(stripped):
259
+ findings.append(
260
+ Finding(
261
+ severity=severity,
262
+ platform=platform,
263
+ category=category,
264
+ title=title,
265
+ path=rel,
266
+ line=idx,
267
+ evidence=stripped[:180],
268
+ fix=fix,
269
+ )
270
+ )
271
+ findings = global_findings(root, stack, "\n".join(snippets)) + findings
272
+ return stack, findings, len(files)
273
+
274
+
275
+ def render_markdown(root: Path, stack: list[str], findings: list[Finding], file_count: int) -> str:
276
+ counts = {key: sum(1 for item in findings if item.severity == key) for key in ("P0", "P1", "P2", "P3")}
277
+ out = [
278
+ "# Mobile UX Static Scan",
279
+ "",
280
+ f"Root: `{root}`",
281
+ f"Files scanned: `{file_count}`",
282
+ f"Detected stack: {', '.join(stack)}",
283
+ f"Findings: P0={counts['P0']} P1={counts['P1']} P2={counts['P2']} P3={counts['P3']}",
284
+ "",
285
+ "> Static scan output is a triage signal. Confirm every finding in the app or code before changing behavior.",
286
+ "",
287
+ ]
288
+ if not findings:
289
+ out.append("No matching static UX signals found.")
290
+ return "\n".join(out)
291
+
292
+ out.extend(["| Severity | Platform | Category | Location | Signal | Evidence | Fix |", "| --- | --- | --- | --- | --- | --- | --- |"])
293
+ for item in findings[:140]:
294
+ location = item.path if item.line == 0 else f"{item.path}:{item.line}"
295
+ evidence = item.evidence.replace("|", "\\|")
296
+ out.append(
297
+ f"| {item.severity} | {item.platform} | {item.category} | `{location}` | {item.title} | `{evidence}` | {item.fix} |"
298
+ )
299
+ if len(findings) > 140:
300
+ out.append(f"\nTruncated to 140 findings out of {len(findings)}. Narrow the scan path for more detail.")
301
+ return "\n".join(out)
302
+
303
+
304
+ def main() -> int:
305
+ parser = argparse.ArgumentParser(description="Scan a mobile app for static UX review signals.")
306
+ parser.add_argument("root", nargs="?", default=".", help="Project root or subdirectory to scan")
307
+ args = parser.parse_args()
308
+
309
+ root = Path(args.root).resolve()
310
+ if not root.exists():
311
+ raise SystemExit(f"Path does not exist: {root}")
312
+ stack, findings, file_count = scan(root)
313
+ print(render_markdown(root, stack, findings, file_count))
314
+ return 0
315
+
316
+
317
+ if __name__ == "__main__":
318
+ raise SystemExit(main())
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: mobile-app-ux-auditor
3
+ description: Use when reviewing, redesigning, or implementing mobile app UI/UX flows in Flutter, React Native, Swift, SwiftUI, UIKit, Kotlin, Jetpack Compose, Java, or Android Views; especially for navigation, onboarding, retention, accessibility, forms, engagement, screen hierarchy, or platform-native fit.
4
+ ---
5
+
6
+ # Mobile App UX Auditor
7
+
8
+ ## Overview
9
+
10
+ Audit mobile apps by mapping real user flows, finding friction with evidence, and proposing or implementing improvements that match iOS and Android platform expectations. Optimize for users reaching value quickly and returning willingly, not for manipulative time-on-device.
11
+
12
+ ## Workflow
13
+
14
+ 1. State the design read: app category, audience, job-to-be-done, target platforms, product maturity, visual register, and risk level. For redesigns, preserve working navigation, copy, analytics names, and accessibility wins unless there is evidence they cause harm.
15
+ 2. If code is available, run the static signal scan before judging:
16
+ ```bash
17
+ python scripts/mobile_ux_static_scan.py <project-root>
18
+ ```
19
+ In Claude Code, use `python ${CLAUDE_SKILL_DIR}/scripts/mobile_ux_static_scan.py <project-root>` when the skill is installed. Treat the script as evidence-gathering, not a replacement for expert review.
20
+ 3. Inspect routing, screens, design system files, navigation components, analytics events, accessibility props, platform-specific UI primitives, tests, and screenshots before recommending changes.
21
+ 4. Build a compact screen and flow map: first launch, onboarding, sign-in, home, primary task, search/discovery, settings, error states, empty states, permission requests, purchase/subscription, and re-entry flows.
22
+ 5. Audit flows against platform conventions, accessibility, clarity, effort, trust, feedback, performance, adaptive layout, and ethical retention. Load `references/mobile-ux-audit-reference.md` for the detailed checklist and framework-specific code signals.
23
+ 6. Rank issues by user impact:
24
+ - P0: Blocks core task, causes data loss, or creates severe accessibility failure.
25
+ - P1: Breaks navigation, trust, comprehension, or completion for many users.
26
+ - P2: Adds avoidable friction, inconsistency, or weak platform fit.
27
+ - P3: Polish, delight, or instrumentation improvement.
28
+ 7. When editing code, preserve the app's existing architecture and design system. Prefer native platform components and established navigation/accessibility APIs over custom controls unless the product has a clear reason.
29
+ 8. Verify with the best available evidence: emulator/simulator walkthrough, screenshots, accessibility scanner, VoiceOver/TalkBack, widget/UI tests, route tests, or static inspection. State any verification that could not be run.
30
+
31
+ ## Quality Bar
32
+
33
+ Hold the output to a senior mobile product design engineer standard:
34
+
35
+ - The primary user can understand where they are, what changed, what to do next, and how to recover from mistakes.
36
+ - Core flows handle loading, empty, error, partial, disabled, offline, slow-network, auth-expired, permission-denied, interrupted, and resumed states.
37
+ - The UI has one coherent design system: typography scale, spacing rhythm, radius rules, color roles, interaction states, motion, density, and platform adaptation.
38
+ - Accessibility is built into labels, roles, states, focus order, target size, screen reader output, dynamic type/font scaling, contrast, and reduced-motion behavior.
39
+ - Mobile performance is UX: launch time, route transitions, jank, unnecessary re-renders, heavy images, blocked gestures, keyboard latency, and battery cost all matter.
40
+ - Retention comes from saved progress, useful reminders, reduced effort, trust, and repeated value. Do not optimize for addiction.
41
+
42
+ ## Output Format
43
+
44
+ Start with findings, not praise. Include:
45
+
46
+ - A prioritized findings table: severity, screen/flow, evidence, user impact, recommended fix.
47
+ - A before/after flow summary when navigation or onboarding changes.
48
+ - Framework-specific implementation notes for Flutter, React Native, Swift/iOS, or Android as applicable.
49
+ - Static scan findings from `scripts/mobile_ux_static_scan.py` when code is available.
50
+ - Accessibility and adaptive-layout checks.
51
+ - Verification performed and remaining risks.
52
+
53
+ When asked to improve the app directly, implement the smallest high-impact changes first, then report changed files and validation.
54
+
55
+ ## Non-Negotiables
56
+
57
+ - Do not recommend "make users stay longer" tactics that rely on confusion, forced continuity, hidden exits, guilt, dark patterns, or notification spam.
58
+ - Do not replace platform conventions with custom UI only for novelty.
59
+ - Do not audit from static screenshots alone when code or an app build is available.
60
+ - Do not give generic advice like "improve onboarding" without naming the exact screen, problem, and change.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Mobile App UX Auditor"
3
+ short_description: "Audit and improve mobile app UI/UX flows"
4
+ default_prompt: "Use $mobile-app-ux-auditor to review this mobile app flow and propose practical UI/UX improvements."