@umituz/react-native-design-system 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,17 +1,19 @@
1
1
  # @umituz/react-native-design-system
2
2
 
3
- Universal design system for React Native apps following Domain-Driven Design (DDD) architecture with Material Design 3 components.
3
+ Universal design system for React Native apps following Domain-Driven Design (DDD) architecture with Material Design 3 principles.
4
4
 
5
5
  ## âœĻ Features
6
6
 
7
7
  - ðŸŽĻ **Material Design 3** - Modern, accessible UI components
8
- - ⚛ïļ **Atomic Design** - Organized component hierarchy (Atoms → Molecules → Organisms)
9
- - 🏗ïļ **DDD Architecture** - Clean domain-driven structure
8
+ - ⚛ïļ **Pure React Native** - No external UI library dependencies (lightweight!)
9
+ - 🏗ïļ **Atomic Design** - Organized component hierarchy (Atoms → Molecules → Organisms)
10
+ - 🧎 **DDD Architecture** - Clean domain-driven structure
10
11
  - 🌓 **Theme Support** - Built-in light/dark mode
11
12
  - ðŸ“ą **Responsive** - Adaptive layouts for phones and tablets
12
13
  - â™ŋ **Accessible** - WCAG AA compliant components
13
14
  - 🎭 **Animations** - Smooth React Native Reanimated animations
14
15
  - ðŸ“Ķ **Zero Config** - Works out of the box
16
+ - ðŸŠķ **Lightweight** - Smaller bundle size (no Paper dependency)
15
17
 
16
18
  ## ðŸ“Ķ Installation
17
19
 
@@ -22,9 +24,11 @@ npm install @umituz/react-native-design-system
22
24
  ### Peer Dependencies
23
25
 
24
26
  ```bash
25
- npm install react@18.3.1 react-native@0.76.3 react-native-paper@^5.12.5 react-native-reanimated@~3.10.1
27
+ npm install react@18.3.1 react-native@0.76.3 react-native-reanimated@~3.10.1 lucide-react-native@^0.468.0
26
28
  ```
27
29
 
30
+ > **v1.3.0 Breaking Change**: React Native Paper dependency removed! All components now use pure React Native implementation for lighter bundle size and full control over styling.
31
+
28
32
  ## 🚀 Usage
29
33
 
