--- name: rn-styling description: Styling patterns for React Native with NativeWind and BrandColors. Use when working with styles, themes, colors, responsive layouts, or platform-specific UI in Expo/React Native. --- # React Native Styling ## Problem Statement React Native styling differs fundamentally from web CSS. NativeWind bridges the gap but has its own rules. This codebase uses a hybrid approach: BrandColors for semantic colors, NativeWind for layout utilities. --- ## Pattern: BrandColors vs NativeWind Classes **Rule:** Use BrandColors for semantic colors, NativeWind for layout/spacing. ```typescript // ✅ CORRECT: Hybrid approach Title // ❌ WRONG: Hardcoded hex colors (violation scanner blocks this) // ❌ WRONG: NativeWind color classes for brand colors // ✅ ACCEPTABLE: NativeWind brand aliases (if configured) ``` **When to use which:** | Use Case | Approach | |----------|----------| | Brand colors (primary, secondary) | `BrandColors.primary` | | Background colors | `BrandColors.background` | | Text colors | `BrandColors.textPrimary`, `textSecondary` | | Layout (flex, padding, margin) | NativeWind classes | | Borders, radius | NativeWind classes | | Shadows | Style object (NativeWind shadows limited on iOS) | --- ## Pattern: Theme-Aware Colors **Problem:** Supporting light/dark mode with BrandColors. ```typescript // BrandColors.ts exports both themes import { BrandColors, BrandColorsDark } from '@/constants/BrandColors'; // Hook for current theme colors import { useColorScheme } from 'react-native'; function useThemeColors() { const colorScheme = useColorScheme(); return colorScheme === 'dark' ? BrandColorsDark : BrandColors; } // Component usage function ThemedCard({ title }: { title: string }) { const colors = useThemeColors(); return ( {title} ); } ``` --- ## Pattern: NativeWind Class Ordering **Problem:** Unlike web CSS, React Native doesn't cascade. Last class wins for conflicting properties. ```typescript // Class order matters! // p-2 wins (last) // p-4 wins (last) // Conditional classes - be explicit // If isCompact: "p-4 p-2" → p-2 wins ✅ // Merging className props interface Props { className?: string; } function Card({ className }: Props) { // Parent classes override defaults (they come last) return ; } // Usage: → p-8 wins over p-4 ``` --- ## Pattern: Platform-Specific Styles ```typescript import { Platform, StyleSheet } from 'react-native'; // Option 1: Platform.select const styles = StyleSheet.create({ shadow: Platform.select({ ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, }, android: { elevation: 4, }, }), }); // Option 2: Platform.OS check // Option 3: NativeWind platform prefixes ``` --- ## Pattern: Safe Area Handling ```typescript import { SafeAreaView } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; // Option 1: SafeAreaView wrapper (simplest) function Screen() { return ( ); } // Option 2: Manual insets (more control) function Screen() { const insets = useSafeAreaInsets(); return ( ); } // Option 3: NativeWind safe area utilities (if configured) ``` --- ## Pattern: Keyboard Avoiding ```typescript import { KeyboardAvoidingView, Platform } from 'react-native'; function FormScreen() { return ( ); } ``` --- ## Pattern: Responsive Breakpoints **Note:** NativeWind v2 breakpoints differ from web Tailwind. ```typescript // NativeWind v2 breakpoints (based on window width) // sm: 640px, md: 768px, lg: 1024px, xl: 1280px // Responsive padding // Responsive flex direction // Check screen size programmatically import { useWindowDimensions } from 'react-native'; function ResponsiveLayout() { const { width } = useWindowDimensions(); const isTablet = width >= 768; return isTablet ? : ; } ``` --- ## Pattern: Animated Styles **Problem:** Avoiding re-renders with Animated values. ```typescript import { Animated } from 'react-native'; function FadeInCard() { // useRef to persist Animated.Value across renders const fadeAnim = useRef(new Animated.Value(0)).current; useEffect(() => { Animated.timing(fadeAnim, { toValue: 1, duration: 300, useNativeDriver: true, // Always use when animating opacity/transform }).start(); }, []); return ( Content ); } ``` **Style arrays:** Combine static + animated styles. ```typescript // ✅ CORRECT: Style array style={[styles.card, { opacity: fadeAnim }]} // ❌ WRONG: Spread (creates new object each render) style={{ ...styles.card, opacity: fadeAnim }} ``` --- ## Pattern: StyleSheet vs Inline ```typescript // Use StyleSheet for: // - Complex styles reused across renders // - Styles with many properties // - Performance-critical components const styles = StyleSheet.create({ card: { padding: 16, borderRadius: 12, backgroundColor: BrandColors.cardBackground, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, }, }); // Use inline/NativeWind for: // - Simple layout utilities // - One-off styles // - Conditional styles ``` --- ## BrandColors Pattern Create a centralized color constants file: ```typescript // constants/BrandColors.ts export const BrandColors = { primary: '#...', secondary: '#...', background: '#...', cardBackground: '#...', textPrimary: '#...', textSecondary: '#...', // ... etc }; export const BrandColorsDark = { // Dark mode variants }; ``` ### Recommended: Violation Scanner Consider adding a violation scanner to block: - Hardcoded hex colors (except allowed exceptions) - Direct color strings ### NativeWind Notes If using NativeWind v2 (not v4), note these differences: - `className` prop on RN components - Limited web Tailwind parity - Some utilities unsupported --- ## Common Issues | Issue | Solution | |-------|----------| | Color not applying | Check BrandColors import, verify theme context | | NativeWind class ignored | Not all Tailwind utilities work - check v2 docs | | Shadow not showing (iOS) | Use StyleSheet with shadowColor/Offset/Opacity/Radius | | Shadow not showing (Android) | Use `elevation` property | | Safe area not respected | Wrap in SafeAreaView or use insets | | Style flicker on mount | Use Animated for transitions | --- ## Recommended File Structure ``` constants/ BrandColors.ts # Color definitions designSystem.ts # Spacing, typography scales components/ ui/Card.tsx # Example hybrid styling app/ _layout.tsx # Theme provider setup ```