9.5 KiB
9.5 KiB
| 1 | No | Category | Issue | Keywords | Platform | Description | Do | Don't | Code Example Good | Code Example Bad | Severity |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 1 | Accessibility | Icon Button Labels | icon button accessibilityLabel | iOS/Android/React Native | Icon-only buttons must expose an accessible label | Set accessibilityLabel or label prop on icon buttons | Icon buttons without accessible names | <Pressable accessibilityLabel="Close"><XIcon /></Pressable> | <Pressable><XIcon /></Pressable> | Critical |
| 3 | 2 | Accessibility | Form Control Labels | form input label accessibilityLabel | iOS/Android/React Native | All inputs must have a visible label and an accessibility label | Pair Text label with input and set accessibilityLabel | Inputs with placeholder only | <View><Text>Email</Text><TextInput accessibilityLabel="Email address" /></View> | <TextInput placeholder="Email" /></View> | Critical |
| 4 | 3 | Accessibility | Role & Traits | accessibilityRole accessibilityTraits | iOS/Android/React Native | Interactive elements must expose correct roles/traits | Use accessibilityRole/button/link/checkbox etc. | Rely on generic views with no roles | <Pressable accessibilityRole="button">Submit</Pressable> | <View onTouchStart={submit}>Submit</View> | High |
| 5 | 4 | Accessibility | Dynamic Updates | accessibilityLiveRegion announce | iOS/Android/React Native | Async status updates should be announced to screen readers | Use accessibilityLiveRegion or announceForAccessibility | Update text silently with no announcement | <Text accessibilityLiveRegion="polite">{status}</Text> | <Text>{status}</Text> | Medium |
| 6 | 5 | Accessibility | Decorative Icons | accessible={false} importantForAccessibility | iOS/Android/React Native | Decorative icons should be hidden from screen readers | Mark decorative icons as not accessible | Have screen reader read every icon | <Icon accessible={false} importantForAccessibility="no" /> | <Icon /> | Medium |
| 7 | 6 | Touch | Touch Target Size | touch 44x44 hitSlop | iOS/Android/React Native | Primary touch targets must be at least 44x44pt | Increase hitSlop or padding to meet minimum | Small icons with tiny touch area | <Pressable hitSlop={10}><Icon /></Pressable> | <Pressable><Icon style={{ width: 16, height: 16 }} /></Pressable> | Critical |
| 8 | 7 | Touch | Touch Spacing | touch spacing gap 8px | iOS/Android/React Native | Adjacent touch targets need enough spacing | Keep at least 8dp spacing between touchables | Cluster many buttons with no gap | <View style={{ gap: 8 }}><Button ... /><Button ... /></View> | <View><Button ... /><Button ... /></View> | Medium |
| 9 | 8 | Touch | Gesture Conflicts | scroll swipe back gesture | iOS/Android/React Native | Custom gestures must not break system scroll/back | Reserve horizontal swipes for carousels | Full-screen custom swipe conflicting with back | HorizontalPager inside vertical ScrollView | PanResponder on full screen blocking back | High |
| 10 | 9 | Navigation | Back Behavior | back handler navigation stack | iOS/Android/React Native | Back navigation should be predictable and preserve state | Use navigation.goBack and keep screen state | Reset stack or exit app unexpectedly | onPress={() => navigation.goBack()} | BackHandler.exitApp() on first press | Critical |
| 11 | 10 | Navigation | Bottom Tabs | tab bar max items | iOS/Android/React Native | Bottom tab bar should have at most 5 primary items | Use 3–5 tabs and move extras to More/Settings | Overloaded tab bar with many icons | Home/Explore/Profile/Settings | Home/Explore/Shop/Cart/Profile/Settings/More | Medium |
| 12 | 11 | Navigation | Modal Escape | modal dismiss close affordance | iOS/Android/React Native | Modals/sheets must have clear close actions | Provide close button and swipe-down where platform expects | Trapping users in modal with no obvious exit | <Modal><Button title="Close" onPress={onClose} /></Modal> | <Modal><View>{children}</View></Modal> | High |
| 13 | 12 | State | Preserve Screen State | navigation preserve state | iOS/Android/React Native | Returning to a screen should restore its scroll and form state | Keep components mounted or persist state | Reset list scroll and form inputs on every visit | <Tab.Navigator screenOptions={{ unmountOnBlur: false }}> | <Tab.Screen options={{ unmountOnBlur: true }} /> | Medium |
| 14 | 13 | Feedback | Loading Indicators | activity indicator skeleton | iOS/Android/React Native | Show visible feedback during network operations | Use ActivityIndicator or skeleton for >300ms operations | Leave button and screen frozen | {loading ? <ActivityIndicator /> : <Button title="Save" />} | <Button title="Save" onPress={submit} /> // no loading | High |
| 15 | 14 | Feedback | Success Feedback | toast checkmark banner | iOS/Android/React Native | Confirm successful actions with brief feedback | Show toast/checkmark or banner | Complete actions silently with no confirmation | showToast('Saved successfully') | // silently update state only | Medium |
| 16 | 15 | Feedback | Error Feedback | inline error banner | iOS/Android/React Native | Show clear error messages near the problem | input-level error + summary banner | Only change border color with no explanation | <TextInput ... /><Text style={{color:'red'}}>{error}</Text> | <TextInput style={{borderColor:'red'}} /> | High |
| 17 | 16 | Forms | Inline Validation | onBlur validation | iOS/Android/React Native | Validate inputs on blur or submit with clear messaging | Validate onBlur and onSubmit | Validate on every keystroke causing jank | onBlur={() => validateEmail(value)} | onChangeText={v => validateEmail(v)} // every char | Medium |
| 18 | 17 | Forms | Keyboard Type | keyboardType returnKeyType | iOS/Android/React Native | Use appropriate keyboardType and returnKeyType | Match email/tel/number/search types | Use default keyboard for all inputs | <TextInput keyboardType="email-address" /> | <TextInput keyboardType="default" /> | Medium |
| 19 | 18 | Forms | Auto Focus & Next | autoFocus blurOnSubmit onSubmitEditing | iOS/Android/React Native | Guide users through form fields with Next/Done flows | Use onSubmitEditing to focus next input | Force users to tap each field manually | onSubmitEditing={() => nextRef.current?.focus()} | // no onSubmitEditing, manual tap only | Low |
| 20 | 19 | Forms | Password Visibility | secureTextEntry toggle | iOS/Android/React Native | Allow toggling password visibility securely | Provide Show/Hide icon toggling secureTextEntry | Force users to type blind with no option | <TextInput secureTextEntry={secure} /><Icon onPress={toggle} /> | <TextInput secureTextEntry /> // no toggle | Medium |
| 21 | 20 | Performance | Virtualize Long Lists | FlatList SectionList virtualization | iOS/Android/React Native | Use FlatList/SectionList for lists over ~50 items | Use keyExtractor and initialNumToRender appropriately | Render hundreds of items with ScrollView | <FlatList data={items} renderItem={...} /> | <ScrollView>{items.map(renderItem)}</ScrollView> | High |
| 22 | 21 | Performance | Image Size & Cache | Image resize cache | iOS/Android/React Native | Use correctly sized and cached images | Use Image component with proper resizeMode and caching | Load full-resolution images everywhere | <Image source={{uri}} resizeMode="cover" /> | <Image source={require('4k.png')} /> // small avatar | Medium |
| 23 | 22 | Performance | Debounce High-Freq Events | debounce scroll search | iOS/Android/React Native | Debounce scroll/search callbacks to avoid jank | Wrap handlers with debounce/throttle | Run heavy logic on every event | onScroll={debouncedHandleScroll} | onScroll={handleScrollHeavy} | Medium |
| 24 | 23 | Animation | Duration & Easing | animation duration easing | iOS/Android/React Native | Micro-interactions should be 150–300ms with native-like easing | Use ease-out for enter/ease-in for exit | Use long or linear animations for core UI | Animated.timing(..., { duration: 200, easing: Easing.out(Easing.quad) }) | Animated.timing(..., { duration: 800, easing: Easing.linear }) | Medium |
| 25 | 24 | Animation | Respect Reduced Motion | reduced motion accessibility | iOS/Android/React Native | Respect OS reduced-motion accessibility setting | Check reduceMotionEnabled and simplify animations | Ignore user motion preferences | if (reduceMotionEnabled) skipAnimation() | Always run complex parallax animations | Critical |
| 26 | 25 | Animation | Limited Continuous Motion | loop animation loader | iOS/Android/React Native | Reserve infinite animations for loaders and live data | Use looping only where necessary | Keep decorative elements looping forever | Animated.loop(loaderAnim) for ActivityIndicator | Animated.loop(bounceAnim) on background icons | Medium |
| 27 | 26 | Typography | Base Font Size | fontScale dynamic type | iOS/Android/React Native | Body text must be readable and support Dynamic Type | Use platform fontScale and at least 14–16pt base | Render critical text below 12pt | <Text style={{ fontSize: 16 }}>Body</Text> | <Text style={{ fontSize: 10 }}>Body</Text> | High |
| 28 | 27 | Typography | Dynamic Type Support | allowFontScaling adjustsFontSizeToFit | iOS/Android/React Native | Support system text scaling without breaking layout | Set allowFontScaling and test large text | Disable scaling on all text globally | <Text allowFontScaling>{label}</Text> | <Text allowFontScaling={false}>{label}</Text> | High |
| 29 | 28 | Safe Areas | Safe Area Insets | safe area insets notch gesture | iOS/Android/React Native | Content must not overlap notches/gesture bars | Wrap screens in SafeAreaView or apply insets | Place tappable content under system bars | <SafeAreaView style={{ flex: 1 }}><Screen /></SafeAreaView> | <View style={{ flex: 1 }}><Screen /></View> | High |
| 30 | 29 | Theming | Light/Dark Contrast | dark mode contrast tokens | iOS/Android/React Native | Ensure sufficient contrast in both light and dark themes | Use semantic tokens and test both themes | Reuse light-theme grays directly in dark mode | colors.textPrimaryDark = '#F9FAFB' | colors.textPrimaryDark = '#9CA3AF' on '#111827' | High |
| 31 | 30 | Anti-Pattern | No Gesture-Only Actions | gesture only hidden controls | iOS/Android/React Native | Don't rely solely on hidden gestures for core actions | Provide visible buttons in addition to gestures | Rely on swipe/shake only with no UI affordance | Swipe to delete + visible Delete button | Only shake device to undo with no UI | Critical |