30
34
  ```typescript
@@ -60,11 +64,12 @@ const MyScreen = () => {
60
64
  ## ðŸ§Đ Components
61
65
 
62
66
  ### Atoms (Primitive UI Components)
63
- - `AtomicButton` - Material Design 3 buttons with variants
64
- - `AtomicText` - Typography with MD3 type scale
65
- - `AtomicInput` - Text inputs with validation
66
- - `AtomicCard` - Container cards
67
- - `AtomicIcon` - Icon components
67
+ - `AtomicButton` - Pure React Native buttons with variants (primary, secondary, outline, text, danger)
68
+ - `AtomicText` - Typography with MD3 type scale (pure RN Text)
69
+ - `AtomicInput` - Text inputs with validation states (pure RN TextInput)
70
+ - `AtomicTextArea` - Multiline inputs with character counter (pure RN TextInput)
71
+ - `AtomicCard` - Container cards with elevation (pure RN View)
72
+ - `AtomicIcon` - Lucide icons with 1,639 icons
68
73
  - `AtomicSwitch` - Toggle switches
69
74
  - `AtomicBadge` - Status badges
70
75
  - `AtomicProgress` - Progress indicators
@@ -79,9 +84,9 @@ const MyScreen = () => {
79
84
  - And more...
80
85
 
81
86
  ### Organisms (Complex Patterns)
82
- - `ScreenLayout` - Screen wrapper with safe area
87
+ - `ScreenLayout` - Screen wrapper with safe area (pure RN View)
83
88
  - `AppHeader` - Application header
84
- - `FormContainer` - Form layout container
89
+ - `FormContainer` - Form layout container with keyboard handling (pure RN View + ScrollView)
85
90
 
86
91
  ## ðŸŽĻ Design Tokens
87
92
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Universal design system for React Native apps - Domain-Driven Design architecture with Material Design 3 components",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -27,7 +27,6 @@
27
27
  "peerDependencies": {
28
28
  "react": ">=18.2.0",
29
29
  "react-native": ">=0.74.0",
30
- "react-native-paper": "^5.12.5",
31
30
  "react-native-reanimated": "~3.10.1",
32
31
  "@react-native-community/datetimepicker": "8.0.1",
33
32
  "@expo/vector-icons": "^14.0.0",
@@ -16,6 +16,7 @@
16
16
 
17
17
  import type { DesignTokens } from '@umituz/react-native-design-system';
18
18
 
19
+ export type IconName = string;
19
20
  export type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
20
21
  export type IconColor =
21
22
  | 'primary'
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
- import { Button as PaperButton } from 'react-native-paper';
3
- import { StyleSheet, StyleProp, ViewStyle, TextStyle } from 'react-native';
2
+ import { StyleSheet, StyleProp, ViewStyle, TextStyle, TouchableOpacity, ActivityIndicator, View } from 'react-native';
4
3
  import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
5
- import { Pressable } from 'react-native';
4
+ import { AtomicText } from './AtomicText';
5
+ import { Icon } from '../../domains/icons/presentation/components/Icon';
6
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
7
+ import type { IconName } from '../../domains/icons/domain/interfaces/IIconAdapter';
6
8
 
7
9
  export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'text' | 'danger';
8
10
  export type ButtonSize = 'sm' | 'md' | 'lg';
@@ -15,14 +17,14 @@ export interface AtomicButtonProps {
15
17
  size?: ButtonSize;
16
18
  disabled?: boolean;
17
19
  loading?: boolean;
18
- icon?: string;
20
+ icon?: IconName;
19
21
  fullWidth?: boolean;
20
22
  style?: StyleProp<ViewStyle>;
21
23
  textStyle?: StyleProp<TextStyle>;
22
24
  testID?: string;
23
25
  }
24
26
 
25
- const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
27
+ const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity);
26
28
 
27
29
  export const AtomicButton: React.FC<AtomicButtonProps> = ({
28
30
  title,
@@ -35,8 +37,11 @@ export const AtomicButton: React.FC<AtomicButtonProps> = ({
35
37
  icon,
36
38
  fullWidth = false,
37
39
  style,
40
+ textStyle,
38
41
  testID,
39
42
  }) => {
43
+ const tokens = useAppDesignTokens();
44
+
40
45
  // Animation
41
46
  const scale = useSharedValue(1);
42
47
 
@@ -63,62 +68,205 @@ export const AtomicButton: React.FC<AtomicButtonProps> = ({
63
68
  }
64
69
  };
65
70
 
66
- // Map variants to Paper modes
67
- const getPaperMode = (): 'contained' | 'outlined' | 'text' | 'contained-tonal' | 'elevated' => {
71
+ // Size configurations
72
+ const sizeConfig = {
73
+ sm: {
74
+ paddingVertical: tokens.spacing.xs,
75
+ paddingHorizontal: tokens.spacing.sm,
76
+ fontSize: tokens.typography.bodySmall.fontSize,
77
+ iconSize: 16,
78
+ minHeight: 32,
79
+ },
80
+ md: {
81
+ paddingVertical: tokens.spacing.sm,
82
+ paddingHorizontal: tokens.spacing.md,
83
+ fontSize: tokens.typography.bodyMedium.fontSize,
84
+ iconSize: 20,
85
+ minHeight: 44,
86
+ },
87
+ lg: {
88
+ paddingVertical: tokens.spacing.md,
89
+ paddingHorizontal: tokens.spacing.lg,
90
+ fontSize: tokens.typography.bodyLarge.fontSize,
91
+ iconSize: 24,
92
+ minHeight: 52,
93
+ },
94
+ };
95
+
96
+ const config = sizeConfig[size];
97
+
98
+ // Variant styles
99
+ const getVariantStyles = () => {
100
+ const baseStyle: ViewStyle = {
101
+ backgroundColor: tokens.colors.primary,
102
+ borderWidth: 0,
103
+ };
104
+
105
+ const baseTextStyle: TextStyle = {
106
+ color: tokens.colors.textInverse,
107
+ };
108
+
68
109
  switch (variant) {
69
110
  case 'primary':
70
- return 'contained';
111
+ return {
112
+ container: {
113
+ ...baseStyle,
114
+ backgroundColor: tokens.colors.primary,
115
+ },
116
+ text: {
117
+ ...baseTextStyle,
118
+ color: tokens.colors.textInverse,
119
+ },
120
+ };
121
+
71
122
  case 'secondary':
72
- return 'contained-tonal';
123
+ return {
124
+ container: {
125
+ ...baseStyle,
126
+ backgroundColor: tokens.colors.surfaceSecondary,
127
+ },
128
+ text: {
129
+ ...baseTextStyle,
130
+ color: tokens.colors.textPrimary,
131
+ },
132
+ };
133
+
73
134
  case 'outline':
74
- return 'outlined';
135
+ return {
136
+ container: {
137
+ ...baseStyle,
138
+ backgroundColor: 'transparent',
139
+ borderWidth: 1,
140
+ borderColor: tokens.colors.border,
141
+ },
142
+ text: {
143
+ ...baseTextStyle,
144
+ color: tokens.colors.textPrimary,
145
+ },
146
+ };
147
+
75
148
  case 'text':
76
- return 'text';
149
+ return {
150
+ container: {
151
+ ...baseStyle,
152
+ backgroundColor: 'transparent',
153
+ },
154
+ text: {
155
+ ...baseTextStyle,
156
+ color: tokens.colors.primary,
157
+ },
158
+ };
159
+
77
160
  case 'danger':
78
- return 'contained';
161
+ return {
162
+ container: {
163
+ ...baseStyle,
164
+ backgroundColor: tokens.colors.error,
165
+ },
166
+ text: {
167
+ ...baseTextStyle,
168
+ color: tokens.colors.textInverse,
169
+ },
170
+ };
171
+
79
172
  default:
80
- return 'contained';
173
+ return {
174
+ container: baseStyle,
175
+ text: baseTextStyle,
176
+ };
81
177
  }
82
178
  };
83
179
 
84
- // Map size to padding
85
- const getContentStyle = () => {
86
- const paddingMap = {
87
- sm: { paddingVertical: 4, paddingHorizontal: 12 },
88
- md: { paddingVertical: 8, paddingHorizontal: 16 },
89
- lg: { paddingVertical: 12, paddingHorizontal: 20 },
90
- };
91
- return paddingMap[size];
92
- };
180
+ const variantStyles = getVariantStyles();
93
181
 
94
- const buttonStyle: StyleProp<ViewStyle> = StyleSheet.flatten([
95
- fullWidth ? { width: '100%' } : undefined,
182
+ const containerStyle: StyleProp<ViewStyle> = [
183
+ styles.button,
184
+ {
185
+ paddingVertical: config.paddingVertical,
186
+ paddingHorizontal: config.paddingHorizontal,
187
+ minHeight: config.minHeight,
188
+ borderRadius: tokens.borders.radius.md,
189
+ },
190
+ variantStyles.container,
191
+ fullWidth ? styles.fullWidth : undefined,
192
+ disabled ? styles.disabled : undefined,
96
193
  style,
97
- ]);
194
+ ];
195
+
196
+ const buttonTextStyle: StyleProp<TextStyle> = [
197
+ {
198
+ fontSize: config.fontSize,
199
+ fontWeight: '600',
200
+ },
201
+ variantStyles.text,
202
+ disabled ? styles.disabledText : undefined,
203
+ textStyle,
204
+ ];
98
205
 
99
206
  const buttonText = title || children;
207
+ const showIcon = icon && !loading;
208
+ const iconColor = variantStyles.text.color;
100
209
 
101
210
  return (
102
- <AnimatedPressable
103
- style={animatedStyle}
211
+ <AnimatedTouchable
212
+ style={[animatedStyle, containerStyle]}
104
213
  onPressIn={handlePressIn}
105
214
  onPressOut={handlePressOut}
106
215
  onPress={handlePress}
216
+ activeOpacity={0.8}
217
+ disabled={disabled || loading}
218
+ testID={testID}
107
219
  >
108
- <PaperButton
109
- mode={getPaperMode()}
110
- disabled={disabled}
111
- loading={loading}
112
- icon={icon}
113
- style={buttonStyle}
114
- contentStyle={getContentStyle()}
115
- testID={testID}
116
- buttonColor={variant === 'danger' ? '#DC2626' : undefined}
117
- >
118
- {buttonText}
119
- </PaperButton>
120
- </AnimatedPressable>
220
+ <View style={styles.content}>
221
+ {loading ? (
222
+ <ActivityIndicator
223
+ size="small"
224
+ color={variantStyles.text.color}
225
+ style={styles.loader}
226
+ />
227
+ ) : showIcon ? (
228
+ <Icon
229
+ name={icon}
230
+ customSize={config.iconSize}
231
+ customColor={typeof iconColor === 'string' ? iconColor : undefined}
232
+ style={styles.icon}
233
+ />
234
+ ) : null}
235
+
236
+ <AtomicText style={buttonTextStyle}>
237
+ {buttonText}
238
+ </AtomicText>
239
+ </View>
240
+ </AnimatedTouchable>
121
241
  );
122
242
  };
123
243
 
244
+ const styles = StyleSheet.create({
245
+ button: {
246
+ alignItems: 'center',
247
+ justifyContent: 'center',
248
+ flexDirection: 'row',
249
+ },
250
+ content: {
251
+ flexDirection: 'row',
252
+ alignItems: 'center',
253
+ justifyContent: 'center',
254
+ },
255
+ fullWidth: {
256
+ width: '100%',
257
+ },
258
+ disabled: {
259
+ opacity: 0.5,
260
+ },
261
+ disabledText: {
262
+ opacity: 0.7,
263
+ },
264
+ icon: {
265
+ marginRight: 8,
266
+ },
267
+ loader: {
268
+ marginRight: 8,
269
+ },
270
+ });
271
+
124
272
  export type { AtomicButtonProps as ButtonProps };
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { Card as PaperCard } from 'react-native-paper';
3
- import { StyleProp, ViewStyle } from 'react-native';
2
+ import { View, StyleProp, ViewStyle } from 'react-native';
4
3
  import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
5
4
  import { Pressable } from 'react-native';
5
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
6
6
 
7
7
  export type AtomicCardVariant = 'flat' | 'elevated' | 'outlined';
8
8
  export type AtomicCardPadding = 'none' | 'sm' | 'md' | 'lg' | 'xl';
@@ -28,6 +28,8 @@ export const AtomicCard: React.FC<AtomicCardProps> = ({
28
28
  children,
29
29
  testID,
30
30
  }) => {
31
+ const tokens = useAppDesignTokens();
32
+
31
33
  // Animation for tap feedback
32
34
  const scale = useSharedValue(1);
33
35
 
@@ -54,43 +56,64 @@ export const AtomicCard: React.FC<AtomicCardProps> = ({
54
56
  }
55
57
  };
56
58
 
57
- // Map variants to Paper modes
58
- const getPaperMode = (): 'elevated' | 'outlined' | 'contained' => {
59
+ // Map padding to token values
60
+ const getPaddingValue = (): number => {
61
+ const paddingMap = {
62
+ none: 0,
63
+ sm: tokens.spacing.sm,
64
+ md: tokens.spacing.md,
65
+ lg: tokens.spacing.lg,
66
+ xl: tokens.spacing.xl,
67
+ };
68
+ return paddingMap[padding];
69
+ };
70
+
71
+ // Get variant styles
72
+ const getVariantStyle = (): ViewStyle => {
73
+ const baseStyle: ViewStyle = {
74
+ backgroundColor: tokens.colors.surface,
75
+ borderRadius: tokens.borders.radius.md,
76
+ };
77
+
59
78
  switch (variant) {
60
79
  case 'elevated':
61
- return 'elevated';
80
+ return {
81
+ ...baseStyle,
82
+ borderWidth: 1,
83
+ borderColor: tokens.colors.border,
84
+ };
85
+
62
86
  case 'outlined':
63
- return 'outlined';
87
+ return {
88
+ ...baseStyle,
89
+ borderWidth: 1,
90
+ borderColor: tokens.colors.border,
91
+ };
92
+
64
93
  case 'flat':
65
- return 'contained';
94
+ return {
95
+ ...baseStyle,
96
+ borderWidth: 0,
97
+ };
98
+
66
99
  default:
67
- return 'elevated';
100
+ return baseStyle;
68
101
  }
69
102
  };
70
103
 
71
- // Map padding to actual values
72
- const getContentStyle = () => {
73
- const paddingMap = {
74
- none: 0,
75
- sm: 8,
76
- md: 16,
77
- lg: 24,
78
- xl: 32,
79
- };
80
- const paddingValue = paddingMap[padding];
81
- return { padding: paddingValue };
82
- };
104
+ const cardStyle: StyleProp<ViewStyle> = [
105
+ getVariantStyle(),
106
+ {
107
+ padding: getPaddingValue(),
108
+ opacity: disabled ? 0.5 : 1,
109
+ },
110
+ style,
111
+ ];
83
112
 
84
113
  const cardContent = (
85
- <PaperCard
86
- mode={getPaperMode()}
87
- style={style}
88
- testID={testID}
89
- >
90
- <PaperCard.Content style={getContentStyle()}>
91
- {children}
92
- </PaperCard.Content>
93
- </PaperCard>
114
+ <View style={cardStyle} testID={testID}>
115
+ {children}
116
+ </View>
94
117
  );
95
118
 
96
119
  // If onPress provided, wrap with animated pressable
@@ -1,8 +1,8 @@
1
1
  import React, { useState } from 'react';
2
- import { View, Pressable, StyleProp, ViewStyle, TextStyle } from 'react-native';
3
- import { TextInput, HelperText } from 'react-native-paper';
2
+ import { View, TextInput, Pressable, StyleSheet, StyleProp, ViewStyle, TextStyle } from 'react-native';
4
3
  import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
5
4
  import { AtomicIcon } from './AtomicIcon';
5
+ import { AtomicText } from './AtomicText';
6
6
  import type { AtomicIconName, AtomicIconSize } from './AtomicIcon';
7
7
 
8
8
  export type AtomicInputVariant = 'outlined' | 'filled' | 'flat';
@@ -61,12 +61,12 @@ export interface AtomicInputProps {
61
61
  }
62
62
 
63
63
  /**
64
- * AtomicInput - Material Design 3 Text Input
64
+ * AtomicInput - Pure React Native Text Input
65
65
  *
66
66
  * Features:
67
- * - React Native Paper TextInput integration
67
+ * - Pure React Native implementation (no Paper dependency)
68
68
  * - Lucide icons for password toggle and custom icons
69
- * - Material Design 3 outlined/filled/flat variants
69
+ * - Outlined/filled/flat variants
70
70
  * - Error, success, disabled states
71
71
  * - Character counter
72
72
  * - Responsive sizing
@@ -100,128 +100,212 @@ export const AtomicInput: React.FC<AtomicInputProps> = ({
100
100
  }) => {
101
101
  const tokens = useAppDesignTokens();
102
102
  const [isPasswordVisible, setIsPasswordVisible] = useState(false);
103
+ const [isFocused, setIsFocused] = useState(false);
103
104
  const isDisabled = state === 'disabled' || disabled;
104
105
  const characterCount = value?.toString().length || 0;
106
+ const hasError = state === 'error';
107
+ const hasSuccess = state === 'success';
105
108
 
106
- // Map variant to Paper mode
107
- const getPaperMode = (): 'outlined' | 'flat' => {
108
- if (variant === 'outlined') return 'outlined';
109
- return 'flat'; // filled and flat both use 'flat' mode
109
+ // Size configuration
110
+ const sizeConfig = {
111
+ sm: {
112
+ paddingVertical: tokens.spacing.xs,
113
+ paddingHorizontal: tokens.spacing.sm,
114
+ fontSize: tokens.typography.bodySmall.fontSize,
115
+ iconSize: 16,
116
+ minHeight: 40,
117
+ },
118
+ md: {
119
+ paddingVertical: tokens.spacing.sm,
120
+ paddingHorizontal: tokens.spacing.md,
121
+ fontSize: tokens.typography.bodyMedium.fontSize,
122
+ iconSize: 20,
123
+ minHeight: 48,
124
+ },
125
+ lg: {
126
+ paddingVertical: tokens.spacing.md,
127
+ paddingHorizontal: tokens.spacing.lg,
128
+ fontSize: tokens.typography.bodyLarge.fontSize,
129
+ iconSize: 24,
130
+ minHeight: 56,
131
+ },
110
132
  };
111
133
 
112
- // Map state to Paper error prop
113
- const hasError = state === 'error';
134
+ const config = sizeConfig[size];
114
135
 
115
- // Get icon size based on input size
116
- const getIconSize = (): AtomicIconSize => {
117
- switch (size) {
118
- case 'sm': return 'xs';
119
- case 'lg': return 'md';
120
- default: return 'sm';
121
- }
122
- };
136
+ // Get variant styles
137
+ const getVariantStyle = (): ViewStyle => {
138
+ const baseStyle: ViewStyle = {
139
+ backgroundColor: tokens.colors.surface,
140
+ borderRadius: tokens.borders.radius.md,
141
+ };
123
142
 
124
- const iconSizeName = getIconSize();
125
- const iconColor = isDisabled
126
- ? tokens.colors.onSurfaceDisabled
127
- : tokens.colors.surfaceVariant;
128
-
129
- // Render leading icon
130
- const renderLeadingIcon = leadingIcon ? () => (
131
- <AtomicIcon
132
- name={leadingIcon}
133
- size={iconSizeName}
134
- customColor={iconColor}
135
- />
136
- ) : undefined;
137
-
138
- // Render trailing icon or password toggle
139
- const renderTrailingIcon = () => {
140
- if (showPasswordToggle && secureTextEntry) {
141
- return (
142
- <Pressable onPress={() => setIsPasswordVisible(!isPasswordVisible)}>
143
- <AtomicIcon
144
- name={isPasswordVisible ? "EyeOff" : "Eye"}
145
- size={iconSizeName}
146
- customColor={iconColor}
147
- />
148
- </Pressable>
149
- );
150
- }
143
+ let borderColor = tokens.colors.border;
144
+ if (isFocused) borderColor = tokens.colors.primary;
145
+ if (hasError) borderColor = tokens.colors.error;
146
+ if (hasSuccess) borderColor = tokens.colors.success;
147
+ if (isDisabled) borderColor = tokens.colors.borderDisabled;
151
148
 
152
- if (trailingIcon) {
153
- const icon = (
154
- <AtomicIcon
155
- name={trailingIcon}
156
- size={iconSizeName}
157
- customColor={iconColor}
158
- />
159
- );
149
+ switch (variant) {
150
+ case 'outlined':
151
+ return {
152
+ ...baseStyle,
153
+ borderWidth: isFocused ? 2 : 1,
154
+ borderColor,
155
+ };
160
156
 
161
- return onTrailingIconPress ? (
162
- <Pressable onPress={onTrailingIconPress}>{icon}</Pressable>
163
- ) : icon;
164
- }
157
+ case 'filled':
158
+ return {
159
+ ...baseStyle,
160
+ backgroundColor: tokens.colors.surfaceSecondary,
161
+ borderWidth: 0,
162
+ borderBottomWidth: isFocused ? 2 : 1,
163
+ borderBottomColor: borderColor,
164
+ };
165
+
166
+ case 'flat':
167
+ return {
168
+ ...baseStyle,
169
+ backgroundColor: 'transparent',
170
+ borderWidth: 0,
171
+ borderBottomWidth: 1,
172
+ borderBottomColor: borderColor,
173
+ borderRadius: 0,
174
+ };
165
175
 
166
- return undefined;
176
+ default:
177
+ return baseStyle;
178
+ }
167
179
  };
168
180
 
169
181
  // Get text color based on state
170
182
  const getTextColor = () => {
171
- if (state === 'error') return tokens.colors.error;
172
- if (state === 'success') return tokens.colors.success;
173
- return tokens.colors.onSurface;
183
+ if (isDisabled) return tokens.colors.textDisabled;
184
+ if (hasError) return tokens.colors.error;
185
+ if (hasSuccess) return tokens.colors.success;
186
+ return tokens.colors.textPrimary;
174
187
  };
175
188
 
189
+ const iconColor = isDisabled ? tokens.colors.textDisabled : tokens.colors.textSecondary;
190
+
191
+ const containerStyle: StyleProp<ViewStyle> = [
192
+ styles.container,
193
+ getVariantStyle(),
194
+ {
195
+ paddingVertical: config.paddingVertical,
196
+ paddingHorizontal: config.paddingHorizontal,
197
+ minHeight: config.minHeight,
198
+ opacity: isDisabled ? 0.5 : 1,
199
+ },
200
+ style,
201
+ ];
202
+
203
+ const textInputStyle: StyleProp<TextStyle> = [
204
+ styles.input,
205
+ {
206
+ fontSize: config.fontSize,
207
+ color: getTextColor(),
208
+ },
209
+ leadingIcon ? { paddingLeft: config.iconSize + 8 } : undefined,
210
+ (trailingIcon || showPasswordToggle) ? { paddingRight: config.iconSize + 8 } : undefined,
211
+ inputStyle,
212
+ ];
213
+
176
214
  return (
177
- <View style={style} testID={testID}>
178
- <TextInput
179
- mode={getPaperMode()}
180
- label={label}
181
- value={value}
182
- onChangeText={onChangeText}
183
- placeholder={placeholder}
184
- error={hasError}
185
- disabled={isDisabled}
186
- secureTextEntry={secureTextEntry && !isPasswordVisible}
187
- maxLength={maxLength}
188
- keyboardType={keyboardType}
189
- autoCapitalize={autoCapitalize}
190
- autoCorrect={autoCorrect}
191
- left={renderLeadingIcon ? <TextInput.Icon icon={renderLeadingIcon} /> : undefined}
192
- right={renderTrailingIcon() ? <TextInput.Icon icon={renderTrailingIcon} /> : undefined}
193
- style={inputStyle}
194
- textColor={getTextColor()}
195
- onBlur={onBlur}
196
- onFocus={onFocus}
197
- testID={testID ? `${testID}-input` : undefined}
198
- />
215
+ <View testID={testID}>
216
+ {label && (
217
+ <AtomicText
218
+ type="labelMedium"
219
+ color={hasError ? 'error' : hasSuccess ? 'success' : 'secondary'}
220
+ style={styles.label}
221
+ >
222
+ {label}
223
+ </AtomicText>
224
+ )}
225
+
226
+ <View style={containerStyle}>
227
+ {leadingIcon && (
228
+ <View style={styles.leadingIcon}>
229
+ <AtomicIcon
230
+ name={leadingIcon}
231
+ customSize={config.iconSize}
232
+ customColor={iconColor}
233
+ />
234
+ </View>
235
+ )}
236
+
237
+ <TextInput
238
+ value={value}
239
+ onChangeText={onChangeText}
240
+ placeholder={placeholder}
241
+ placeholderTextColor={tokens.colors.textSecondary}
242
+ secureTextEntry={secureTextEntry && !isPasswordVisible}
243
+ maxLength={maxLength}
244
+ keyboardType={keyboardType}
245
+ autoCapitalize={autoCapitalize}
246
+ autoCorrect={autoCorrect}
247
+ editable={!isDisabled}
248
+ style={textInputStyle}
249
+ onBlur={() => {
250
+ setIsFocused(false);
251
+ onBlur?.();
252
+ }}
253
+ onFocus={() => {
254
+ setIsFocused(true);
255
+ onFocus?.();
256
+ }}
257
+ testID={testID ? `${testID}-input` : undefined}
258
+ />
259
+
260
+ {(showPasswordToggle && secureTextEntry) && (
261
+ <Pressable
262
+ onPress={() => setIsPasswordVisible(!isPasswordVisible)}
263
+ style={styles.trailingIcon}
264
+ >
265
+ <AtomicIcon
266
+ name={isPasswordVisible ? "EyeOff" : "Eye"}
267
+ customSize={config.iconSize}
268
+ customColor={iconColor}
269
+ />
270
+ </Pressable>
271
+ )}
272
+
273
+ {trailingIcon && !showPasswordToggle && (
274
+ <Pressable
275
+ onPress={onTrailingIconPress}
276
+ style={styles.trailingIcon}
277
+ disabled={!onTrailingIconPress}
278
+ >
279
+ <AtomicIcon
280
+ name={trailingIcon}
281
+ customSize={config.iconSize}
282
+ customColor={iconColor}
283
+ />
284
+ </Pressable>
285
+ )}
286
+ </View>
199
287
 
200
288
  {(helperText || showCharacterCount) && (
201
- <View style={{
202
- flexDirection: 'row',
203
- justifyContent: 'space-between',
204
- marginTop: tokens.spacing.xs,
205
- }}>
289
+ <View style={styles.helperRow}>
206
290
  {helperText && (
207
- <HelperText
208
- type={hasError ? 'error' : 'info'}
209
- visible={Boolean(helperText)}
210
- style={{ flex: 1 }}
291
+ <AtomicText
292
+ type="bodySmall"
293
+ color={hasError ? 'error' : 'secondary'}
294
+ style={styles.helperText}
211
295
  testID={testID ? `${testID}-helper` : undefined}
212
296
  >
213
297
  {helperText}
214
- </HelperText>
298
+ </AtomicText>
215
299
  )}
216
300
  {showCharacterCount && maxLength && (
217
- <HelperText
218
- type="info"
219
- visible={true}
220
- style={{ marginLeft: tokens.spacing.xs }}
301
+ <AtomicText
302
+ type="bodySmall"
303
+ color="secondary"
304
+ style={styles.characterCount}
221
305
  testID={testID ? `${testID}-count` : undefined}
222
306
  >
223
307
  {characterCount}/{maxLength}
224
- </HelperText>
308
+ </AtomicText>
225
309
  )}
226
310
  </View>
227
311
  )}
@@ -229,4 +313,38 @@ export const AtomicInput: React.FC<AtomicInputProps> = ({
229
313
  );
230
314
  };
231
315
 
316
+ const styles = StyleSheet.create({
317
+ container: {
318
+ flexDirection: 'row',
319
+ alignItems: 'center',
320
+ },
321
+ input: {
322
+ flex: 1,
323
+ },
324
+ label: {
325
+ marginBottom: 4,
326
+ },
327
+ leadingIcon: {
328
+ position: 'absolute',
329
+ left: 12,
330
+ zIndex: 1,
331
+ },
332
+ trailingIcon: {
333
+ position: 'absolute',
334
+ right: 12,
335
+ zIndex: 1,
336
+ },
337
+ helperRow: {
338
+ flexDirection: 'row',
339
+ justifyContent: 'space-between',
340
+ marginTop: 4,
341
+ },
342
+ helperText: {
343
+ flex: 1,
344
+ },
345
+ characterCount: {
346
+ marginLeft: 8,
347
+ },
348
+ });
349
+
232
350
  export type { AtomicInputProps as InputProps };
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { Text as PaperText } from 'react-native-paper';
3
- import { StyleProp, TextStyle } from 'react-native';
2
+ import { Text, StyleProp, TextStyle } from 'react-native';
3
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
4
4
 
5
5
  export type TextStyleVariant =
6
6
  | 'displayLarge' | 'displayMedium' | 'displaySmall'
@@ -37,14 +37,46 @@ export const AtomicText: React.FC<AtomicTextProps> = ({
37
37
  style,
38
38
  testID,
39
39
  }) => {
40
+ const tokens = useAppDesignTokens();
41
+
42
+ // Get typography style from tokens
43
+ const typographyStyle = tokens.typography[type];
44
+
45
+ // Get color from tokens or use custom color
46
+ const getTextColor = (): string => {
47
+ if (!color) {
48
+ return tokens.colors.textPrimary;
49
+ }
50
+
51
+ // Check if it's a semantic color name
52
+ const colorMap: Record<ColorVariant, string> = {
53
+ primary: tokens.colors.textPrimary,
54
+ secondary: tokens.colors.textSecondary,
55
+ tertiary: tokens.colors.textTertiary,
56
+ disabled: tokens.colors.textDisabled,
57
+ inverse: tokens.colors.textInverse,
58
+ success: tokens.colors.success,
59
+ error: tokens.colors.error,
60
+ warning: tokens.colors.warning,
61
+ info: tokens.colors.info,
62
+ };
63
+
64
+ return colorMap[color as ColorVariant] || color;
65
+ };
66
+
67
+ const textStyle: StyleProp<TextStyle> = [
68
+ typographyStyle,
69
+ { color: getTextColor() },
70
+ style,
71
+ ];
72
+
40
73
  return (
41
- <PaperText
42
- variant={type}
74
+ <Text
43
75
  numberOfLines={numberOfLines}
44
- style={style}
76
+ style={textStyle}
45
77
  testID={testID}
46
78
  >
47
79
  {children}
48
- </PaperText>
80
+ </Text>
49
81
  );
50
82
  };
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * AtomicTextArea Component
3
3
  *
4
- * A multiline text input component with Material Design 3 integration
4
+ * A multiline text input component with pure React Native implementation
5
5
  * for longer text entry with consistent styling.
6
6
  *
7
7
  * Features:
8
- * - React Native Paper TextInput with multiline
9
- * - Material Design 3 outlined/filled/flat variants
8
+ * - Pure React Native TextInput with multiline
9
+ * - Outlined/filled/flat variants
10
10
  * - Error, success, disabled states
11
11
  * - Character counter with max length
12
12
  * - Helper text for guidance or errors
@@ -31,10 +31,10 @@
31
31
  * ```
