@buivietphi/skill-mobile-mt 2.0.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.
- package/AGENTS.md +482 -0
- package/README.md +528 -0
- package/SKILL.md +1399 -0
- package/android/android-native.md +480 -0
- package/bin/install.mjs +976 -0
- package/flutter/flutter.md +304 -0
- package/humanizer/humanizer-mobile.md +295 -0
- package/ios/ios-native.md +182 -0
- package/package.json +56 -0
- package/react-native/react-native.md +743 -0
- package/shared/agent-rules-template.md +343 -0
- package/shared/ai-dlc-workflow.md +237 -0
- package/shared/anti-patterns.md +407 -0
- package/shared/architecture-intelligence.md +416 -0
- package/shared/bug-detection.md +71 -0
- package/shared/ci-cd.md +423 -0
- package/shared/claude-md-template.md +125 -0
- package/shared/code-review.md +133 -0
- package/shared/common-pitfalls.md +117 -0
- package/shared/document-analysis.md +167 -0
- package/shared/error-recovery.md +467 -0
- package/shared/observability.md +688 -0
- package/shared/offline-first.md +377 -0
- package/shared/performance-prediction.md +210 -0
- package/shared/platform-excellence.md +244 -0
- package/shared/prompt-engineering.md +705 -0
- package/shared/release-checklist.md +82 -0
- package/shared/testing-strategy.md +332 -0
- package/shared/ui-ux-mobile.md +667 -0
- package/shared/version-management.md +526 -0
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
# UI/UX Mobile — Screen Design & Implementation
|
|
2
|
+
|
|
3
|
+
> Use when: "create screen X", "build UI for Y", "design this layout", "demo screen", "mockup".
|
|
4
|
+
> Covers: design system, screen templates, navigation, touch, color, typography, animation, accessibility.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Design Decision Framework
|
|
9
|
+
|
|
10
|
+
**Before any screen, answer these:**
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
SCREEN: [name / purpose]
|
|
14
|
+
PLATFORM: [iOS / Android / Cross-platform]
|
|
15
|
+
TYPE: [form / list / detail / dashboard / chat / auth / onboarding]
|
|
16
|
+
DATA: [static / API / real-time / offline-first]
|
|
17
|
+
PRIORITY: [speed-to-build / pixel-perfect / accessibility-first]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Then auto-think:**
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
<think>
|
|
24
|
+
SCREEN: [description]
|
|
25
|
+
TEMPLATE: [closest match from templates below]
|
|
26
|
+
TOKENS: [which design tokens to use]
|
|
27
|
+
STATES: loading / error / empty / success
|
|
28
|
+
NAVIGATION: [how user arrives + leaves]
|
|
29
|
+
PLATFORM RULES: [iOS HIG / Material Design 3 specifics]
|
|
30
|
+
TOUCH: [primary CTA in thumb zone?]
|
|
31
|
+
DARK MODE: [semantic colors, no hardcoded]
|
|
32
|
+
A11Y: [labels, contrast, Dynamic Type]
|
|
33
|
+
</think>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Design Token Architecture (3 Layers)
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Primitive tokens: blue-500, spacing-4 (raw values)
|
|
42
|
+
↓
|
|
43
|
+
Semantic tokens: color-primary, text-error (purpose-based)
|
|
44
|
+
↓
|
|
45
|
+
Component tokens: button-bg, card-radius (component-specific)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Rule: NEVER hardcode values. Always use design tokens.**
|
|
49
|
+
|
|
50
|
+
### React Native
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// theme/tokens.ts
|
|
54
|
+
export const colors = {
|
|
55
|
+
primary: '#007AFF',
|
|
56
|
+
secondary: '#5856D6',
|
|
57
|
+
background: '#FFFFFF',
|
|
58
|
+
surface: '#F2F2F7',
|
|
59
|
+
text: '#000000',
|
|
60
|
+
textSecondary: '#8E8E93',
|
|
61
|
+
error: '#FF3B30',
|
|
62
|
+
success: '#34C759',
|
|
63
|
+
warning: '#FF9500',
|
|
64
|
+
border: '#E5E5EA',
|
|
65
|
+
dark: {
|
|
66
|
+
background: '#000000', // OLED: true black = 0% battery
|
|
67
|
+
surface: '#1C1C1E', // elevation level 1
|
|
68
|
+
surfaceElevated: '#2C2C2E', // elevation level 2
|
|
69
|
+
text: '#E0E0E0', // NOT pure white (causes eye strain)
|
|
70
|
+
textSecondary: '#8E8E93',
|
|
71
|
+
border: '#38383A',
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const spacing = {
|
|
76
|
+
xs: 4, sm: 8, md: 16, lg: 24, xl: 32, xxl: 48,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const radius = {
|
|
80
|
+
sm: 8, md: 12, lg: 16, xl: 24, full: 9999,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const fontSize = {
|
|
84
|
+
caption: 12, body: 16, title: 20, heading: 28, hero: 34,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const shadow = {
|
|
88
|
+
sm: { shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.1, shadowRadius: 2, elevation: 2 },
|
|
89
|
+
md: { shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.15, shadowRadius: 8, elevation: 4 },
|
|
90
|
+
lg: { shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.2, shadowRadius: 16, elevation: 8 },
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Flutter
|
|
95
|
+
|
|
96
|
+
```dart
|
|
97
|
+
// theme/app_theme.dart
|
|
98
|
+
class AppTheme {
|
|
99
|
+
static ThemeData light() => ThemeData(
|
|
100
|
+
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF007AFF)),
|
|
101
|
+
useMaterial3: true,
|
|
102
|
+
textTheme: const TextTheme(
|
|
103
|
+
headlineLarge: TextStyle(fontSize: 34, fontWeight: FontWeight.bold),
|
|
104
|
+
titleLarge: TextStyle(fontSize: 20, fontWeight: FontWeight.w600),
|
|
105
|
+
bodyLarge: TextStyle(fontSize: 16),
|
|
106
|
+
bodySmall: TextStyle(fontSize: 12, color: Color(0xFF8E8E93)),
|
|
107
|
+
),
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
static ThemeData dark() => ThemeData(
|
|
111
|
+
colorScheme: ColorScheme.fromSeed(
|
|
112
|
+
seedColor: const Color(0xFF007AFF),
|
|
113
|
+
brightness: Brightness.dark,
|
|
114
|
+
),
|
|
115
|
+
useMaterial3: true,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Platform Color Rules
|
|
123
|
+
|
|
124
|
+
### iOS Semantic Colors (auto light/dark)
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
Label: .label → primary text
|
|
128
|
+
SecondaryLabel: → secondary text
|
|
129
|
+
SystemBackground: → screen bg
|
|
130
|
+
SecondarySystemBackground: → card/section bg
|
|
131
|
+
Separator: → thin dividers
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Android Material You (Dynamic Color)
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
API 31+: Colors extracted from wallpaper automatically
|
|
138
|
+
Primary / Secondary / Tertiary + On variants
|
|
139
|
+
Surface: elevation = slightly lighter overlay
|
|
140
|
+
0dp = 0% overlay | 4dp = 9% | 8dp = 12% | 12dp = 14%
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### OLED Battery Impact
|
|
144
|
+
|
|
145
|
+
| Color | Battery Usage |
|
|
146
|
+
|-------|--------------|
|
|
147
|
+
| #000000 (true black) | 0% |
|
|
148
|
+
| #1A1A1A (near black) | ~15% |
|
|
149
|
+
| #333333 (dark gray) | ~30% |
|
|
150
|
+
| #FFFFFF (white) | 100% |
|
|
151
|
+
|
|
152
|
+
**Rule:** Dark mode backgrounds = `#000000`. Surfaces = `#0D0D0D` to `#1C1C1E`.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Typography System
|
|
157
|
+
|
|
158
|
+
### iOS (SF Pro)
|
|
159
|
+
|
|
160
|
+
| Style | Size | Weight | Usage |
|
|
161
|
+
|-------|------|--------|-------|
|
|
162
|
+
| Large Title | 34pt | Bold | Screen titles |
|
|
163
|
+
| Title 1 | 28pt | Bold | Section titles |
|
|
164
|
+
| Title 3 | 20pt | Semibold | Subtitles |
|
|
165
|
+
| Body | 17pt | Regular | Main content |
|
|
166
|
+
| Callout | 16pt | Regular | Secondary content |
|
|
167
|
+
| Caption 1 | 12pt | Regular | Labels, timestamps |
|
|
168
|
+
|
|
169
|
+
### Android (Roboto / Material 3)
|
|
170
|
+
|
|
171
|
+
| Style | Size | Weight | Usage |
|
|
172
|
+
|-------|------|--------|-------|
|
|
173
|
+
| Display Large | 57sp | Regular | Hero text |
|
|
174
|
+
| Headline Medium | 28sp | Regular | Section titles |
|
|
175
|
+
| Title Large | 22sp | Regular | Card titles |
|
|
176
|
+
| Body Large | 16sp | Regular | Main content |
|
|
177
|
+
| Body Medium | 14sp | Regular | Supporting text |
|
|
178
|
+
| Label Small | 11sp | Medium | Captions, chips |
|
|
179
|
+
|
|
180
|
+
**Critical rules:**
|
|
181
|
+
- iOS: Dynamic Type is MANDATORY, not optional
|
|
182
|
+
- Android: always use `sp` for text, `dp` for everything else
|
|
183
|
+
- Test at 200% font scale — UI must not break
|
|
184
|
+
- Dark mode: text appears thinner (halation) — consider medium weight
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Touch & Thumb Zone
|
|
189
|
+
|
|
190
|
+
### Touch Targets
|
|
191
|
+
|
|
192
|
+
| Platform | Minimum | Recommended | Critical Actions |
|
|
193
|
+
|----------|---------|-------------|-----------------|
|
|
194
|
+
| iOS (HIG) | 44×44 pt | 48×48 pt | 56+ pt |
|
|
195
|
+
| Android (Material) | 48×48 dp | 48×48 dp | 56+ dp |
|
|
196
|
+
| Between targets | 8 dp gap | 12 dp gap | |
|
|
197
|
+
| WCAG 2.2 | 44×44 px | — | — |
|
|
198
|
+
|
|
199
|
+
**Rule:** Use `hitSlop` (RN) or `MaterialTapTargetSize.padded` (Flutter) if visual size is smaller.
|
|
200
|
+
|
|
201
|
+
### Thumb Zone (One-Handed Use)
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
┌─────────────────────────┐
|
|
205
|
+
│ HARD HARD │ ← Top corners (status, settings)
|
|
206
|
+
│ │
|
|
207
|
+
│ OK OK │ ← Middle area
|
|
208
|
+
│ │
|
|
209
|
+
│ EASY ██ EASY │ ← Bottom = PRIMARY CTAs go here
|
|
210
|
+
│ THUMB │
|
|
211
|
+
│ [Tab1] [Tab2] [Tab3] │ ← Tab bar in easy zone
|
|
212
|
+
└─────────────────────────┘
|
|
213
|
+
|
|
214
|
+
49% of users hold phone one-handed.
|
|
215
|
+
Primary CTA → bottom.
|
|
216
|
+
Destructive actions → top-left (hardest to reach = safer).
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Haptic Feedback
|
|
220
|
+
|
|
221
|
+
| Action | iOS | Android |
|
|
222
|
+
|--------|-----|---------|
|
|
223
|
+
| Toggle/selection | `selection` (light) | `TICK` |
|
|
224
|
+
| Button tap | `medium` | `CLICK` |
|
|
225
|
+
| Important confirm | `heavy` | `HEAVY_CLICK` |
|
|
226
|
+
| Success | `success` | `DOUBLE_CLICK` |
|
|
227
|
+
| Error | `error` | `REJECT` |
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Navigation Patterns
|
|
232
|
+
|
|
233
|
+
### Decision Tree
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
3-5 top-level sections → Tab Bar / Bottom Navigation
|
|
237
|
+
Deep hierarchy (>2 levels) → Stack Navigation
|
|
238
|
+
Many destinations (>5) → Drawer Navigation
|
|
239
|
+
Single linear flow → Stack only (wizard/onboarding)
|
|
240
|
+
Tablet / Foldable → Navigation Rail + List-Detail
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Tab Bar Rules
|
|
244
|
+
|
|
245
|
+
| Rule | iOS | Android |
|
|
246
|
+
|------|-----|---------|
|
|
247
|
+
| Max items | 5 | 5 |
|
|
248
|
+
| Height | 49pt | 80dp |
|
|
249
|
+
| Icon size | 25×25pt (SF Symbols) | 24dp (Material Symbols) |
|
|
250
|
+
| Labels | Always visible | Always visible |
|
|
251
|
+
| Active state | Filled icon + tint | Filled icon + indicator pill |
|
|
252
|
+
| On tab switch | Preserve each tab's navigation stack |
|
|
253
|
+
| Deep link | Construct full back stack |
|
|
254
|
+
|
|
255
|
+
### Critical Navigation Rules
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
✅ Each tab maintains its own navigation stack
|
|
259
|
+
✅ System back button works predictably
|
|
260
|
+
✅ Deep link URLs match navigation path
|
|
261
|
+
⛔ NEVER use modals for everything — use push navigation
|
|
262
|
+
⛔ NEVER reset tab stack on tab switch
|
|
263
|
+
⛔ NEVER override platform back gesture
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Screen Templates
|
|
269
|
+
|
|
270
|
+
### 1. Login Screen
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
┌─────────────────────────┐
|
|
274
|
+
│ │
|
|
275
|
+
│ [Logo] │
|
|
276
|
+
│ App Name / Tagline │
|
|
277
|
+
│ │
|
|
278
|
+
│ ┌───────────────────┐ │
|
|
279
|
+
│ │ Email │ │
|
|
280
|
+
│ └───────────────────┘ │
|
|
281
|
+
│ ┌───────────────────┐ │
|
|
282
|
+
│ │ Password 👁 │ │
|
|
283
|
+
│ └───────────────────┘ │
|
|
284
|
+
│ │
|
|
285
|
+
│ [ Forgot password? ] │
|
|
286
|
+
│ │
|
|
287
|
+
│ ┌───────────────────┐ │
|
|
288
|
+
│ │ Sign In │ │ ← Primary CTA (full width)
|
|
289
|
+
│ └───────────────────┘ │
|
|
290
|
+
│ │
|
|
291
|
+
│ ── or continue with ── │
|
|
292
|
+
│ [Google] [Apple] [FB] │ ← Social login row
|
|
293
|
+
│ │
|
|
294
|
+
│ Don't have account? │
|
|
295
|
+
│ [Sign Up] │
|
|
296
|
+
└─────────────────────────┘
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Rules:** `KeyboardAvoidingView` (RN) / `SingleChildScrollView` (Flutter). Password toggle. Disable button during loading. Show inline error below input, not toast.
|
|
300
|
+
|
|
301
|
+
### 2. Home / Feed
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
┌─────────────────────────┐
|
|
305
|
+
│ [☰] Home [🔔][👤]│ ← Header: menu, title, actions
|
|
306
|
+
├─────────────────────────┤
|
|
307
|
+
│ ┌───────────────────┐ │
|
|
308
|
+
│ │ 🔍 Search... │ │
|
|
309
|
+
│ └───────────────────┘ │
|
|
310
|
+
│ [Filter chips scrollH] │ ← Horizontal scroll filter
|
|
311
|
+
│ ┌───────────────────┐ │
|
|
312
|
+
│ │ Card 1 │ │
|
|
313
|
+
│ │ Image + Title │ │ ← FlatList / ListView.builder
|
|
314
|
+
│ │ Subtitle + Meta │ │
|
|
315
|
+
│ └───────────────────┘ │
|
|
316
|
+
│ ┌───────────────────┐ │
|
|
317
|
+
│ │ Card 2 │ │
|
|
318
|
+
│ └───────────────────┘ │
|
|
319
|
+
│ ... │
|
|
320
|
+
├─────────────────────────┤
|
|
321
|
+
│ [🏠] [🔍] [➕] [💬] [👤]│ ← Bottom tab bar
|
|
322
|
+
└─────────────────────────┘
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Rules:** `FlatList` / `ListView.builder` — NEVER `ScrollView` for dynamic lists. Pull-to-refresh. Pagination. Loading skeleton. Empty state. `key`/`Key` on each item.
|
|
326
|
+
|
|
327
|
+
### 3. Detail Screen
|
|
328
|
+
|
|
329
|
+
```
|
|
330
|
+
┌─────────────────────────┐
|
|
331
|
+
│ [←] Detail [⋮] │
|
|
332
|
+
├─────────────────────────┤
|
|
333
|
+
│ ┌───────────────────┐ │
|
|
334
|
+
│ │ Hero Image │ │ ← Full width, 16:9
|
|
335
|
+
│ └───────────────────┘ │
|
|
336
|
+
│ Title (heading) │
|
|
337
|
+
│ Subtitle / category │
|
|
338
|
+
│ ★★★★☆ 4.2 (128) │
|
|
339
|
+
│ │
|
|
340
|
+
│ Description text... │
|
|
341
|
+
│ │
|
|
342
|
+
│ ── Related ────────── │
|
|
343
|
+
│ [Card] [Card] [Card]→ │ ← Horizontal scroll
|
|
344
|
+
├─────────────────────────┤
|
|
345
|
+
│ [$29.99] [Add to Cart]│ ← Sticky bottom CTA
|
|
346
|
+
└─────────────────────────┘
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Rules:** `ScrollView` OK (single item). Sticky bottom bar outside scroll. Safe area at bottom. Share/bookmark in header.
|
|
350
|
+
|
|
351
|
+
### 4. Profile / Settings
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
┌─────────────────────────┐
|
|
355
|
+
│ [←] Profile [Edit] │
|
|
356
|
+
├─────────────────────────┤
|
|
357
|
+
│ ┌─────┐ │
|
|
358
|
+
│ │ 👤 │ │ ← Avatar (circular)
|
|
359
|
+
│ └─────┘ │
|
|
360
|
+
│ John Doe │
|
|
361
|
+
│ john@email.com │
|
|
362
|
+
│ ┌───────────────────┐ │
|
|
363
|
+
│ │ Account > │ │
|
|
364
|
+
│ ├───────────────────┤ │
|
|
365
|
+
│ │ Notifications > │ │ ← Grouped sections with chevrons
|
|
366
|
+
│ ├───────────────────┤ │
|
|
367
|
+
│ │ Appearance > │ │
|
|
368
|
+
│ ├───────────────────┤ │
|
|
369
|
+
│ │ Privacy > │ │
|
|
370
|
+
│ └───────────────────┘ │
|
|
371
|
+
│ ┌───────────────────┐ │
|
|
372
|
+
│ │ Terms > │ │
|
|
373
|
+
│ ├───────────────────┤ │
|
|
374
|
+
│ │ Help > │ │
|
|
375
|
+
│ └───────────────────┘ │
|
|
376
|
+
│ [ Sign Out ] │ ← Red/destructive
|
|
377
|
+
│ App v2.1.0 │
|
|
378
|
+
└─────────────────────────┘
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### 5. Onboarding (Swipeable)
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
┌─────────────────────────┐
|
|
385
|
+
│ [Skip] │
|
|
386
|
+
│ ┌───────────┐ │
|
|
387
|
+
│ │ Illustration │ │
|
|
388
|
+
│ └───────────┘ │
|
|
389
|
+
│ Welcome to AppName │
|
|
390
|
+
│ Short description │
|
|
391
|
+
│ ● ○ ○ │ ← Page indicator dots
|
|
392
|
+
│ ┌───────────────────┐ │
|
|
393
|
+
│ │ Get Started │ │ ← Last page: primary CTA
|
|
394
|
+
│ └───────────────────┘ │
|
|
395
|
+
└─────────────────────────┘
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**Rules:** 3-5 pages max. Skip always visible. Last page = CTA. Store `hasSeenOnboarding`.
|
|
399
|
+
|
|
400
|
+
### 6. Form / Multi-Step
|
|
401
|
+
|
|
402
|
+
```
|
|
403
|
+
┌─────────────────────────┐
|
|
404
|
+
│ [←] Create [Save] │
|
|
405
|
+
├─────────────────────────┤
|
|
406
|
+
│ Step 1 of 3 ●●○ │ ← Progress indicator
|
|
407
|
+
│ │
|
|
408
|
+
│ Title * │
|
|
409
|
+
│ ┌───────────────────┐ │
|
|
410
|
+
│ │ │ │
|
|
411
|
+
│ └───────────────────┘ │
|
|
412
|
+
│ ⚠ Title is required │ ← Inline error (red, below input)
|
|
413
|
+
│ │
|
|
414
|
+
│ Category │
|
|
415
|
+
│ ┌───────────────────┐ │
|
|
416
|
+
│ │ Select... ▼│ │
|
|
417
|
+
│ └───────────────────┘ │
|
|
418
|
+
│ │
|
|
419
|
+
│ Description │
|
|
420
|
+
│ ┌───────────────────┐ │
|
|
421
|
+
│ │ │ │
|
|
422
|
+
│ │ │ │
|
|
423
|
+
│ └───────────────────┘ │
|
|
424
|
+
│ 0/500 │ ← Character count
|
|
425
|
+
│ │
|
|
426
|
+
├─────────────────────────┤
|
|
427
|
+
│ [Back] [Continue] │ ← Sticky bottom
|
|
428
|
+
└─────────────────────────┘
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Rules:** Validate on blur. Show errors inline below input. Disable submit while loading. `KeyboardAvoidingView`. Mark required fields with `*`. Unsaved changes warning on back.
|
|
432
|
+
|
|
433
|
+
### 7. Chat / Messages
|
|
434
|
+
|
|
435
|
+
```
|
|
436
|
+
┌─────────────────────────┐
|
|
437
|
+
│ [←] John Doe [📞][⋮]│
|
|
438
|
+
├─────────────────────────┤
|
|
439
|
+
│ Today │
|
|
440
|
+
│ │
|
|
441
|
+
│ ┌─────────────┐ │
|
|
442
|
+
│ │ Hey, how are │ │ ← Received (left, gray bg)
|
|
443
|
+
│ │ you? │ │
|
|
444
|
+
│ └─────────────┘ 10:30 │
|
|
445
|
+
│ │
|
|
446
|
+
│ ┌─────────────┐ │
|
|
447
|
+
│ │ I'm good! │ │ ← Sent (right, primary color)
|
|
448
|
+
│ │ Thanks │ │
|
|
449
|
+
│ └─────────────┘ │
|
|
450
|
+
│ 10:31 │
|
|
451
|
+
│ │
|
|
452
|
+
│ ┌─────────────┐ │
|
|
453
|
+
│ │ ··· │ │ ← Typing indicator
|
|
454
|
+
│ └─────────────┘ │
|
|
455
|
+
├─────────────────────────┤
|
|
456
|
+
│ [+] [Message... ] [→]│ ← Input bar + send
|
|
457
|
+
└─────────────────────────┘
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**Rules:** `FlatList inverted` (RN) / `ListView reverse` (Flutter). Auto-scroll on new message. Typing indicator. Timestamp grouping (Today/Yesterday/date). Input bar above keyboard.
|
|
461
|
+
|
|
462
|
+
### 8. Search Results
|
|
463
|
+
|
|
464
|
+
```
|
|
465
|
+
┌─────────────────────────┐
|
|
466
|
+
│ [←] ┌───────────────┐ │
|
|
467
|
+
│ │ pizza nearby ✕ │ │ ← Search with clear button
|
|
468
|
+
│ └───────────────┘ │
|
|
469
|
+
├─────────────────────────┤
|
|
470
|
+
│ Recent: [sushi] [tacos]│ ← Search history chips
|
|
471
|
+
│ │
|
|
472
|
+
│ 3 results for "pizza" │
|
|
473
|
+
│ ┌───────────────────┐ │
|
|
474
|
+
│ │ 🍕 Pizza Palace │ │
|
|
475
|
+
│ │ ★4.5 · 0.3mi · $$ │ │
|
|
476
|
+
│ └───────────────────┘ │
|
|
477
|
+
│ ┌───────────────────┐ │
|
|
478
|
+
│ │ 🍕 Mario's │ │
|
|
479
|
+
│ └───────────────────┘ │
|
|
480
|
+
└─────────────────────────┘
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
**Rules:** Debounce search input (300ms+). Show recent searches. Show "No results" with suggestions, never blank. Autocomplete dropdown as user types.
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Component Patterns
|
|
488
|
+
|
|
489
|
+
### Bottom Sheet
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
// RN: @gorhom/bottom-sheet
|
|
493
|
+
<BottomSheet snapPoints={['25%', '50%', '90%']}>
|
|
494
|
+
<BottomSheetView>{/* content */}</BottomSheetView>
|
|
495
|
+
</BottomSheet>
|
|
496
|
+
|
|
497
|
+
// Flutter: showModalBottomSheet
|
|
498
|
+
showModalBottomSheet(
|
|
499
|
+
context: context,
|
|
500
|
+
isScrollControlled: true,
|
|
501
|
+
builder: (context) => DraggableScrollableSheet(...),
|
|
502
|
+
);
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Empty State
|
|
506
|
+
|
|
507
|
+
```
|
|
508
|
+
┌─────────────────────────┐
|
|
509
|
+
│ [Illustration] │
|
|
510
|
+
│ No items yet │ ← Clear title
|
|
511
|
+
│ Add your first item │ ← Helpful subtitle
|
|
512
|
+
│ to get started. │
|
|
513
|
+
│ [ + Add Item ] │ ← Action button
|
|
514
|
+
└─────────────────────────┘
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
**Rule:** Every list screen MUST have an empty state. Never show blank screen.
|
|
518
|
+
|
|
519
|
+
### Loading Skeleton
|
|
520
|
+
|
|
521
|
+
```
|
|
522
|
+
Show skeleton ONLY when there is no data to display.
|
|
523
|
+
If cached data exists → show stale data + refresh indicator.
|
|
524
|
+
Skeleton shape MUST match final UI layout.
|
|
525
|
+
Show 3-5 skeleton items.
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### Toast / Snackbar
|
|
529
|
+
|
|
530
|
+
```
|
|
531
|
+
Position: bottom (Android) or top (iOS)
|
|
532
|
+
Duration: 3s (info), 5s (error), persistent (action required)
|
|
533
|
+
Actions: max 1 button ("Undo", "Retry")
|
|
534
|
+
Never: block UI or require dismiss for non-critical info
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
### Confirmation Dialog
|
|
538
|
+
|
|
539
|
+
```
|
|
540
|
+
Destructive actions (delete, sign out) → ALWAYS confirm first.
|
|
541
|
+
Title: "Delete item?"
|
|
542
|
+
Message: "This action cannot be undone."
|
|
543
|
+
Actions: [Cancel] [Delete] ← destructive button in red
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## Dark Mode
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
// RN: useColorScheme()
|
|
552
|
+
const scheme = useColorScheme(); // 'light' | 'dark'
|
|
553
|
+
const bg = scheme === 'dark' ? colors.dark.background : colors.background;
|
|
554
|
+
|
|
555
|
+
// Flutter: Theme.of(context).brightness
|
|
556
|
+
final isDark = Theme.of(context).brightness == Brightness.dark;
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**Rules:**
|
|
560
|
+
- NEVER hardcode colors — always semantic tokens
|
|
561
|
+
- Dark text on dark bg: use `#E0E0E0`, NOT pure `#FFFFFF` (eye strain)
|
|
562
|
+
- Shadows: reduce or remove (invisible on dark backgrounds)
|
|
563
|
+
- Borders: use lighter shade (`#38383A`, not `#E5E5EA`)
|
|
564
|
+
- Images: add dark variant or semi-transparent overlay
|
|
565
|
+
- Elevation in dark = slightly lighter surface (Material guideline)
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## Animation Guidelines
|
|
570
|
+
|
|
571
|
+
| Animation | Duration | Easing |
|
|
572
|
+
|-----------|----------|--------|
|
|
573
|
+
| Button press | 100ms | ease-out |
|
|
574
|
+
| Screen transition | 300ms | ease-in-out |
|
|
575
|
+
| Bottom sheet open | 250ms | spring (damping 0.8) |
|
|
576
|
+
| Fade in content | 200ms | ease-in |
|
|
577
|
+
| List item appear | 150ms stagger | ease-out |
|
|
578
|
+
|
|
579
|
+
**Rules:**
|
|
580
|
+
- `useNativeDriver: true` (RN) — always for transforms/opacity
|
|
581
|
+
- 60 FPS target — no layout animations on main thread
|
|
582
|
+
- Animate ONLY `transform` and `opacity` (never `width`/`height`/`top`/`left`)
|
|
583
|
+
- Reduce motion: `AccessibilityInfo.isReduceMotionEnabled` (RN) / `MediaQuery.disableAnimations` (Flutter)
|
|
584
|
+
- No animation > 500ms (feels sluggish)
|
|
585
|
+
- Never apply universal `transition: all` — specify properties
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
## Accessibility Checklist
|
|
590
|
+
|
|
591
|
+
| Check | RN | Flutter |
|
|
592
|
+
|-------|-----|---------|
|
|
593
|
+
| Screen reader label | `accessibilityLabel` | `Semantics(label:)` |
|
|
594
|
+
| Button role | `accessibilityRole="button"` | `Semantics(button: true)` |
|
|
595
|
+
| Image alt text | `accessible={true} accessibilityLabel` | `Semantics(image: true, label:)` |
|
|
596
|
+
| Focus order | `accessibilityElementsHidden` | `ExcludeSemantics` |
|
|
597
|
+
| Color contrast | 4.5:1 (text), 3:1 (large text) | Same ratios |
|
|
598
|
+
| Font scaling | Support Dynamic Type | `MediaQuery.textScaleFactor` |
|
|
599
|
+
| State changes | `accessibilityLiveRegion` | `Semantics(liveRegion: true)` |
|
|
600
|
+
|
|
601
|
+
**Rules:**
|
|
602
|
+
- Every interactive element needs a label
|
|
603
|
+
- Every image needs alt text (or `decorative` flag)
|
|
604
|
+
- Never rely on color alone to convey meaning (add icons/text)
|
|
605
|
+
- Test with VoiceOver (iOS) and TalkBack (Android)
|
|
606
|
+
- Test at 200% font scale
|
|
607
|
+
- Visible focus indicators on all interactive elements
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## UX Anti-Patterns (Never Do These)
|
|
612
|
+
|
|
613
|
+
| Anti-Pattern | Why | Fix |
|
|
614
|
+
|-------------|------|-----|
|
|
615
|
+
| `ScrollView` for long lists | Memory explosion | `FlatList` / `ListView.builder` |
|
|
616
|
+
| Hardcoded colors | Breaks dark mode | Semantic design tokens |
|
|
617
|
+
| Touch target < 44pt | Frustrating taps | Min 44pt iOS / 48dp Android |
|
|
618
|
+
| No empty state | Confusing blank screen | Illustration + message + CTA |
|
|
619
|
+
| Error only at form top | User can't find which field | Inline error below each input |
|
|
620
|
+
| Silent failures | User doesn't know what happened | Toast/banner with retry option |
|
|
621
|
+
| Modal for everything | Feels trapped | Stack navigation for content flow |
|
|
622
|
+
| Auto-play video | Drains battery + data | Click-to-play or pause off-screen |
|
|
623
|
+
| Placeholder as only label | Disappears on focus | Persistent label above input |
|
|
624
|
+
| Layout shift on load | Jarring CLS | Reserve space / skeleton placeholders |
|
|
625
|
+
| `100vh` on mobile | Address bar overlap | `dvh` or platform safe area |
|
|
626
|
+
| No loading indicator | UI feels frozen | Skeleton or spinner after 300ms |
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## Self-Critique Protocol
|
|
631
|
+
|
|
632
|
+
**After generating any screen, ask:**
|
|
633
|
+
|
|
634
|
+
```
|
|
635
|
+
1. "Does this look like a default template?" → If yes, customize tokens/spacing
|
|
636
|
+
2. "Would a user know what to do first?" → If unclear, strengthen visual hierarchy
|
|
637
|
+
3. "What happens on error/empty/loading?" → If any missing, add them
|
|
638
|
+
4. "Does primary CTA sit in thumb zone?" → If not, move to bottom
|
|
639
|
+
5. "Can I use this in dark mode?" → If hardcoded colors, fix
|
|
640
|
+
6. "Does it pass 44pt touch targets?" → If buttons are small, enlarge
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
**If the screen could be mistaken for a tutorial demo, it needs more design work.**
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
## Screen Sizing Reference
|
|
648
|
+
|
|
649
|
+
| Device | Width (pt/dp) | Safe Area Top | Safe Area Bottom |
|
|
650
|
+
|--------|--------------|---------------|-----------------|
|
|
651
|
+
| iPhone SE | 375 | 20 | 0 |
|
|
652
|
+
| iPhone 15 | 393 | 59 | 34 |
|
|
653
|
+
| iPhone 15 Pro Max | 430 | 59 | 34 |
|
|
654
|
+
| Android small | 360 | 24 (status) | 48 (nav bar) |
|
|
655
|
+
| Android large | 412 | 24 | 48 |
|
|
656
|
+
| iPad | 768-1024 | 24 | 20 |
|
|
657
|
+
|
|
658
|
+
**Rules:**
|
|
659
|
+
- Always use `SafeAreaView` (RN) / `SafeArea` (Flutter)
|
|
660
|
+
- Design for 375pt width (smallest common), scale up
|
|
661
|
+
- Test on smallest AND largest device
|
|
662
|
+
- Scrollable content — never assume fixed heights
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
> Mobile is NOT a small desktop. Touch-first, thumb-zone aware, battery conscious.
|
|
667
|
+
> Tokens first, 4 states always, platform rules respected, accessibility mandatory.
|