react-native-sop-docs/04-design-integration/design-system.md

258 lines
5.0 KiB
Markdown

# Design System Setup
## Theme Configuration
**Install design dependencies:**
```bash
yarn add react-native-vector-icons react-native-svg
yarn add -D @types/react-native-vector-icons
```
**Theme Configuration:**
```typescript
// src/config/theme.ts
export const Colors = {
primary: '#007AFF',
secondary: '#5856D6',
success: '#34C759',
warning: '#FF9500',
error: '#FF3B30',
background: '#F2F2F7',
surface: '#FFFFFF',
text: {
primary: '#000000',
secondary: '#6D6D80',
disabled: '#C7C7CC',
},
border: '#E5E5E7',
};
export const Typography = {
h1: {
fontSize: 32,
fontWeight: '700' as const,
lineHeight: 38,
},
h2: {
fontSize: 24,
fontWeight: '600' as const,
lineHeight: 30,
},
body: {
fontSize: 16,
fontWeight: '400' as const,
lineHeight: 22,
},
caption: {
fontSize: 12,
fontWeight: '400' as const,
lineHeight: 16,
},
};
export const Spacing = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
};
```
## Design Tokens
Create a comprehensive design token system:
```typescript
// src/config/designTokens.ts
export const DesignTokens = {
colors: {
// Primary palette
primary: {
50: '#E3F2FD',
100: '#BBDEFB',
500: '#2196F3',
700: '#1976D2',
900: '#0D47A1',
},
// Semantic colors
semantic: {
success: '#4CAF50',
warning: '#FF9800',
error: '#F44336',
info: '#2196F3',
},
// Neutral colors
neutral: {
white: '#FFFFFF',
gray50: '#FAFAFA',
gray100: '#F5F5F5',
gray200: '#EEEEEE',
gray300: '#E0E0E0',
gray400: '#BDBDBD',
gray500: '#9E9E9E',
gray600: '#757575',
gray700: '#616161',
gray800: '#424242',
gray900: '#212121',
black: '#000000',
},
},
typography: {
fontFamily: {
regular: 'Montserrat-Regular',
medium: 'Montserrat-Medium',
semiBold: 'Montserrat-SemiBold',
bold: 'Montserrat-Bold',
},
fontSize: {
xs: 12,
sm: 14,
base: 16,
lg: 18,
xl: 20,
'2xl': 24,
'3xl': 30,
'4xl': 36,
},
lineHeight: {
tight: 1.25,
normal: 1.5,
relaxed: 1.75,
},
},
spacing: {
0: 0,
1: 4,
2: 8,
3: 12,
4: 16,
5: 20,
6: 24,
8: 32,
10: 40,
12: 48,
16: 64,
20: 80,
},
borderRadius: {
none: 0,
sm: 4,
base: 8,
md: 12,
lg: 16,
xl: 24,
full: 9999,
},
shadows: {
sm: {
shadowColor: '#000',
shadowOffset: {width: 0, height: 1},
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 1,
},
base: {
shadowColor: '#000',
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
lg: {
shadowColor: '#000',
shadowOffset: {width: 0, height: 4},
shadowOpacity: 0.15,
shadowRadius: 8,
elevation: 5,
},
},
};
```
## Theme Provider
```typescript
// src/providers/ThemeProvider.tsx
import React, {createContext, useContext, ReactNode} from 'react';
import {DesignTokens} from '@config/designTokens';
interface ThemeContextType {
colors: typeof DesignTokens.colors;
typography: typeof DesignTokens.typography;
spacing: typeof DesignTokens.spacing;
borderRadius: typeof DesignTokens.borderRadius;
shadows: typeof DesignTokens.shadows;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
interface ThemeProviderProps {
children: ReactNode;
}
export const ThemeProvider: React.FC<ThemeProviderProps> = ({children}) => {
const theme = {
colors: DesignTokens.colors,
typography: DesignTokens.typography,
spacing: DesignTokens.spacing,
borderRadius: DesignTokens.borderRadius,
shadows: DesignTokens.shadows,
};
return (
<ThemeContext.Provider value={theme}>
{children}
</ThemeContext.Provider>
);
};
export const useTheme = (): ThemeContextType => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
```
## Responsive Design
```typescript
// src/utils/responsive.ts
import {Dimensions, PixelRatio} from 'react-native';
const {width: SCREEN_WIDTH, height: SCREEN_HEIGHT} = Dimensions.get('window');
// Based on iPhone 6/7/8 dimensions
const BASE_WIDTH = 375;
const BASE_HEIGHT = 667;
export const wp = (percentage: number): number => {
const value = (percentage * SCREEN_WIDTH) / 100;
return Math.round(PixelRatio.roundToNearestPixel(value));
};
export const hp = (percentage: number): number => {
const value = (percentage * SCREEN_HEIGHT) / 100;
return Math.round(PixelRatio.roundToNearestPixel(value));
};
export const normalize = (size: number): number => {
const scale = SCREEN_WIDTH / BASE_WIDTH;
const newSize = size * scale;
return Math.round(PixelRatio.roundToNearestPixel(newSize));
};
export const isTablet = (): boolean => {
return SCREEN_WIDTH >= 768;
};
export const getDeviceType = (): 'phone' | 'tablet' => {
return isTablet() ? 'tablet' : 'phone';
};
```