π¨ Styling with Unistyles 3.0
Shipnative uses React Native Unistyles 3.0 for a powerful and flexible styling solution. Unistyles provides a unified way to manage themes, responsive design, and component variants, ensuring a consistent and maintainable UI across your application.
CRITICAL: Always use theme values from Unistyles. Avoid hardcoding colors, spacing, or typography values directly in your components.
Key Concepts
Theme Values
Unistyles centralizes your design tokens (colors, spacing, typography, etc.) in a single theme file (apps/app/app/theme/unistyles.ts). This allows for easy customization and ensures consistency.
Accessing Theme Values:
import { useUnistyles } from 'react-native-unistyles'
const MyComponent = () => {
const { theme } = useUnistyles()
return (
<View style={{ backgroundColor: theme.colors.background, padding: theme.spacing.md }}>
<Text style={{ color: theme.colors.foreground, fontSize: theme.typography.sizes['2xl'] }}>
Hello Shipnative!
</Text>
</View>
)
}
Common Theme Values:
- Colors (semantic):
theme.colors.primary (Primary action color)
theme.colors.background (Main screen background)
theme.colors.foreground (Main text color)
theme.colors.card (Card backgrounds)
theme.colors.error (Error states)
- Spacing (8px grid):
theme.spacing.sm (8px)
theme.spacing.md (16px)
theme.spacing.lg (24px)
- Typography:
theme.typography.sizes.base (Base font size, e.g., 16px)
theme.typography.fonts.bold (Bold font family)
- Radius:
theme.radius.lg (Large border radius, e.g., 16px)
- Shadows:
theme.shadows.md (Medium elevation shadow)
You can find the complete theme definition in apps/app/app/theme/unistyles.ts.
StyleSheet.create with Theme Function
Always define your styles using StyleSheet.create with a theme function. This ensures your styles automatically adapt to the current theme (e.g., dark mode).
import { StyleSheet, useUnistyles } from 'react-native-unistyles'
const MyScreen = () => {
const { theme } = useUnistyles() // Access the current theme
return (
<View style={styles.container}>
<Text style={styles.title}>My App Title</Text>
</View>
)
}
const styles = StyleSheet.create((theme) => ({
container: {
flex: 1,
backgroundColor: theme.colors.background, // Uses theme background color
padding: theme.spacing.lg,
},
title: {
fontSize: theme.typography.sizes['2xl'],
fontFamily: theme.typography.fonts.bold,
color: theme.colors.foreground, // Uses theme foreground color
},
}))
Component Variants
Unistyles allows you to define component variants, making it easy to create reusable components with different visual states (e.g., different button sizes or styles).
import { StyleSheet, useUnistyles } from 'react-native-unistyles'
import { Button as RNButton } from 'react-native'
type ButtonProps = {
variant?: 'filled' | 'outlined' | 'ghost';
size?: 'sm' | 'md' | 'lg';
title: string;
onPress: () => void;
}
const Button = ({ variant = 'filled', size = 'md', title, onPress }: ButtonProps) => {
const { theme } = useUnistyles()
const variantStyles = styles.button({ variant, size }) // Apply variants
return (
<RNButton
title={title}
onPress={onPress}
style={[variantStyles, styles.baseButton]} // Combine base and variant styles
/>
)
}
const styles = StyleSheet.create((theme) => ({
baseButton: {
borderRadius: theme.radius.md,
justifyContent: 'center',
alignItems: 'center',
},
button: {
variants: {
variant: {
filled: { backgroundColor: theme.colors.primary, color: theme.colors.primaryForeground },
outlined: { borderWidth: 1, borderColor: theme.colors.border, color: theme.colors.foreground },
ghost: { backgroundColor: 'transparent', color: theme.colors.primary },
},
size: {
sm: { height: 36, paddingHorizontal: theme.spacing.md },
md: { height: 44, paddingHorizontal: theme.spacing.lg },
lg: { height: 56, paddingHorizontal: theme.spacing.xl },
},
},
},
}))
Dark Mode Support
All components in Shipnative are designed to support dark mode automatically through the use of semantic theme colors. When the userβs device theme changes, Unistyles will automatically apply the corresponding dark mode colors defined in apps/app/app/theme/unistyles.ts.
// Example from unistyles.ts (simplified)
export const lightTheme = {
colors: {
background: '#FFFFFF',
foreground: '#000000',
primary: '#007AFF',
},
// ... other tokens
}
export const darkTheme = {
colors: {
background: '#000000',
foreground: '#FFFFFF',
primary: '#0A84FF',
},
// ... other tokens
}
By using theme.colors.background and theme.colors.foreground, your components will automatically switch between white/black backgrounds and black/white text respectively, based on the active theme.
Common Mistakes to Avoid
- β Hardcoding values:
color: "#000000" or marginTop: 24
- β
Solution: Always use theme values:
color: theme.colors.foreground, marginTop: theme.spacing.lg
- β Using inline styles for complex styling:
style={{ flex: 1, padding: 16 }}
- β
Solution: Use
StyleSheet.create for better performance and maintainability: style={styles.container}
- β Not using the theme function in
StyleSheet.create: StyleSheet.create({ container: { ... } })
- β
Solution: Pass the theme function to
StyleSheet.create to enable dynamic theme access: StyleSheet.create((theme) => ({ container: { ... } }))
π¨ Component Showcase
Shipnative includes a Component Showcase - a visual gallery of all pre-built components with live examples. This is perfect for:
- π Seeing what components are available
- π Copy-pasting component code
- π¨ Viewing components in light/dark mode
- π± Testing responsive behavior
How to Access
Open the Dev Menu in your running app:
- iOS Simulator: Press
Cmd + D
- Android Emulator: Press
Cmd + M (Mac) or Ctrl + M (Windows/Linux)
- Physical Device: Shake the device
Then select βComponent Showcaseβ from the menu.
Whatβs Included
The showcase displays all Shipnative components with examples:
UI Components:
- Button (filled, outlined, ghost, danger variants)
- TextField (with labels, errors, helpers)
- Card (default, elevated, outlined presets)
- Text (heading, subheading, body, caption presets)
- Avatar (with images and initials)
- Badge (status indicators)
- Divider
- IconButton
- Spinner
Each component shows:
- β
All variants and sizes
- β
Light and dark mode versions
- β
Code snippets ready to copy
- β
Props and usage examples
Tip: Use the Component Showcase as a reference when building your UI. You can see exactly how each component looks and behaves before using it in your screens!
Building Your Own Components
When creating custom components, follow the same patterns used in the showcase:
- Use Unistyles theme values
- Support variants with
StyleSheet.create
- Include TypeScript props interface
- Support dark mode automatically
- Add to the showcase for team reference
Example based on showcase patterns:
import { Pressable } from 'react-native'
import { StyleSheet, useUnistyles } from 'react-native-unistyles'
import { Text } from '@/components/Text'
interface MyComponentProps {
title: string
variant?: 'primary' | 'secondary'
onPress?: () => void
}
export function MyComponent({
title,
variant = 'primary',
onPress
}: MyComponentProps) {
const { theme } = useUnistyles()
return (
<Pressable
style={[
styles.container,
variant === 'primary' && styles.primary,
variant === 'secondary' && styles.secondary,
]}
onPress={onPress}
>
<Text preset="subheading">{title}</Text>
</Pressable>
)
}
const styles = StyleSheet.create((theme) => ({
container: {
padding: theme.spacing.md,
borderRadius: theme.radius.lg,
alignItems: 'center',
},
primary: {
backgroundColor: theme.colors.primary,
},
secondary: {
backgroundColor: theme.colors.secondary,
},
}))
Resources
- Unistyles Documentation: https://unistyl.es/
- Shipnative Theme Definition:
apps/app/app/theme/unistyles.ts
- Shipnative Core Components:
apps/app/app/components/ (for examples of Unistyles usage)
- Component Showcase: Access via Dev Menu in your running app