Темизация и цвета
Светлая и тёмная темы, единая система цветов.
Система цветов
// constants/colors.ts
export const colors = {
primary: '#c1442e',
primaryDark: '#a83926',
text: '#1a1a1a',
textMuted: '#666',
background: '#fafafa',
surface: '#ffffff',
border: '#e5e5e5',
success: '#10b981',
error: '#ef4444',
warning: '#f59e0b',
};
Использование:
const styles = StyleSheet.create({
button: { backgroundColor: colors.primary },
text: { color: colors.text },
});
useColorScheme
Системная тема:
import { useColorScheme } from 'react-native';
function Component() {
const scheme = useColorScheme(); // 'light' | 'dark' | null
return (
<View style={{ backgroundColor: scheme === 'dark' ? '#000' : '#fff' }}>
{/* контент */}
</View>
);
}
Контекст темы
// contexts/ThemeContext.tsx
import { createContext, useContext, ReactNode } from 'react';
import { useColorScheme } from 'react-native';
type Theme = {
colors: typeof lightColors;
isDark: boolean;
};
const lightColors = { background: '#fff', text: '#000', primary: '#c1442e' };
const darkColors = { background: '#0a0a0a', text: '#fff', primary: '#ff6b4a' };
const ThemeContext = createContext<Theme | null>(null);
export function ThemeProvider({ children }: { children: ReactNode }) {
const scheme = useColorScheme();
const isDark = scheme === 'dark';
const theme: Theme = {
colors: isDark ? darkColors : lightColors,
isDark,
};
return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>;
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) throw new Error('useTheme must be used within ThemeProvider');
return context;
}
Использование:
function Component() {
const { colors, isDark } = useTheme();
return (
<View style={{ backgroundColor: colors.background }}>
<Text style={{ color: colors.text }}>Текст</Text>
</View>
);
}
Стили с темой
Создание стилей внутри компонента:
function Component() {
const { colors } = useTheme();
const styles = useMemo(() => StyleSheet.create({
container: { backgroundColor: colors.background },
text: { color: colors.text },
}), [colors]);
return <View style={styles.container} />;
}
useMemo чтобы не создавать стили на каждый рендер.
Переключатель темы
function ThemeToggle() {
const [mode, setMode] = useState<'system' | 'light' | 'dark'>('system');
return (
<View>
<Pressable onPress={() => setMode('light')}>
<Text>Светлая</Text>
</Pressable>
<Pressable onPress={() => setMode('dark')}>
<Text>Тёмная</Text>
</Pressable>
<Pressable onPress={() => setMode('system')}>
<Text>Системная</Text>
</Pressable>
</View>
);
}
Сохранение выбора в AsyncStorage.
Expo: userInterfaceStyle
В app.json:
{
"expo": {
"userInterfaceStyle": "automatic"
}
}
Варианты: light, dark, automatic.
automatic — следует системной теме.
Готовые библиотеки
- React Navigation — встроенная поддержка тем
- tamagui — дизайн-система
- restyle — типизированные темы
- nativewind — Tailwind с dark mode
Если ты уже знаешь React
В вебе — CSS-переменные и prefers-color-scheme. В React Native — контекст и useColorScheme. Логика похожа.
Итоги
useColorSchemeдля системной темы- Контекст для общих цветов
useMemoдля стилейapp.jsonдля userInterfaceStyle- AsyncStorage для сохранения выбора