258 lines
5.0 KiB
Markdown
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';
|
|
};
|
|
``` |