Skip to main content

StyleSheet Basics

GymApp uses React Native’s StyleSheet API for creating optimized styles:
import { StyleSheet, View, Text } from 'react-native';

function MyComponent() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Hello World</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#fff',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
  },
});

Why StyleSheet?

  • Performance: Styles are optimized and sent to native before render
  • Validation: Catches errors in style definitions
  • Type Safety: Works well with TypeScript
Always use StyleSheet.create() instead of inline objects for better performance.

Theme System

GymApp implements a custom theme system for consistent styling across light and dark modes.

Colors

Colors are defined in constants/theme.ts:
export const Colors = {
  light: {
    text: '#11181C',
    background: '#fff',
    tint: '#0a7ea4',
    icon: '#687076',
    tabIconDefault: '#687076',
    tabIconSelected: '#0a7ea4',
  },
  dark: {
    text: '#ECEDEE',
    background: '#151718',
    tint: '#fff',
    icon: '#9BA1A6',
    tabIconDefault: '#9BA1A6',
    tabIconSelected: '#fff',
  },
};

Fonts

Platform-specific fonts are also defined in constants/theme.ts:
export const Fonts = Platform.select({
  ios: {
    sans: 'system-ui',
    serif: 'ui-serif',
    rounded: 'ui-rounded',
    mono: 'ui-monospace',
  },
  android: {
    sans: 'normal',
    serif: 'serif',
    rounded: 'normal',
    mono: 'monospace',
  },
  web: {
    sans: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto",
    serif: "Georgia, 'Times New Roman', serif",
    mono: "SFMono-Regular, Menlo, Monaco, Consolas",
  },
});

Themed Components

GymApp provides pre-built themed components that automatically adapt to light/dark mode.

ThemedView

A container that adapts its background color:
import { ThemedView } from '@/components/themed-view';

<ThemedView style={styles.container}>
  {/* Content */}
</ThemedView>
Custom colors per theme:
<ThemedView 
  lightColor="#f5f5f5" 
  darkColor="#1a1a1a"
  style={styles.container}
>
  {/* Content */}
</ThemedView>

ThemedText

Text component with theme-aware colors and type variants:
import { ThemedText } from '@/components/themed-text';

// Default text
<ThemedText>Regular text</ThemedText>

// Title variant
<ThemedText type="title">Page Title</ThemedText>

// Other variants
<ThemedText type="subtitle">Section Heading</ThemedText>
<ThemedText type="defaultSemiBold">Bold Text</ThemedText>
<ThemedText type="link">Clickable Link</ThemedText>
Available Text Variants:
TypeFont SizeWeightUse Case
default16pxnormalBody text
title32pxboldPage titles
subtitle20pxboldSection headers
defaultSemiBold16px600Emphasized text
link16pxnormalInteractive links

useThemeColor Hook

For custom components that need theme awareness:
import { useThemeColor } from '@/hooks/use-theme-color';

function CustomComponent() {
  const textColor = useThemeColor({}, 'text');
  const tintColor = useThemeColor({}, 'tint');
  
  // Or with custom overrides
  const backgroundColor = useThemeColor(
    { light: '#f0f0f0', dark: '#2a2a2a' },
    'background'
  );
  
  return (
    <View style={{ backgroundColor }}>
      <Text style={{ color: textColor }}>Themed content</Text>
    </View>
  );
}

Platform-Specific Styling

GymApp demonstrates platform-specific implementations for optimal native feel.

File-Based Platform Styling

Use platform-specific file extensions:
icon-symbol.tsx       # Android & Web implementation
icon-symbol.ios.tsx   # iOS-specific implementation
Example from components/ui/icon-symbol.tsx:
// icon-symbol.ios.tsx - Uses native SF Symbols
import { SymbolView } from 'expo-symbols';

export function IconSymbol({ name, size, color }) {
  return (
    <SymbolView
      name={name}
      tintColor={color}
      style={{ width: size, height: size }}
    />
  );
}

// icon-symbol.tsx - Uses Material Icons
import MaterialIcons from '@expo/vector-icons/MaterialIcons';

export function IconSymbol({ name, size, color }) {
  return <MaterialIcons name={MAPPING[name]} size={size} color={color} />;
}

Platform.select()

For inline platform-specific values:
import { Platform } from 'react-native';

const styles = StyleSheet.create({
  container: {
    padding: Platform.select({
      ios: 20,
      android: 16,
      web: 24,
    }),
  },
  text: {
    fontFamily: Platform.select({
      ios: 'system-ui',
      android: 'Roboto',
      default: 'system-ui',
    }),
  },
});

Platform-Specific Styles

Conditional styling based on platform:
const styles = StyleSheet.create({
  shadow: {
    ...Platform.select({
      ios: {
        shadowColor: '#000',
        shadowOffset: { width: 0, height: 2 },
        shadowOpacity: 0.25,
        shadowRadius: 4,
      },
      android: {
        elevation: 5,
      },
    }),
  },
});
iOS uses shadow properties while Android uses elevation. Never mix them or you’ll get unexpected results.

Responsive Styling

Dimensions API

Get screen dimensions for responsive layouts:
import { Dimensions, useWindowDimensions } from 'react-native';

// Hook (recommended - updates on rotation)
function MyComponent() {
  const { width, height } = useWindowDimensions();
  
  return (
    <View style={{ width: width * 0.9 }}>
      {/* 90% of screen width */}
    </View>
  );
}

// Static (doesn't update on rotation)
const { width, height } = Dimensions.get('window');

Flexbox Layouts

React Native uses Flexbox by default:
const styles = StyleSheet.create({
  row: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  column: {
    flexDirection: 'column',
    flex: 1,
  },
});

Safe Area Handling

GymApp includes react-native-safe-area-context for handling notches and rounded corners:
import { SafeAreaView } from 'react-native-safe-area-context';

function Screen() {
  return (
    <SafeAreaView style={styles.container}>
      {/* Content automatically avoids notches */}
    </SafeAreaView>
  );
}

Animation Styling

GymApp includes react-native-reanimated for performant animations:
import Animated, {
  useAnimatedStyle,
  useSharedValue,
  withSpring,
} from 'react-native-reanimated';

function AnimatedComponent() {
  const offset = useSharedValue(0);
  
  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ translateX: offset.value }],
  }));
  
  return (
    <Animated.View style={[styles.box, animatedStyle]} />
  );
}
Reanimated runs animations on the UI thread for 60fps performance, even when JavaScript thread is busy.

Alternative Styling Solutions

While GymApp uses StyleSheet and themed components, consider these alternatives for larger projects:
  • NativeWind - Tailwind CSS for React Native
  • Tamagui - Universal UI kit with optimizing compiler
  • Unistyles - Runtime stylesheets with TypeScript

Best Practices

  1. Use StyleSheet.create() for all styles
  2. Leverage themed components instead of hardcoding colors
  3. Test both light and dark modes before deployment
  4. Use platform-specific files for significantly different implementations
  5. Prefer Flexbox over absolute positioning
  6. Use SafeAreaView on screens to avoid notches
Avoid inline styles like style={{ color: 'red' }} in production code. They prevent optimizations and hurt performance.

Next Steps

Build docs developers (and LLMs) love