32
32
  */
33
33
 
34
- import React from 'react';
35
- import { View, StyleProp, ViewStyle, TextStyle } from 'react-native';
36
- import { TextInput, HelperText } from 'react-native-paper';
34
+ import React, { useState } from 'react';
35
+ import { View, TextInput, StyleSheet, StyleProp, ViewStyle, TextStyle } from 'react-native';
37
36
  import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
37
+ import { AtomicText } from './AtomicText';
38
38
 
39
39
  export type AtomicTextAreaVariant = 'outlined' | 'filled' | 'flat';
40
40
  export type AtomicTextAreaState = 'default' | 'error' | 'success' | 'disabled';
@@ -84,7 +84,7 @@ export interface AtomicTextAreaProps {
84
84
  }
85
85
 
86
86
  /**
87
- * AtomicTextArea - Material Design 3 Multiline Text Input
87
+ * AtomicTextArea - Pure React Native Multiline Text Input
88
88
  */
89
89
  export const AtomicTextArea: React.FC<AtomicTextAreaProps> = ({
90
90
  variant = 'outlined',
@@ -109,85 +109,177 @@ export const AtomicTextArea: React.FC<AtomicTextAreaProps> = ({
109
109
  onFocus,
110
110
  }) => {
111
111
  const tokens = useAppDesignTokens();
112
+ const [isFocused, setIsFocused] = useState(false);
112
113
  const isDisabled = state === 'disabled' || disabled;
113
114
  const characterCount = value?.toString().length || 0;
115
+ const hasError = state === 'error';
116
+ const hasSuccess = state === 'success';
114
117
 
115
- // Map variant to Paper mode
116
- const getPaperMode = (): 'outlined' | 'flat' => {
117
- if (variant === 'outlined') return 'outlined';
118
- return 'flat'; // filled and flat both use 'flat' mode
118
+ // Size configuration
119
+ const sizeConfig = {
120
+ sm: {
121
+ paddingVertical: tokens.spacing.xs,
122
+ paddingHorizontal: tokens.spacing.sm,
123
+ fontSize: tokens.typography.bodySmall.fontSize,
124
+ lineHeight: 20,
125
+ },
126
+ md: {
127
+ paddingVertical: tokens.spacing.sm,
128
+ paddingHorizontal: tokens.spacing.md,
129
+ fontSize: tokens.typography.bodyMedium.fontSize,
130
+ lineHeight: 24,
131
+ },
132
+ lg: {
133
+ paddingVertical: tokens.spacing.md,
134
+ paddingHorizontal: tokens.spacing.lg,
135
+ fontSize: tokens.typography.bodyLarge.fontSize,
136
+ lineHeight: 28,
137
+ },
119
138
  };
120
139
 
121
- // Map state to Paper error prop
122
- const hasError = state === 'error';
140
+ const config = sizeConfig[size];
123
141
 
124
142
  // Calculate height based on rows
125
143
  const getTextAreaHeight = () => {
126
144
  if (minHeight) return minHeight;
145
+ const paddingVertical = config.paddingVertical * 2;
146
+ return (rows * config.lineHeight) + paddingVertical;
147
+ };
148
+
149
+ // Get variant styles
150
+ const getVariantStyle = (): ViewStyle => {
151
+ const baseStyle: ViewStyle = {
152
+ backgroundColor: tokens.colors.surface,
153
+ borderRadius: tokens.borders.radius.md,
154
+ };
155
+
156
+ let borderColor = tokens.colors.border;
157
+ if (isFocused) borderColor = tokens.colors.primary;
158
+ if (hasError) borderColor = tokens.colors.error;
159
+ if (hasSuccess) borderColor = tokens.colors.success;
160
+ if (isDisabled) borderColor = tokens.colors.borderDisabled;
161
+
162
+ switch (variant) {
163
+ case 'outlined':
164
+ return {
165
+ ...baseStyle,
166
+ borderWidth: isFocused ? 2 : 1,
167
+ borderColor,
168
+ };
169
+
170
+ case 'filled':
171
+ return {
172
+ ...baseStyle,
173
+ backgroundColor: tokens.colors.surfaceSecondary,
174
+ borderWidth: 0,
175
+ borderBottomWidth: isFocused ? 2 : 1,
176
+ borderBottomColor: borderColor,
177
+ };
127
178
 
128
- // Base line height: 24px per row (approximate)
129
- const lineHeight = 24;
130
- const padding = 32; // Top and bottom padding
131
- return (rows * lineHeight) + padding;
179
+ case 'flat':
180
+ return {
181
+ ...baseStyle,
182
+ backgroundColor: 'transparent',
183
+ borderWidth: 0,
184
+ borderBottomWidth: 1,
185
+ borderBottomColor: borderColor,
186
+ borderRadius: 0,
187
+ };
188
+
189
+ default:
190
+ return baseStyle;
191
+ }
132
192
  };
133
193
 
134
194
  // Get text color based on state
135
195
  const getTextColor = () => {
136
- if (state === 'error') return tokens.colors.error;
137
- if (state === 'success') return tokens.colors.success;
138
- return tokens.colors.onSurface;
196
+ if (isDisabled) return tokens.colors.textDisabled;
197
+ if (hasError) return tokens.colors.error;
198
+ if (hasSuccess) return tokens.colors.success;
199
+ return tokens.colors.textPrimary;
139
200
  };
140
201
 
202
+ const containerStyle: StyleProp<ViewStyle> = [
203
+ styles.container,
204
+ getVariantStyle(),
205
+ {
206
+ paddingVertical: config.paddingVertical,
207
+ paddingHorizontal: config.paddingHorizontal,
208
+ height: getTextAreaHeight(),
209
+ opacity: isDisabled ? 0.5 : 1,
210
+ },
211
+ style,
212
+ ];
213
+
214
+ const textInputStyle: StyleProp<TextStyle> = [
215
+ styles.input,
216
+ {
217
+ fontSize: config.fontSize,
218
+ lineHeight: config.lineHeight,
219
+ color: getTextColor(),
220
+ },
221
+ inputStyle,
222
+ ];
223
+
141
224
  return (
142
- <View style={style} testID={testID}>
143
- <TextInput
144
- mode={getPaperMode()}
145
- label={label}
146
- value={value}
147
- onChangeText={onChangeText}
148
- placeholder={placeholder}
149
- error={hasError}
150
- disabled={isDisabled}
151
- maxLength={maxLength}
152
- autoCapitalize={autoCapitalize}
153
- autoCorrect={autoCorrect}
154
- multiline={true}
155
- numberOfLines={rows}
156
- style={[
157
- { height: getTextAreaHeight(), textAlignVertical: 'top' },
158
- inputStyle,
159
- ]}
160
- textColor={getTextColor()}
161
- onBlur={onBlur}
162
- onFocus={onFocus}
163
- testID={testID ? `${testID}-input` : undefined}
164
- />
225
+ <View testID={testID}>
226
+ {label && (
227
+ <AtomicText
228
+ type="labelMedium"
229
+ color={hasError ? 'error' : hasSuccess ? 'success' : 'secondary'}
230
+ style={styles.label}
231
+ >
232
+ {label}
233
+ </AtomicText>
234
+ )}
235
+
236
+ <View style={containerStyle}>
237
+ <TextInput
238
+ value={value}
239
+ onChangeText={onChangeText}
240
+ placeholder={placeholder}
241
+ placeholderTextColor={tokens.colors.textSecondary}
242
+ maxLength={maxLength}
243
+ autoCapitalize={autoCapitalize}
244
+ autoCorrect={autoCorrect}
245
+ editable={!isDisabled}
246
+ multiline={true}
247
+ numberOfLines={rows}
248
+ textAlignVertical="top"
249
+ style={textInputStyle}
250
+ onBlur={() => {
251
+ setIsFocused(false);
252
+ onBlur?.();
253
+ }}
254
+ onFocus={() => {
255
+ setIsFocused(true);
256
+ onFocus?.();
257
+ }}
258
+ testID={testID ? `${testID}-input` : undefined}
259
+ />
260
+ </View>
165
261
 
166
262
  {(helperText || showCharacterCount) && (
167
- <View style={{
168
- flexDirection: 'row',
169
- justifyContent: 'space-between',
170
- marginTop: tokens.spacing.xs,
171
- }}>
263
+ <View style={styles.helperRow}>
172
264
  {helperText && (
173
- <HelperText
174
- type={hasError ? 'error' : 'info'}
175
- visible={Boolean(helperText)}
176
- style={{ flex: 1 }}
265
+ <AtomicText
266
+ type="bodySmall"
267
+ color={hasError ? 'error' : 'secondary'}
268
+ style={styles.helperText}
177
269
  testID={testID ? `${testID}-helper` : undefined}
178
270
  >
179
271
  {helperText}
180
- </HelperText>
272
+ </AtomicText>
181
273
  )}
182
274
  {showCharacterCount && maxLength && (
183
- <HelperText
184
- type="info"
185
- visible={true}
186
- style={{ marginLeft: tokens.spacing.xs }}
275
+ <AtomicText
276
+ type="bodySmall"
277
+ color="secondary"
278
+ style={styles.characterCount}
187
279
  testID={testID ? `${testID}-count` : undefined}
188
280
  >
189
281
  {characterCount}/{maxLength}
190
- </HelperText>
282
+ </AtomicText>
191
283
  )}
192
284
  </View>
193
285
  )}
@@ -195,4 +287,27 @@ export const AtomicTextArea: React.FC<AtomicTextAreaProps> = ({
195
287
  );
196
288
  };
197
289
 
290
+ const styles = StyleSheet.create({
291
+ container: {
292
+ justifyContent: 'flex-start',
293
+ },
294
+ input: {
295
+ flex: 1,
296
+ },
297
+ label: {
298
+ marginBottom: 4,
299
+ },
300
+ helperRow: {
301
+ flexDirection: 'row',
302
+ justifyContent: 'space-between',
303
+ marginTop: 4,
304
+ },
305
+ helperText: {
306
+ flex: 1,
307
+ },
308
+ characterCount: {
309
+ marginLeft: 8,
310
+ },
311
+ });
312
+
198
313
  export type { AtomicTextAreaProps as TextAreaProps };
@@ -1,17 +1,16 @@
1
1
  /**
2
2
  * FormContainer Component
3
3
  *
4
- * A reusable container for forms with Material Design 3 integration,
5
- * proper keyboard handling, and responsive layout.
4
+ * A reusable container for forms with proper keyboard handling and responsive layout.
6
5
  *
7
6
  * Features:
8
- * - Material Design 3 Surface component for elevation and theme integration
7
+ * - Pure React Native implementation (no Paper dependency)
9
8
  * - Universal keyboard handling (no platform-specific code)
10
9
  * - ScrollView with automatic content padding
11
10
  * - Safe area insets for bottom tab navigation overlap
12
11
  * - Responsive max width for large screens (tablets)
13
12
  * - Consistent vertical spacing between form elements
14
- * - Theme-aware surface colors and elevation
13
+ * - Theme-aware surface colors
15
14
  * - Optimized performance with memoized styles
16
15
  *
17
16
  * Usage:
@@ -31,13 +30,12 @@
31
30
  * - Consistent form layout across all 100+ generated apps
32
31
  * - Responsive design for tablets (max 700px) and phones (full width)
33
32
  * - Automatic vertical spacing between form elements (no manual marginBottom)
34
- * - Material Design 3 surface with proper elevation
35
33
  * - Reduces boilerplate in form screens
36
34
  * - Universal code - no platform checks, works on iOS, Android, Web
37
35
  *
38
36
  * Technical Details:
39
37
  * - Uses ScrollView with contentContainerStyle for keyboard handling
40
- * - React Native Paper Surface for Material Design 3 integration
38
+ * - Pure React Native View for surface (lightweight)
41
39
  * - Vertical spacing via Children.map() wrapping (universal compatibility)
42
40
  * - Safe area insets from react-native-safe-area-context
43
41
  * - Responsive values from useResponsive hook
@@ -53,7 +51,6 @@ import {
53
51
  StyleProp,
54
52
  ViewStyle,
55
53
  } from 'react-native';
56
- import { Surface } from 'react-native-paper';
57
54
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
58
55
  import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
59
56
  import { useResponsive } from '../hooks/useResponsive';
@@ -72,15 +69,15 @@ export interface FormContainerProps {
72
69
  showsVerticalScrollIndicator?: boolean;
73
70
  /** Optional test ID for E2E testing */
74
71
  testID?: string;
75
- /** Disable Material Design elevation (default: false) */
76
- disableElevation?: boolean;
72
+ /** Show surface border (default: true) */
73
+ showBorder?: boolean;
77
74
  }
78
75
 
79
76
  /**
80
77
  * FormContainer - Universal form wrapper component
81
78
  *
82
79
  * Wraps forms with:
83
- * - Material Design 3 Surface component
80
+ * - Pure React Native surface
84
81
  * - Universal keyboard handling (no platform checks)
85
82
  * - ScrollView for content overflow
86
83
  * - Safe area insets (bottom tabs, notch)
@@ -93,7 +90,7 @@ export const FormContainer: React.FC<FormContainerProps> = ({
93
90
  contentContainerStyle,
94
91
  showsVerticalScrollIndicator = false,
95
92
  testID,
96
- disableElevation = false,
93
+ showBorder = true,
97
94
  }) => {
98
95
  const tokens = useAppDesignTokens();
99
96
  const insets = useSafeAreaInsets();
@@ -111,6 +108,9 @@ export const FormContainer: React.FC<FormContainerProps> = ({
111
108
  surface: {
112
109
  flex: 1,
113
110
  backgroundColor: tokens.colors.surface,
111
+ borderWidth: showBorder ? 1 : 0,
112
+ borderColor: tokens.colors.border,
113
+ borderRadius: tokens.borders.radius.md,
114
114
  },
115
115
  scrollView: {
116
116
  flex: 1,
@@ -135,12 +135,15 @@ export const FormContainer: React.FC<FormContainerProps> = ({
135
135
  [
136
136
  tokens.colors.backgroundPrimary,
137
137
  tokens.colors.surface,
138
+ tokens.colors.border,
139
+ tokens.borders.radius.md,
138
140
  tokens.spacing.lg,
139
141
  tokens.spacing.xl,
140
142
  formBottomPadding,
141
143
  formContentWidth,
142
144
  formElementSpacing,
143
145
  insets.bottom,
146
+ showBorder,
144
147
  ]
145
148
  );
146
149
 
@@ -160,10 +163,7 @@ export const FormContainer: React.FC<FormContainerProps> = ({
160
163
 
161
164
  return (
162
165
  <View style={[styles.container, containerStyle]} testID={testID}>
163
- <Surface
164
- style={styles.surface}
165
- elevation={disableElevation ? 0 : 1}
166
- >
166
+ <View style={styles.surface}>
167
167
  <ScrollView
168
168
  style={styles.scrollView}
169
169
  contentContainerStyle={[styles.contentContainer, contentContainerStyle]}
@@ -174,7 +174,7 @@ export const FormContainer: React.FC<FormContainerProps> = ({
174
174
  >
175
175
  {childrenWithSpacing}
176
176
  </ScrollView>
177
- </Surface>
177
+ </View>
178
178
  </View>
179
179
  );
180
180
  };
@@ -155,6 +155,7 @@ export const lightColors = {
155
155
  borderLight: '#F1F5F9',
156
156
  borderMedium: '#CBD5E1',
157
157
  borderFocus: '#3B82F6',
158
+ borderDisabled: '#F1F5F9',
158
159
 
159
160
  // =============================================================================
160
161
  // COMPONENT-SPECIFIC COLORS
@@ -327,6 +328,7 @@ export const darkColors = {
327
328
  borderLight: '#475569', // Slate 600 - Light border
328
329
  borderMedium: '#64748B', // Slate 500 - Medium border
329
330
  borderFocus: '#60A5FA', // Blue 400 - Focus border (lighter)
331
+ borderDisabled: '#475569', // Slate 600 - Disabled border
330
332
 
331
333
  // =============================================================================
332
334
  // COMPONENT-SPECIFIC COLORS (dark mode specific)