WellNuo/constants/theme.ts
Sergei 610104090a Add dark mode support with theme toggle
- Create ThemeContext with light/dark/system mode support
- Add DarkColors palette for dark mode UI
- Extend Colors object with full dark theme variants
- Update useThemeColor hook to use ThemeContext
- Add useThemeColors, useResolvedTheme, useIsDarkMode hooks
- Update RootLayout (native and web) with ThemeProvider
- Add theme toggle UI in ProfileDrawer settings
- Theme preference persisted to AsyncStorage
- Add comprehensive tests for ThemeContext and hooks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-01 10:01:07 -08:00

458 lines
10 KiB
TypeScript

/**
* WellNuo Design System 2025
* Modern, minimalist design with wellness-focused color palette
* Based on 2024-2025 mobile design trends:
* - Exaggerated minimalism
* - Soft rounded edges
* - Blue professional calming colors (#0076BF)
* - Glass morphism effects
* - Bold typography with generous whitespace
*/
import { Platform, ViewStyle } from 'react-native';
// ============================================
// COLOR PALETTE
// ============================================
export const AppColors = {
// Primary - Blue (trustworthy, professional, wellness)
primary: '#0076BF',
primaryDark: '#005A94',
primaryLight: '#3391CC',
primaryLighter: '#CCE6F4',
primarySubtle: '#E6F3FA',
// Accent - Violet (AI, premium features)
accent: '#8B5CF6',
accentLight: '#EDE9FE',
accentDark: '#7C3AED',
// Status Colors
success: '#10B981',
successLight: '#D1FAE5',
warning: '#F59E0B',
warningLight: '#FEF3C7',
error: '#EF4444',
errorLight: '#FEE2E2',
info: '#3B82F6',
infoLight: '#DBEAFE',
// Neutral Backgrounds
white: '#FFFFFF',
background: '#FAFBFC',
backgroundSecondary: '#F1F5F9',
surface: '#FFFFFF',
surfaceSecondary: '#F8FAFC',
surfaceElevated: '#FFFFFF',
// Borders
border: '#E2E8F0',
borderLight: '#F1F5F9',
borderFocus: '#0076BF',
// Text
textPrimary: '#0F172A',
textSecondary: '#475569',
textMuted: '#94A3B8',
textLight: '#FFFFFF',
textDisabled: '#CBD5E1',
// Beneficiary Status
online: '#10B981',
offline: '#94A3B8',
away: '#F59E0B',
// Gradients (for special elements)
gradientStart: '#0076BF',
gradientEnd: '#3391CC',
// Overlay
overlay: 'rgba(15, 23, 42, 0.5)',
overlayLight: 'rgba(15, 23, 42, 0.3)',
};
// Dark mode color palette
export const DarkColors = {
// Backgrounds
background: '#0F172A',
backgroundSecondary: '#1E293B',
surface: '#1E293B',
surfaceSecondary: '#334155',
surfaceElevated: '#334155',
// Text
textPrimary: '#F1F5F9',
textSecondary: '#94A3B8',
textMuted: '#64748B',
textLight: '#0F172A',
textDisabled: '#475569',
// Borders
border: '#334155',
borderLight: '#475569',
borderFocus: '#3391CC',
// Primary (slightly lighter for dark mode)
primary: '#3391CC',
primaryDark: '#0076BF',
primaryLight: '#60A5FA',
primaryLighter: '#1E3A5F',
primarySubtle: '#0F2744',
// Status Colors (brighter for dark mode visibility)
success: '#34D399',
successLight: '#064E3B',
warning: '#FBBF24',
warningLight: '#78350F',
error: '#F87171',
errorLight: '#7F1D1D',
info: '#60A5FA',
infoLight: '#1E3A8A',
// Accent
accent: '#A78BFA',
accentLight: '#2E1065',
accentDark: '#8B5CF6',
// Status Indicators
online: '#34D399',
offline: '#64748B',
away: '#FBBF24',
// Special
white: '#F8FAFC',
overlay: 'rgba(0, 0, 0, 0.6)',
overlayLight: 'rgba(0, 0, 0, 0.4)',
};
// Theme variants
const tintColorLight = AppColors.primary;
const tintColorDark = DarkColors.primary;
export const Colors = {
light: {
text: AppColors.textPrimary,
textSecondary: AppColors.textSecondary,
textMuted: AppColors.textMuted,
background: AppColors.background,
backgroundSecondary: AppColors.backgroundSecondary,
tint: tintColorLight,
icon: AppColors.textSecondary,
tabIconDefault: AppColors.textMuted,
tabIconSelected: tintColorLight,
surface: AppColors.surface,
surfaceSecondary: AppColors.surfaceSecondary,
border: AppColors.border,
borderLight: AppColors.borderLight,
primary: AppColors.primary,
primaryLight: AppColors.primaryLight,
error: AppColors.error,
errorLight: AppColors.errorLight,
success: AppColors.success,
successLight: AppColors.successLight,
warning: AppColors.warning,
warningLight: AppColors.warningLight,
accent: AppColors.accent,
accentLight: AppColors.accentLight,
overlay: AppColors.overlay,
},
dark: {
text: DarkColors.textPrimary,
textSecondary: DarkColors.textSecondary,
textMuted: DarkColors.textMuted,
background: DarkColors.background,
backgroundSecondary: DarkColors.backgroundSecondary,
tint: tintColorDark,
icon: DarkColors.textSecondary,
tabIconDefault: DarkColors.textMuted,
tabIconSelected: tintColorDark,
surface: DarkColors.surface,
surfaceSecondary: DarkColors.surfaceSecondary,
border: DarkColors.border,
borderLight: DarkColors.borderLight,
primary: DarkColors.primary,
primaryLight: DarkColors.primaryLight,
error: DarkColors.error,
errorLight: DarkColors.errorLight,
success: DarkColors.success,
successLight: DarkColors.successLight,
warning: DarkColors.warning,
warningLight: DarkColors.warningLight,
accent: DarkColors.accent,
accentLight: DarkColors.accentLight,
overlay: DarkColors.overlay,
},
};
// ============================================
// SPACING SYSTEM
// ============================================
export const Spacing = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
xxl: 48,
xxxl: 64,
};
// ============================================
// BORDER RADIUS (Modern, rounded aesthetic)
// ============================================
export const BorderRadius = {
xs: 6,
sm: 8,
md: 12,
lg: 16,
xl: 20,
'2xl': 24,
'3xl': 32,
full: 9999,
};
// ============================================
// TYPOGRAPHY
// ============================================
export const FontSizes = {
xs: 12,
sm: 14,
base: 16,
lg: 18,
xl: 20,
'2xl': 24,
'3xl': 30,
'4xl': 36,
'5xl': 48,
};
export const FontWeights = {
normal: '400' as const,
medium: '500' as const,
semibold: '600' as const,
bold: '700' as const,
extrabold: '800' as const,
};
export const LineHeights = {
tight: 1.2,
normal: 1.5,
relaxed: 1.75,
};
export const LetterSpacing = {
tight: -0.5,
normal: 0,
wide: 0.5,
wider: 1,
};
export const Fonts = Platform.select({
ios: {
sans: 'SF Pro Display',
text: 'SF Pro Text',
rounded: 'SF Pro Rounded',
mono: 'SF Mono',
},
default: {
sans: 'System',
text: 'System',
rounded: 'System',
mono: 'monospace',
},
web: {
sans: "Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
text: "Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
rounded: "'SF Pro Rounded', Inter, sans-serif",
mono: "'SF Mono', 'Fira Code', Menlo, monospace",
},
});
// ============================================
// SHADOWS (Soft, modern shadows)
// ============================================
export const Shadows = {
none: {
shadowColor: 'transparent',
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0,
shadowRadius: 0,
elevation: 0,
} as ViewStyle,
xs: {
shadowColor: '#0F172A',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.03,
shadowRadius: 2,
elevation: 1,
} as ViewStyle,
sm: {
shadowColor: '#0F172A',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.04,
shadowRadius: 4,
elevation: 2,
} as ViewStyle,
md: {
shadowColor: '#0F172A',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.06,
shadowRadius: 8,
elevation: 4,
} as ViewStyle,
lg: {
shadowColor: '#0F172A',
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 0.08,
shadowRadius: 16,
elevation: 8,
} as ViewStyle,
xl: {
shadowColor: '#0F172A',
shadowOffset: { width: 0, height: 12 },
shadowOpacity: 0.1,
shadowRadius: 24,
elevation: 12,
} as ViewStyle,
// Colored shadows for interactive elements
primary: {
shadowColor: AppColors.primary,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 4,
} as ViewStyle,
success: {
shadowColor: AppColors.success,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 4,
} as ViewStyle,
};
// ============================================
// ANIMATION DURATIONS
// ============================================
export const Animation = {
fast: 150,
normal: 250,
slow: 400,
spring: {
damping: 15,
stiffness: 150,
},
};
// ============================================
// COMMON COMPONENT STYLES
// ============================================
export const CommonStyles = {
// Card styles
card: {
backgroundColor: AppColors.surface,
borderRadius: BorderRadius.xl,
padding: Spacing.lg,
...Shadows.sm,
} as ViewStyle,
cardElevated: {
backgroundColor: AppColors.surface,
borderRadius: BorderRadius.xl,
padding: Spacing.lg,
...Shadows.md,
} as ViewStyle,
// Glass effect (iOS blur)
glass: {
backgroundColor: 'rgba(255, 255, 255, 0.8)',
borderRadius: BorderRadius.xl,
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.3)',
} as ViewStyle,
// Input styles
input: {
backgroundColor: AppColors.surfaceSecondary,
borderRadius: BorderRadius.lg,
paddingHorizontal: Spacing.md,
paddingVertical: Spacing.md,
fontSize: FontSizes.base,
color: AppColors.textPrimary,
borderWidth: 1.5,
borderColor: AppColors.borderLight,
} as ViewStyle,
inputFocused: {
borderColor: AppColors.primary,
backgroundColor: AppColors.white,
} as ViewStyle,
// Button base
buttonBase: {
borderRadius: BorderRadius.lg,
paddingVertical: Spacing.md,
paddingHorizontal: Spacing.lg,
alignItems: 'center' as const,
justifyContent: 'center' as const,
flexDirection: 'row' as const,
} as ViewStyle,
// Screen container
screenContainer: {
flex: 1,
backgroundColor: AppColors.background,
} as ViewStyle,
// Section
section: {
backgroundColor: AppColors.surface,
borderRadius: BorderRadius.xl,
padding: Spacing.lg,
marginHorizontal: Spacing.md,
marginBottom: Spacing.md,
...Shadows.xs,
} as ViewStyle,
};
// ============================================
// ICON SIZES
// ============================================
export const IconSizes = {
xs: 16,
sm: 20,
md: 24,
lg: 28,
xl: 32,
'2xl': 40,
'3xl': 48,
};
// ============================================
// AVATAR SIZES
// ============================================
export const AvatarSizes = {
xs: 32,
sm: 40,
md: 56,
lg: 72,
xl: 96,
'2xl': 120,
};