Mobile Native Standards Enforcer
This skill ensures all code adheres to the mobile native template's core principles. Apply these checks proactively when creating or modifying components, screens, or any UI code.
Before Making Changes
Run this validation checklist mentally or explicitly:
Standards Validation:
- [ ] No hardcoded colors (hex codes or Tailwind color classes)
- [ ] Using theme system via useTheme() hook
- [ ] Safe areas handled with useSafeAreaInsets() or layout components
- [ ] Platform-specific code uses correct patterns
- [ ] TypeScript strict mode compliant (no 'any' types)
- [ ] Animations use Reanimated 4
- [ ] Component is production-grade (if creating new component)
Core Principle Enforcement
1. Zero Hardcoded Colors
Rule: Every color must come from the theme system.
Check for violations:
- Hex codes:
#000000,#FFFFFF,#FF5733, etc. - RGB/RGBA values:
rgb(0,0,0),rgba(255,255,255,0.5) - Tailwind color classes:
bg-black,text-blue-500,border-red-600 - Named colors:
backgroundColor: 'red',color: 'white'
Correct pattern:
typescript1import { useTheme } from '@/hooks'; 2 3const { colors } = useTheme(); 4 5// For View/TouchableOpacity/Pressable 6<View style={{ backgroundColor: colors.background.primary }} /> 7 8// For Text 9<Text style={{ color: colors.text.primary }} /> 10 11// For borders 12<View style={{ borderColor: colors.border.default }} />
Available theme colors (check constants/colors.ts for complete list):
colors.background.*- primary, secondary, tertiary, elevatedcolors.text.*- primary, secondary, tertiary, inversecolors.border.*- default, subtle, emphasiscolors.surface.*- primary, secondary, overlaycolors.accent.*- primary, secondary, success, warning, error, info
If you see a violation: Stop and refactor to use theme colors before proceeding.
2. Safe Area Handling
Rule: Never use deprecated SafeAreaView from react-native.
Check for violations:
typescript1// ❌ WRONG - deprecated 2import { SafeAreaView } from 'react-native';
Correct patterns:
Option A: Use the hook (preferred for animations)
typescript1import { useSafeAreaInsets } from 'react-native-safe-area-context'; 2 3const insets = useSafeAreaInsets(); 4 5<View style={{ paddingTop: insets.top, paddingBottom: insets.bottom }}> 6 {/* content */} 7</View>
Option B: Use layout components
typescript1import { ScreenLayout } from '@/components/layouts'; 2 3<ScreenLayout> 4 {/* content - safe areas handled automatically */} 5</ScreenLayout>
3. Platform-Specific Code
Rule: Use correct patterns for platform differences.
For full component differences, use file extensions:
Component.ios.tsx // iOS 26 Liquid Glass implementation
Component.android.tsx // Android 16 Material 3 implementation
Component.web.tsx // Web fallback
Component.tsx // Base/shared logic or type exports
For minor differences, use Platform module:
typescript1import { Platform } from 'react-native'; 2 3const hitSlop = Platform.select({ 4 ios: { top: 10, bottom: 10, left: 10, right: 10 }, 5 android: 20, 6 default: 10 7}); 8 9// Or conditional logic 10if (Platform.OS === 'ios') { 11 // iOS-specific code 12} else if (Platform.OS === 'android') { 13 // Android-specific code 14}
4. Animation Standards
Rule: All animations must use Reanimated 4.
Check for violations:
- Using
Animatedfromreact-native(deprecated) - Using
LayoutAnimation(unreliable) - CSS transitions on web that don't work on native
Correct pattern:
typescript1import Animated, { 2 FadeIn, 3 FadeOut, 4 SlideInRight, 5 useAnimatedStyle, 6 withTiming 7} from 'react-native-reanimated'; 8 9// Entering/exiting animations 10<Animated.View entering={FadeIn} exiting={FadeOut}> 11 {/* content */} 12</Animated.View> 13 14// Custom animations 15const animatedStyle = useAnimatedStyle(() => ({ 16 opacity: withTiming(isVisible ? 1 : 0) 17}));
5. TypeScript Strict Mode
Rule: No any types. Use proper generics and type inference.
Check for violations:
typescript1// ❌ WRONG 2const data: any = fetchData(); 3function process(item: any) { }
Correct patterns:
typescript1// ✅ Use proper types 2interface User { 3 id: string; 4 name: string; 5} 6 7const data: User[] = fetchData(); 8 9// ✅ Use generics 10function process<T>(item: T): T { 11 return item; 12} 13 14// ✅ Use unknown for truly unknown types, then narrow 15const data: unknown = fetchData(); 16if (typeof data === 'object' && data !== null) { 17 // Now you can safely access properties 18}
Production-Grade Component Standards
When creating a new component in components/ui/, it must meet ALL criteria:
Component Checklist
Production Component Requirements:
- [ ] Fully typed with TypeScript strict mode
- [ ] Props interface exported and documented with JSDoc
- [ ] Supports light/dark mode via useTheme()
- [ ] Has variants (size, color, state) as needed
- [ ] Works on iOS, Android, and web
- [ ] Uses Reanimated 4 for animations
- [ ] Handles accessibility (accessibilityLabel, accessibilityRole)
- [ ] No hardcoded colors or spacing
- [ ] Uses centralized spacing from constants/spacing.ts
- [ ] Exported from components/ui/index.ts
Component Template
Use this structure for new components:
typescript1import React from 'react'; 2import { View, Text, Pressable } from 'react-native'; 3import { useTheme } from '@/hooks'; 4import { spacing } from '@/constants'; 5 6/** 7 * [Component description] 8 * 9 * @example 10 * ```tsx 11 * <YourComponent variant="primary" size="medium"> 12 * Content 13 * </YourComponent> 14 * ``` 15 */ 16 17interface YourComponentProps { 18 /** Component children */ 19 children?: React.ReactNode; 20 /** Visual variant */ 21 variant?: 'primary' | 'secondary' | 'tertiary'; 22 /** Size variant */ 23 size?: 'small' | 'medium' | 'large'; 24 /** Optional callback */ 25 onPress?: () => void; 26} 27 28export function YourComponent({ 29 children, 30 variant = 'primary', 31 size = 'medium', 32 onPress, 33}: YourComponentProps) { 34 const { colors } = useTheme(); 35 36 // Calculate styles based on props 37 const backgroundColor = variant === 'primary' 38 ? colors.accent.primary 39 : colors.background.secondary; 40 41 const paddingSize = size === 'small' 42 ? spacing.sm 43 : size === 'large' 44 ? spacing.lg 45 : spacing.md; 46 47 return ( 48 <Pressable 49 onPress={onPress} 50 style={{ 51 backgroundColor, 52 padding: paddingSize, 53 borderRadius: spacing.xs, 54 }} 55 accessibilityRole="button" 56 > 57 <Text style={{ color: colors.text.primary }}> 58 {children} 59 </Text> 60 </Pressable> 61 ); 62}
Common Violations and Fixes
Violation: Hardcoded Black Background
typescript1// ❌ WRONG 2<View style={{ backgroundColor: '#000000' }} /> 3<View className="bg-black" /> 4 5// ✅ CORRECT 6const { colors } = useTheme(); 7<View style={{ backgroundColor: colors.background.primary }} />
Violation: Using SafeAreaView from React Native
typescript1// ❌ WRONG 2import { SafeAreaView } from 'react-native'; 3 4<SafeAreaView> 5 <View>Content</View> 6</SafeAreaView> 7 8// ✅ CORRECT 9import { useSafeAreaInsets } from 'react-native-safe-area-context'; 10 11const insets = useSafeAreaInsets(); 12 13<View style={{ paddingTop: insets.top }}> 14 <View>Content</View> 15</View>
Violation: Any Type Usage
typescript1// ❌ WRONG 2function handleData(data: any) { 3 console.log(data.name); 4} 5 6// ✅ CORRECT 7interface Data { 8 name: string; 9} 10 11function handleData(data: Data) { 12 console.log(data.name); 13}
Violation: Magic Numbers for Spacing
typescript1// ❌ WRONG 2<View style={{ padding: 16, margin: 8 }} /> 3 4// ✅ CORRECT 5import { spacing } from '@/constants'; 6 7<View style={{ padding: spacing.md, margin: spacing.sm }} />
Violation: Using Animated from react-native
typescript1// ❌ WRONG 2import { Animated } from 'react-native'; 3 4const fadeAnim = new Animated.Value(0); 5 6// ✅ CORRECT 7import Animated, { FadeIn } from 'react-native-reanimated'; 8 9<Animated.View entering={FadeIn}> 10 {/* content */} 11</Animated.View>
Enforcement Workflow
When creating or modifying code:
- Before writing: Review relevant standards above
- While writing: Use theme system, proper imports, and types
- After writing: Run the validation checklist
- If violations found: Fix immediately before proceeding
Quick Reference
Colors: const { colors } = useTheme();
Safe Areas: const insets = useSafeAreaInsets();
Spacing: import { spacing } from '@/constants';
Platform: import { Platform } from 'react-native';
Animation: import Animated, { FadeIn } from 'react-native-reanimated';
Philosophy Reminder
This template prioritizes:
- Native first: iOS 26 Liquid Glass + Android 16 Material 3 Expressive
- No shortcuts: Three platform implementations if needed
- Design system discipline: Single source of truth for all styles
Enforce these principles in every change.