feat: integrate theme and i18n across all screens
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
/**
|
||||
* GlassCard - Liquid Glass Container
|
||||
* iOS 18.4 inspired glassmorphism
|
||||
* iOS 18.4 inspired glassmorphism — theme-aware
|
||||
*/
|
||||
|
||||
import { ReactNode } from 'react'
|
||||
import { ReactNode, useMemo } from 'react'
|
||||
import { StyleSheet, View, ViewStyle } from 'react-native'
|
||||
import { BlurView } from 'expo-blur'
|
||||
|
||||
import { DARK, GLASS, SHADOW, BORDER } from '../constants/colors'
|
||||
import { useThemeColors } from '../theme'
|
||||
import type { ThemeColors } from '../theme/types'
|
||||
import { RADIUS } from '../constants/borderRadius'
|
||||
|
||||
type GlassVariant = 'base' | 'elevated' | 'inset' | 'tinted'
|
||||
@@ -20,34 +21,38 @@ interface GlassCardProps {
|
||||
blurIntensity?: number
|
||||
}
|
||||
|
||||
const variantStyles: Record<GlassVariant, ViewStyle> = {
|
||||
base: {
|
||||
backgroundColor: GLASS.BASE.backgroundColor,
|
||||
borderColor: GLASS.BASE.borderColor,
|
||||
borderWidth: GLASS.BASE.borderWidth,
|
||||
},
|
||||
elevated: {
|
||||
backgroundColor: GLASS.ELEVATED.backgroundColor,
|
||||
borderColor: GLASS.ELEVATED.borderColor,
|
||||
borderWidth: GLASS.ELEVATED.borderWidth,
|
||||
},
|
||||
inset: {
|
||||
backgroundColor: GLASS.INSET.backgroundColor,
|
||||
borderColor: GLASS.INSET.borderColor,
|
||||
borderWidth: GLASS.INSET.borderWidth,
|
||||
},
|
||||
tinted: {
|
||||
backgroundColor: GLASS.TINTED.backgroundColor,
|
||||
borderColor: GLASS.TINTED.borderColor,
|
||||
borderWidth: GLASS.TINTED.borderWidth,
|
||||
},
|
||||
function getVariantStyles(colors: ThemeColors): Record<GlassVariant, ViewStyle> {
|
||||
return {
|
||||
base: {
|
||||
backgroundColor: colors.glass.base.backgroundColor,
|
||||
borderColor: colors.glass.base.borderColor,
|
||||
borderWidth: colors.glass.base.borderWidth,
|
||||
},
|
||||
elevated: {
|
||||
backgroundColor: colors.glass.elevated.backgroundColor,
|
||||
borderColor: colors.glass.elevated.borderColor,
|
||||
borderWidth: colors.glass.elevated.borderWidth,
|
||||
},
|
||||
inset: {
|
||||
backgroundColor: colors.glass.inset.backgroundColor,
|
||||
borderColor: colors.glass.inset.borderColor,
|
||||
borderWidth: colors.glass.inset.borderWidth,
|
||||
},
|
||||
tinted: {
|
||||
backgroundColor: colors.glass.tinted.backgroundColor,
|
||||
borderColor: colors.glass.tinted.borderColor,
|
||||
borderWidth: colors.glass.tinted.borderWidth,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const shadowStyles: Record<GlassVariant, ViewStyle> = {
|
||||
base: SHADOW.sm,
|
||||
elevated: SHADOW.md,
|
||||
inset: {},
|
||||
tinted: SHADOW.sm,
|
||||
function getShadowStyles(colors: ThemeColors): Record<GlassVariant, ViewStyle> {
|
||||
return {
|
||||
base: colors.shadow.sm,
|
||||
elevated: colors.shadow.md,
|
||||
inset: {},
|
||||
tinted: colors.shadow.sm,
|
||||
}
|
||||
}
|
||||
|
||||
export function GlassCard({
|
||||
@@ -55,17 +60,22 @@ export function GlassCard({
|
||||
variant = 'base',
|
||||
style,
|
||||
hasBlur = true,
|
||||
blurIntensity = GLASS.BLUR_MEDIUM,
|
||||
blurIntensity,
|
||||
}: GlassCardProps) {
|
||||
const colors = useThemeColors()
|
||||
const variantStyles = useMemo(() => getVariantStyles(colors), [colors])
|
||||
const shadowStyles = useMemo(() => getShadowStyles(colors), [colors])
|
||||
|
||||
const glassStyle = variantStyles[variant]
|
||||
const shadowStyle = shadowStyles[variant]
|
||||
const intensity = blurIntensity ?? colors.glass.blurMedium
|
||||
|
||||
if (hasBlur) {
|
||||
return (
|
||||
<View style={[styles.container, glassStyle, shadowStyle, style]}>
|
||||
<BlurView
|
||||
intensity={blurIntensity}
|
||||
tint="dark"
|
||||
intensity={intensity}
|
||||
tint={colors.glass.blurTint}
|
||||
style={StyleSheet.absoluteFill}
|
||||
/>
|
||||
<View style={styles.content}>{children}</View>
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
* Reusable wrapper for each onboarding screen — progress bar, animation, layout
|
||||
*/
|
||||
|
||||
import { useRef, useEffect } from 'react'
|
||||
import { useRef, useEffect, useMemo } from 'react'
|
||||
import { View, StyleSheet, Animated, Dimensions } from 'react-native'
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
import { DARK, BRAND, TEXT } from '../constants/colors'
|
||||
import { useThemeColors, BRAND } from '../theme'
|
||||
import type { ThemeColors } from '../theme/types'
|
||||
import { SPACING, LAYOUT } from '../constants/spacing'
|
||||
import { DURATION, EASE } from '../constants/animations'
|
||||
|
||||
@@ -19,6 +20,8 @@ interface OnboardingStepProps {
|
||||
}
|
||||
|
||||
export function OnboardingStep({ step, totalSteps, children }: OnboardingStepProps) {
|
||||
const colors = useThemeColors()
|
||||
const styles = useMemo(() => createStyles(colors), [colors])
|
||||
const insets = useSafeAreaInsets()
|
||||
const slideAnim = useRef(new Animated.Value(SCREEN_WIDTH)).current
|
||||
const fadeAnim = useRef(new Animated.Value(0)).current
|
||||
@@ -83,26 +86,28 @@ export function OnboardingStep({ step, totalSteps, children }: OnboardingStepPro
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: DARK.BASE,
|
||||
},
|
||||
progressTrack: {
|
||||
height: 3,
|
||||
backgroundColor: DARK.SURFACE,
|
||||
marginHorizontal: LAYOUT.SCREEN_PADDING,
|
||||
borderRadius: 2,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
progressFill: {
|
||||
height: '100%',
|
||||
backgroundColor: BRAND.PRIMARY,
|
||||
borderRadius: 2,
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
paddingHorizontal: LAYOUT.SCREEN_PADDING,
|
||||
paddingTop: SPACING[8],
|
||||
},
|
||||
})
|
||||
function createStyles(colors: ThemeColors) {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.bg.base,
|
||||
},
|
||||
progressTrack: {
|
||||
height: 3,
|
||||
backgroundColor: colors.bg.surface,
|
||||
marginHorizontal: LAYOUT.SCREEN_PADDING,
|
||||
borderRadius: 2,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
progressFill: {
|
||||
height: '100%',
|
||||
backgroundColor: BRAND.PRIMARY,
|
||||
borderRadius: 2,
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
paddingHorizontal: LAYOUT.SCREEN_PADDING,
|
||||
paddingTop: SPACING[8],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* TabataFit StyledText
|
||||
* Unified text component — replaces 5 local copies
|
||||
* Unified text component — uses theme for default color
|
||||
*/
|
||||
|
||||
import { Text as RNText, TextStyle, StyleProp } from 'react-native'
|
||||
import { TEXT } from '../constants/colors'
|
||||
import { useThemeColors } from '../theme'
|
||||
|
||||
type FontWeight = 'regular' | 'medium' | 'semibold' | 'bold'
|
||||
|
||||
@@ -28,17 +28,20 @@ export function StyledText({
|
||||
children,
|
||||
size = 17,
|
||||
weight = 'regular',
|
||||
color = TEXT.PRIMARY,
|
||||
color,
|
||||
style,
|
||||
numberOfLines,
|
||||
}: StyledTextProps) {
|
||||
const colors = useThemeColors()
|
||||
const resolvedColor = color ?? colors.text.primary
|
||||
|
||||
return (
|
||||
<RNText
|
||||
style={[
|
||||
{
|
||||
fontSize: size,
|
||||
fontWeight: WEIGHT_MAP[weight],
|
||||
color,
|
||||
color: resolvedColor,
|
||||
},
|
||||
style,
|
||||
]}
|
||||
|
||||
Reference in New Issue
Block a user