Mobile Design System
Philosophy: Touch-first. Battery-conscious. Platform-respectful. Offline-capable.
Core Principle: Mobile is NOT a small desktop. THINK mobile constraints, ASK platform choice.
๐ง Runtime Scripts
Execute these for validation (don't read, just run):
| Script |
Purpose |
Usage |
scripts/mobile_audit.py |
Mobile UX & Touch Audit |
python scripts/mobile_audit.py <project_path> |
๐ด MANDATORY: Read Reference Files Before Working!
โ DO NOT start development until you read the relevant files:
Universal (Always Read)
| File |
Content |
Status |
| mobile-design-thinking.md |
โ ๏ธ ANTI-MEMORIZATION: Forces thinking, prevents AI defaults |
โฌ CRITICAL FIRST |
| touch-psychology.md |
Fitts' Law, gestures, haptics, thumb zone |
โฌ CRITICAL |
| mobile-performance.md |
RN/Flutter performance, 60fps, memory |
โฌ CRITICAL |
| mobile-backend.md |
Push notifications, offline sync, mobile API |
โฌ CRITICAL |
| mobile-testing.md |
Testing pyramid, E2E, platform-specific |
โฌ CRITICAL |
| mobile-debugging.md |
Native vs JS debugging, Flipper, Logcat |
โฌ CRITICAL |
| mobile-navigation.md |
Tab/Stack/Drawer, deep linking |
โฌ Read |
| mobile-typography.md |
System fonts, Dynamic Type, a11y |
โฌ Read |
| mobile-color-system.md |
OLED, dark mode, battery-aware |
โฌ Read |
| decision-trees.md |
Framework/state/storage selection |
โฌ Read |
๐ง mobile-design-thinking.md is PRIORITY! This file ensures AI thinks instead of using memorized patterns.
Platform-Specific (Read Based on Target)
| Platform |
File |
Content |
When to Read |
| iOS |
platform-ios.md |
Human Interface Guidelines, SF Pro, SwiftUI patterns |
Building for iPhone/iPad |
| Android |
platform-android.md |
Material Design 3, Roboto, Compose patterns |
Building for Android |
| Cross-Platform |
Both above |
Platform divergence points |
React Native / Flutter |
๐ด If building for iOS โ Read platform-ios.md FIRST!
๐ด If building for Android โ Read platform-android.md FIRST!
๐ด If cross-platform โ Read BOTH and apply conditional platform logic!
โ ๏ธ CRITICAL: ASK BEFORE ASSUMING (MANDATORY)
STOP! If the user's request is open-ended, DO NOT default to your favorites.
You MUST Ask If Not Specified:
| Aspect |
Ask |
Why |
| Platform |
"iOS, Android, or both?" |
Affects EVERY design decision |
| Framework |
"React Native, Flutter, or native?" |
Determines patterns and tools |
| Navigation |
"Tab bar, drawer, or stack-based?" |
Core UX decision |
| State |
"What state management? (Zustand/Redux/Riverpod/BLoC?)" |
Architecture foundation |
| Offline |
"Does this need to work offline?" |
Affects data strategy |
| Target devices |
"Phone only, or tablet support?" |
Layout complexity |
โ AI MOBILE ANTI-PATTERNS (YASAK LฤฐSTESฤฐ)
๐ซ These are AI default tendencies that MUST be avoided!
Performance Sins
| โ NEVER DO |
Why It's Wrong |
โ
ALWAYS DO |
| ScrollView for long lists |
Renders ALL items, memory explodes |
Use FlatList / FlashList / ListView.builder |
| Inline renderItem function |
New function every render, all items re-render |
useCallback + React.memo |
| Missing keyExtractor |
Index-based keys cause bugs on reorder |
Unique, stable ID from data |
| Skip getItemLayout |
Async layout = janky scroll |
Provide when items have fixed height |
| setState() everywhere |
Unnecessary widget rebuilds |
Targeted state, const constructors |
| Native driver: false |
Animations blocked by JS thread |
useNativeDriver: true always |
| console.log in production |
Blocks JS thread severely |
Remove before release build |
| Skip React.memo/const |
Every item re-renders on any change |
Memoize list items ALWAYS |
Touch/UX Sins
| โ NEVER DO |
Why It's Wrong |
โ
ALWAYS DO |
| Touch target < 44px |
Impossible to tap accurately, frustrating |
Minimum 44pt (iOS) / 48dp (Android) |
| Spacing < 8px between targets |
Accidental taps on neighbors |
Minimum 8-12px gap |
| Gesture-only interactions |
Motor impaired users excluded |
Always provide button alternative |
| No loading state |
User thinks app crashed |
ALWAYS show loading feedback |
| No error state |
User stuck, no recovery path |
Show error with retry option |
| No offline handling |
Crash/block when network lost |
Graceful degradation, cached data |
| Ignore platform conventions |
Users confused, muscle memory broken |
iOS feels iOS, Android feels Android |
Security Sins
| โ NEVER DO |
Why It's Wrong |
โ
ALWAYS DO |
| Token in AsyncStorage |
Easily accessible, stolen on rooted device |
SecureStore / Keychain / EncryptedSharedPreferences |
| Hardcode API keys |
Reverse engineered from APK/IPA |
Environment variables, secure storage |
| Skip SSL pinning |
MITM attacks possible |
Pin certificates in production |
| Log sensitive data |
Logs can be extracted |
Never log tokens, passwords, PII |
Architecture Sins
| โ NEVER DO |
Why It's Wrong |
โ
ALWAYS DO |
| Business logic in UI |
Untestable, unmaintainable |
Service layer separation |
| Global state for everything |
Unnecessary re-renders, complexity |
Local state default, lift when needed |
| Deep linking as afterthought |
Notifications, shares broken |
Plan deep links from day one |
| Skip dispose/cleanup |
Memory leaks, zombie listeners |
Clean up subscriptions, timers |
๐ฑ Platform Decision Matrix
When to Unify vs Diverge
UNIFY (same on both) DIVERGE (platform-specific)
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโ
Business Logic โ
Always -
Data Layer โ
Always -
Core Features โ
Always -
Navigation - โ
iOS: edge swipe, Android: back button
Gestures - โ
Platform-native feel
Icons - โ
SF Symbols vs Material Icons
Date Pickers - โ
Native pickers feel right
Modals/Sheets - โ
iOS: bottom sheet vs Android: dialog
Typography - โ
SF Pro vs Roboto (or custom)
Error Dialogs - โ
Platform conventions for alerts
Quick Reference: Platform Defaults
| Element |
iOS |
Android |
| Primary Font |
SF Pro / SF Compact |
Roboto |
| Min Touch Target |
44pt ร 44pt |
48dp ร 48dp |
| Back Navigation |
Edge swipe left |
System back button/gesture |
| Bottom Tab Icons |
SF Symbols |
Material Symbols |
| Action Sheet |
UIActionSheet from bottom |
Bottom Sheet / Dialog |
| Progress |
Spinner |
Linear progress (Material) |
| Pull to Refresh |
Native UIRefreshControl |
SwipeRefreshLayout |
๐ง Mobile UX Psychology (Quick Reference)
Fitts' Law for Touch
Desktop: Cursor is precise (1px)
Mobile: Finger is imprecise (~7mm contact area)
โ Touch targets MUST be 44-48px minimum
โ Important actions in THUMB ZONE (bottom of screen)
โ Destructive actions AWAY from easy reach
Thumb Zone (One-Handed Usage)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HARD TO REACH โ โ Navigation, menu, back
โ (stretch) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ OK TO REACH โ โ Secondary actions
โ (natural) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ EASY TO REACH โ โ PRIMARY CTAs, tab bar
โ (thumb's natural arc) โ โ Main content interaction
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[ HOME ]
Mobile-Specific Cognitive Load
| Desktop |
Mobile Difference |
| Multiple windows |
ONE task at a time |
| Keyboard shortcuts |
Touch gestures |
| Hover states |
NO hover (tap or nothing) |
| Large viewport |
Limited space, scroll vertical |
| Stable attention |
Interrupted constantly |
For deep dive: touch-psychology.md
โก Performance Principles (Quick Reference)
React Native Critical Rules
const ListItem = React.memo(({ item }: { item: Item }) => (
<View style={styles.item}>
<Text>{item.title}</Text>
</View>
));
const renderItem = useCallback(
({ item }: { item: Item }) => <ListItem item={item} />,
[]
);
<FlatList
data={items}
renderItem={renderItem}
keyExtractor={(item) => item.id}
getItemLayout={(data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
})}
removeClippedSubviews={true}
maxToRenderPerBatch={10}
windowSize={5}
/>
Flutter Critical Rules
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
return const Column(
children: [
Text('Static content'),
MyConstantWidget(),
],
);
}
}