# POWR App Styling Guide
**Last Updated:** 2025-04-01
**Status:** Active
**Related To:** Design System, Component Architecture, UI/UX, Cross-Platform Development
## Purpose
This document outlines the styling principles, component usage patterns, and theming implementation for the POWR fitness app. Following these guidelines ensures a consistent look and feel across the application, facilitates cross-platform compatibility, and enhances the overall user experience.
## Cross-Platform Approach
The POWR app is designed to run on both iOS and Android platforms, which present unique challenges for UI implementation. Our approach prioritizes:
1. **Platform Consistency**: Maintaining a consistent look and feel across platforms
2. **Platform Adaptation**: Respecting platform-specific UX patterns where appropriate
3. **Graceful Fallbacks**: Implementing fallbacks for features not available on all platforms
4. **Testing on Both Platforms**: All UI changes must be verified on both iOS and Android
## Theme System Architecture
The POWR app uses a flexible theme system built with React Native, Tailwind CSS, and shadcn/ui components. The theming infrastructure supports both light and dark modes, with dynamic color adjustments for different UI states.
### Theme File Organization
```
lib/theme/
├── index.ts - Main theme export
├── colors.ts - Color definitions
├── constants.ts - Theme constants
├── iconUtils.ts - Icon styling utilities
└── useColorScheme.tsx - Theme mode selection hook
```
### Theme Implementation Strategy
The application uses:
- Tailwind classes for general styling with `nativewind`
- Specialized hooks for cross-platform compatibility (`useIconColor`, etc.)
- `shadcn/ui` component library for consistent UI elements
## Color System
All colors should be accessed through the theme system rather than using hardcoded values. Never use direct color codes in components.
### Color Imports
```typescript
// Import theme utilities
import { useTheme } from '@/lib/theme';
import { useIconColor } from '@/lib/theme/iconUtils';
import { FIXED_COLORS } from '@/lib/theme/colors';
```
### Color Variants
The theme includes semantic color variants for different UI elements:
- `primary` - Brand color, used for main interactive elements (purple)
- `secondary` - Supporting UI elements
- `muted` - Subdued elements, backgrounds, disabled states
- `accent` - Highlights and accents
- `destructive` - Error states, deletion actions (red)
- `success` - Confirmation, completion states (green)
- `warning` - Caution states (yellow/orange)
### Accessing Colors
Always access colors through Tailwind classes:
```jsx
// Good - uses theme system
Hello World
// Bad - hardcoded values that won't respond to theme changes
Hello World
```
## Icon Styling
Icons must use the icon utility functions to ensure visibility across platforms. Different platforms may require different stroke widths, colors, and other properties.
### Icon Usage
```typescript
import { useIconColor } from '@/lib/theme/iconUtils';
import { Play, Star, Trash2 } from 'lucide-react-native';
// Inside your functional component
function MyComponent() {
const { getIconProps, getIconColor } = useIconColor();
return (
{/* Primary action icon */}
{/* Destructive action icon */}
{/* Icon with conditional fill */}
);
}
```
### Icon Variants
- `primary` - For main actions and interactive elements
- `muted` - For secondary or less important actions
- `destructive` - For delete/remove actions
- `success` - For confirmation/complete actions
- `warning` - For caution indicators
### Platform-Specific Icon Considerations
- **Android**:
- Icons often appear thinner and less visible on Android
- Always use `strokeWidth={2}` or higher for better visibility on Android
- Minimum recommended icon size is 24px for Android (vs. 20px for iOS)
- Use the `getIconProps` function which handles these platform differences automatically
- **iOS**:
- Icons generally appear as expected with default stroke width
- iOS has better support for gradients and complex icon styles
## Button Styling
Use the standard `Button` component with appropriate variants to maintain a consistent look and feel.
### Button Variants
```jsx
import { Button } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
// Primary button
// Destructive button
// Outline button
// Ghost button (minimal visual impact)
// Link button
```
### Button States
Buttons handle the following states automatically through the theme system:
- Default
- Hover/active (handled differently on mobile and web)
- Disabled
- Loading
```jsx
// Disabled button
// Loading button
```
### Platform-Specific Button Considerations
- **Android**:
- Android buttons may need additional padding to match iOS visual weight
- Use `android:elevation` or equivalent shadow values for proper elevation on Android
- Ripple effects require additional configuration to work properly
- Consider using `TouchableNativeFeedback` for Android-specific feedback on buttons
- **iOS**:
- iOS buttons typically have more subtle feedback effects
- Shadow properties work more predictably on iOS
## Header Component
Use the `Header` component consistently across all screens for navigation and context.
### Header Configuration
```jsx
import { Header } from '@/components/Header';
// Standard header with title
// Header with logo
// Header with custom right element
}
/>
// Header with back button
navigation.goBack()}
/>
```
### Platform-Specific Header Considerations
- **Android**:
- Android status bar customization requires `StatusBar` component with platform checks
- Text in headers may render differently, requiring platform-specific adjustments
- Back button styling differs between platforms - use the Header component's built-in options
- Shadow effects need to be handled differently on Android (elevation vs shadowProps)
- **iOS**:
- iOS has native support for large titles and collapsible headers
- Safe area insets are critical for proper header positioning on iOS
- Status bar content color changes (dark/light) may need to be explicitly specified
## Text Styling
Use the `Text` component with appropriate Tailwind classes for typography. This ensures the correct font styles across platforms.
### Text Hierarchy
```jsx
import { Text } from '@/components/ui/text';
// Page title
Page Title
// Section heading
Section Heading
// Subsection heading
Subsection Heading
// Body text
Regular body text for primary content.
// Secondary text
Secondary information or supporting text.
// Small text / captions
Caption text, timestamps, etc.
```
### Platform-Specific Text Considerations
- **Android**:
- Font rendering is different on Android - text may appear smaller or thinner
- Android requires explicit `fontFamily` specification for custom fonts
- Line height calculations differ between platforms - may need adjustments
- Some text styling properties like `letterSpacing` work differently on Android
- Use `includeFontPadding: false` on Android to fix inconsistent text height
- **iOS**:
- Dynamic Type (iOS accessibility feature) should be supported
- Certain text styles like small caps require different implementations
- Font weights map differently between platforms (400 on iOS may not look the same as 400 on Android)
## Card Components
Use the Card component family for content blocks throughout the app.
### Basic Card
```jsx
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
Card Title
Card content goes here.
```
### Interactive Card
For cards that function as buttons:
```jsx
Interactive Card
Tap to interact
```
### Platform-Specific Card Considerations
- **Android**:
- Use `elevation` for shadows on Android instead of `shadow-*` classes
- Border radius may render differently on older Android versions
- Ripple effects for interactive cards need platform-specific configuration
- Border styles may appear differently on Android
- **iOS**:
- Shadow properties work more predictably on iOS
- Cards with dynamic height may need additional configuration for iOS
## Dialog/Alert Styling
Center buttons in dialogs for better usability and maintain consistent styling for these components.
### Alert Dialog Example
```jsx
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
Confirm Action
Are you sure you want to continue? This action cannot be undone.
CancelConfirm
```
### Platform-Specific Dialog Considerations
- **Android**:
- Android dialogs traditionally have buttons aligned to the right
- Back button behavior needs special handling on Android dialogs
- Touch outside to dismiss works differently on Android
- Dialog animations differ between platforms
- Material Design guidelines suggest different spacing and typography than iOS
- **iOS**:
- iOS dialogs typically have vertically stacked buttons
- Safe area insets must be respected on full-screen iOS sheets
- iOS has specific swipe gestures for sheet dismissal
## Form Elements
Style form elements consistently for a coherent user experience.
### Form Field Examples
```jsx
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
// Text input with label
{error && (
{error}
)}
// Select input with label
```
### Platform-Specific Form Element Considerations
- **Android**:
- Input fields may need additional padding or height adjustments
- Text field focus appearance differs significantly (Material Design vs. iOS)
- Android requires explicit configuration for soft keyboard behavior
- Date/time pickers have completely different UIs between platforms
- Dropdown selects appear and behave differently on Android
- **iOS**:
- Form elements typically have a lighter visual style
- iOS has specific picker components that are different from Android
- Keyboard accessories are common on iOS but less so on Android
- Text selection handles and behavior differ between platforms
## Common Cross-Platform Issues and Solutions
### Shadow Implementation
**Issue**: Shadow styling works differently between iOS and Android.
**Solution**:
```jsx
// Cross-platform shadow solution
Content with consistent shadow across platforms
```
### Icon Rendering
**Issue**: Icons appear properly on iOS but are barely visible on Android.
**Solution**:
```jsx
// Always use the icon utility
import { useIconColor } from '@/lib/theme/iconUtils';
function MyComponent() {
const { getIconProps } = useIconColor();
return (
);
}
```
### Text Alignment
**Issue**: Text alignment and truncation behaves differently across platforms.
**Solution**:
```jsx
// Text alignment helper component
function AlignedText({ children, ...props }) {
return (
{children}
);
}
```
### Touchable Feedback
**Issue**: Touch feedback effects differ between platforms.
**Solution**:
```jsx
// Platform-specific touchable
function AppTouchable({ children, onPress, ...props }) {
if (Platform.OS === 'android') {
return (
{children}
);
}
return (
{children}
);
}
```
### Keyboard Handling
**Issue**: Keyboard behavior and avoidance differs between platforms.
**Solution**:
```jsx
// Keyboard handling helper
{/* Form content */}
```
## Platform-Specific Component Extensions
For cases where significant platform differences exist, create platform-specific component extensions:
### Example: Platform-Specific DatePicker
```jsx
// DatePickerWrapper.jsx
import { DatePicker } from './DatePicker.ios';
import { DatePicker } from './DatePicker.android';
export const DatePickerWrapper = (props) => {
const Component = Platform.select({
ios: DatePickerIOS,
android: DatePickerAndroid,
});
return ;
};
```
## Best Practices for Cross-Platform Development
1. **Always test on both platforms** before considering a feature complete
2. **Use platform detection judiciously** - prefer cross-platform solutions where possible
3. **Create abstraction layers** for significantly different platform components
4. **Leverage UI component libraries** that handle cross-platform differences (like UI Kitten, React Native Paper)
5. **Document platform-specific quirks** that you encounter for future reference
6. **Create utility functions** for common platform-specific adjustments
7. **Use feature detection** instead of platform detection when possible
8. **Consider native device capabilities** like haptic feedback that may not exist on all devices
## Best Practices for POWR App Styling
1. **Never use hardcoded colors** - Always use theme variables through Tailwind classes
2. **Always use `getIconProps` for icons** - Ensures visibility on both iOS and Android
3. **Use semantic variants** - Choose button and icon variants based on their purpose
4. **Maintain consistent spacing** - Use Tailwind spacing classes (p-4, m-2, etc.)
5. **Test both platforms** - Verify UI rendering on both iOS and Android
6. **Use platform-specific overrides** when necessary
7. **Document platform-specific behavior** in component comments
## Troubleshooting Common Issues
### Icons Not Visible on Android
Problem: Icons don't appear or are difficult to see on Android devices.
Solution:
- Ensure you're using `getIconProps()` instead of direct styling
- Add `strokeWidth={2}` to increase visibility
- Verify that icon size is appropriate (min 24px recommended for Android)
- Check that the icon color has sufficient contrast with the background
### Inconsistent Colors
Problem: Colors appear inconsistent between components or platforms.
Solution:
- Verify you're using Tailwind classes (text-primary vs #8B5CF6)
- Check that the correct variant is being used for the component
- Ensure components are properly wrapped with theme provider
- Examine component hierarchy for style inheritance issues
### Text Truncation Issues
Problem: Text doesn't truncate properly or layout breaks with long content.
Solution:
- Add `numberOfLines={1}` for single-line truncation
- Use `ellipsizeMode="tail"` for text truncation
- Wrap Text components with a fixed-width container
- Consider using a more robust solution for responsive text
- Apply platform-specific text style adjustments
### Shadow and Elevation
Problem: Shadows appear on iOS but not on Android, or look inconsistent.
Solution:
- Use platform-specific shadow implementation (see example above)
- For Android, use `elevation` property
- For iOS, use `shadowColor`, `shadowOffset`, `shadowOpacity`, and `shadowRadius`
- Test shadow values on different Android versions
### Keyboard Issues
Problem: Keyboard covers input fields or doesn't dismiss properly.
Solution:
- Use KeyboardAvoidingView with platform-specific behavior
- Implement Keyboard.dismiss on background taps
- Add ScrollView for forms to ensure all fields are accessible
- Consider using a keyboard manager library for complex forms
## Related Documentation
- [Coding Style Guide](../../guides/coding_style.md) - General coding patterns and practices
- [Component Architecture](../../architecture/index.md) - How components are organized
- [Accessibility Guidelines](../../guides/accessibility.md) - Making the app accessible to all users