@idealyst/components 1.2.42 → 1.2.44

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/components",
3
- "version": "1.2.42",
3
+ "version": "1.2.44",
4
4
  "description": "Shared component library for React and React Native",
5
5
  "documentation": "https://github.com/IdealystIO/idealyst-framework/tree/main/packages/components#readme",
6
6
  "readme": "README.md",
@@ -56,7 +56,7 @@
56
56
  "publish:npm": "npm publish"
57
57
  },
58
58
  "peerDependencies": {
59
- "@idealyst/theme": "^1.2.42",
59
+ "@idealyst/theme": "^1.2.44",
60
60
  "@mdi/js": ">=7.0.0",
61
61
  "@mdi/react": ">=1.0.0",
62
62
  "@react-native-vector-icons/common": ">=12.0.0",
@@ -107,7 +107,7 @@
107
107
  },
108
108
  "devDependencies": {
109
109
  "@idealyst/blur": "^1.2.40",
110
- "@idealyst/theme": "^1.2.42",
110
+ "@idealyst/theme": "^1.2.44",
111
111
  "@idealyst/tooling": "^1.2.30",
112
112
  "@mdi/react": "^1.6.1",
113
113
  "@types/react": "^19.1.0",
@@ -11,6 +11,7 @@ const Avatar = forwardRef<IdealystElement, AvatarProps>(({
11
11
  fallback,
12
12
  size = 'md',
13
13
  shape = 'circle',
14
+ color,
14
15
  style,
15
16
  testID,
16
17
  id,
@@ -42,8 +43,8 @@ const Avatar = forwardRef<IdealystElement, AvatarProps>(({
42
43
  setHasError(true);
43
44
  };
44
45
 
45
- const avatarStyle = (avatarStyles.avatar as any)({});
46
- const fallbackStyle = (avatarStyles.fallback as any)({});
46
+ const avatarStyle = (avatarStyles.avatar as any)({ color });
47
+ const fallbackStyle = (avatarStyles.fallback as any)({ color });
47
48
 
48
49
  return (
49
50
  <View ref={ref as any} nativeID={id} style={[avatarStyle, style]} testID={testID} {...nativeA11yProps}>
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { StyleSheet } from 'react-native-unistyles';
5
5
  import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
6
- import type { Theme as BaseTheme, Size } from '@idealyst/theme';
6
+ import type { Theme as BaseTheme, Size, Color } from '@idealyst/theme';
7
7
 
8
8
  // Required: Unistyles must see StyleSheet usage in original source to process this file
9
9
  void StyleSheet;
@@ -16,38 +16,71 @@ type AvatarShape = 'circle' | 'square';
16
16
  export type AvatarDynamicProps = {
17
17
  size?: Size;
18
18
  shape?: AvatarShape;
19
+ color?: Color;
19
20
  };
20
21
 
22
+ /**
23
+ * Resolve a color string to an actual color value from the theme.
24
+ * Supports formats: 'blue', 'blue.500', etc.
25
+ */
26
+ function resolveColor(theme: BaseTheme, color?: Color): string | undefined {
27
+ if (!color) return undefined;
28
+
29
+ const pallet = theme.colors.pallet;
30
+ if (!pallet) return undefined;
31
+
32
+ // Check if it's a pallet.shade format (e.g., 'blue.500')
33
+ if (color.includes('.')) {
34
+ const [palletName, shade] = color.split('.') as [string, string];
35
+ const palletColors = pallet[palletName as keyof typeof pallet];
36
+ if (palletColors && typeof palletColors === 'object') {
37
+ return (palletColors as Record<string, string>)[shade];
38
+ }
39
+ } else {
40
+ // Just a pallet name (e.g., 'blue') - use the 500 shade as default
41
+ const palletColors = pallet[color as keyof typeof pallet];
42
+ if (palletColors && typeof palletColors === 'object') {
43
+ return (palletColors as Record<string, string>)['500'];
44
+ }
45
+ }
46
+
47
+ return undefined;
48
+ }
49
+
21
50
  /**
22
51
  * Avatar styles with size and shape variants.
23
52
  */
24
53
  export const avatarStyles = defineStyle('Avatar', (theme: Theme) => ({
25
- avatar: (_props: AvatarDynamicProps) => ({
26
- display: 'flex' as const,
27
- alignItems: 'center' as const,
28
- justifyContent: 'center' as const,
29
- backgroundColor: theme.colors.surface.secondary,
30
- overflow: 'hidden' as const,
31
- variants: {
32
- // $iterator expands for each avatar size
33
- size: {
34
- width: theme.sizes.$avatar.width,
35
- height: theme.sizes.$avatar.height,
36
- },
37
- shape: {
38
- circle: { borderRadius: 9999 },
39
- square: { borderRadius: 8 },
54
+ avatar: ({ color }: AvatarDynamicProps) => {
55
+ const resolvedColor = resolveColor(theme as unknown as BaseTheme, color);
56
+ return {
57
+ display: 'flex' as const,
58
+ alignItems: 'center' as const,
59
+ justifyContent: 'center' as const,
60
+ backgroundColor: resolvedColor ?? theme.colors.surface.secondary,
61
+ overflow: 'hidden' as const,
62
+ variants: {
63
+ // $iterator expands for each avatar size
64
+ size: {
65
+ width: theme.sizes.$avatar.width,
66
+ height: theme.sizes.$avatar.height,
67
+ },
68
+ shape: {
69
+ circle: { borderRadius: 9999 },
70
+ square: { borderRadius: 8 },
71
+ },
40
72
  },
41
- },
42
- }),
73
+ };
74
+ },
43
75
 
44
76
  image: (_props: AvatarDynamicProps) => ({
45
77
  width: '100%' as const,
46
78
  height: '100%' as const,
47
79
  }),
48
80
 
49
- fallback: (_props: AvatarDynamicProps) => ({
50
- color: theme.colors.text.primary,
81
+ fallback: ({ color }: AvatarDynamicProps) => ({
82
+ // Use white text for colored backgrounds, primary text otherwise
83
+ color: color ? '#ffffff' : theme.colors.text.primary,
51
84
  fontWeight: '600' as const,
52
85
  variants: {
53
86
  size: {
@@ -16,6 +16,7 @@ const Avatar = forwardRef<IdealystElement, AvatarProps>(({
16
16
  fallback,
17
17
  size = 'md',
18
18
  shape = 'circle',
19
+ color,
19
20
  style,
20
21
  testID,
21
22
  id,
@@ -43,11 +44,11 @@ const Avatar = forwardRef<IdealystElement, AvatarProps>(({
43
44
  shape,
44
45
  });
45
46
 
46
- const avatarStyleArray = [(avatarStyles.avatar as any)({}), style];
47
+ const avatarStyleArray = [(avatarStyles.avatar as any)({ color }), style];
47
48
  const avatarProps = getWebProps(avatarStyleArray);
48
49
 
49
50
  // Generate fallback text styles with proper theming and size
50
- const fallbackStyleArray = [(avatarStyles.fallback as any)({})];
51
+ const fallbackStyleArray = [(avatarStyles.fallback as any)({ color })];
51
52
  const fallbackProps = getWebProps(fallbackStyleArray);
52
53
 
53
54
  const handleImageError = () => {
@@ -26,6 +26,7 @@ const Card = forwardRef<IdealystElement, CardProps>(({
26
26
  margin,
27
27
  marginVertical,
28
28
  marginHorizontal,
29
+ background,
29
30
  style,
30
31
  testID,
31
32
  id,
@@ -71,6 +72,7 @@ const Card = forwardRef<IdealystElement, CardProps>(({
71
72
  radius,
72
73
  clickable,
73
74
  disabled,
75
+ background,
74
76
  gap,
75
77
  padding,
76
78
  paddingVertical,
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { StyleSheet } from 'react-native-unistyles';
5
5
  import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
6
- import type { Theme as BaseTheme, Intent, Radius } from '@idealyst/theme';
6
+ import type { Theme as BaseTheme, Intent, Radius, Surface } from '@idealyst/theme';
7
7
  import { ViewStyleSize } from '../utils/viewStyleProps';
8
8
 
9
9
  // Required: Unistyles must see StyleSheet usage in original source to process this file
@@ -14,6 +14,7 @@ type Theme = ThemeStyleWrapper<BaseTheme>;
14
14
 
15
15
  type CardType = 'outlined' | 'elevated' | 'filled';
16
16
  type CardIntent = Intent | 'info' | 'neutral';
17
+ type CardPadding = ViewStyleSize | 'none';
17
18
 
18
19
  export type CardVariants = {
19
20
  type: CardType;
@@ -21,13 +22,14 @@ export type CardVariants = {
21
22
  intent: CardIntent;
22
23
  clickable: boolean;
23
24
  disabled: boolean;
24
- gap: ViewStyleSize;
25
- padding: ViewStyleSize;
26
- paddingVertical: ViewStyleSize;
27
- paddingHorizontal: ViewStyleSize;
28
- margin: ViewStyleSize;
29
- marginVertical: ViewStyleSize;
30
- marginHorizontal: ViewStyleSize;
25
+ background: Surface;
26
+ gap: CardPadding;
27
+ padding: CardPadding;
28
+ paddingVertical: CardPadding;
29
+ paddingHorizontal: CardPadding;
30
+ margin: CardPadding;
31
+ marginVertical: CardPadding;
32
+ marginHorizontal: CardPadding;
31
33
  };
32
34
 
33
35
  export type CardDynamicProps = {
@@ -65,59 +67,98 @@ export const cardStyles = defineStyle('Card', (theme: Theme) => ({
65
67
  },
66
68
  },
67
69
  radius: {
68
- none: { borderRadius: 0 },
69
- xs: { borderRadius: 2 },
70
- sm: { borderRadius: 4 },
71
- md: { borderRadius: 8 },
72
- lg: { borderRadius: 12 },
73
- xl: { borderRadius: 16 },
74
- },
75
- clickable: {
76
- true: {
77
- _web: {
78
- cursor: 'pointer',
79
- transition: 'all 0.2s ease',
80
- _hover: {
81
- transform: 'translateY(-2px)',
82
- boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.06)',
83
- },
70
+ none: { borderRadius: 0 },
71
+ xs: { borderRadius: 2 },
72
+ sm: { borderRadius: 4 },
73
+ md: { borderRadius: 8 },
74
+ lg: { borderRadius: 12 },
75
+ xl: { borderRadius: 16 },
76
+ },
77
+ // $iterator expands for each surface color
78
+ background: {
79
+ backgroundColor: theme.colors.$surface,
80
+ },
81
+ clickable: {
82
+ true: {
83
+ _web: {
84
+ cursor: 'pointer',
85
+ transition: 'all 0.2s ease',
86
+ _hover: {
87
+ transform: 'translateY(-2px)',
88
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.06)',
84
89
  },
85
90
  },
86
- false: {
87
- _web: { cursor: 'default' },
88
- },
89
- },
90
- disabled: {
91
- true: { opacity: 0.6, _web: { cursor: 'not-allowed' } },
92
- false: { opacity: 1 },
93
- },
94
- // $iterator expands for each view size
95
- gap: {
96
- gap: theme.sizes.$view.spacing,
97
- },
98
- padding: {
99
- padding: theme.sizes.$view.padding,
100
- },
101
- paddingVertical: {
102
- paddingVertical: theme.sizes.$view.padding,
103
- },
104
- paddingHorizontal: {
105
- paddingHorizontal: theme.sizes.$view.padding,
106
91
  },
107
- margin: {
108
- margin: theme.sizes.$view.padding,
109
- },
110
- marginVertical: {
111
- marginVertical: theme.sizes.$view.padding,
112
- },
113
- marginHorizontal: {
114
- marginHorizontal: theme.sizes.$view.padding,
92
+ false: {
93
+ _web: { cursor: 'default' },
115
94
  },
116
95
  },
117
- _web: {
118
- display: 'flex',
119
- flexDirection: 'column',
120
- boxSizing: 'border-box',
96
+ disabled: {
97
+ true: { opacity: 0.6, _web: { cursor: 'not-allowed' } },
98
+ false: { opacity: 1 },
99
+ },
100
+ // $iterator expands for each view size, plus 'none'
101
+ gap: {
102
+ none: { gap: 0 },
103
+ xs: { gap: theme.sizes.view.xs.spacing },
104
+ sm: { gap: theme.sizes.view.sm.spacing },
105
+ md: { gap: theme.sizes.view.md.spacing },
106
+ lg: { gap: theme.sizes.view.lg.spacing },
107
+ xl: { gap: theme.sizes.view.xl.spacing },
108
+ },
109
+ padding: {
110
+ none: { padding: 0 },
111
+ xs: { padding: theme.sizes.view.xs.padding },
112
+ sm: { padding: theme.sizes.view.sm.padding },
113
+ md: { padding: theme.sizes.view.md.padding },
114
+ lg: { padding: theme.sizes.view.lg.padding },
115
+ xl: { padding: theme.sizes.view.xl.padding },
116
+ },
117
+ paddingVertical: {
118
+ none: { paddingVertical: 0 },
119
+ xs: { paddingVertical: theme.sizes.view.xs.padding },
120
+ sm: { paddingVertical: theme.sizes.view.sm.padding },
121
+ md: { paddingVertical: theme.sizes.view.md.padding },
122
+ lg: { paddingVertical: theme.sizes.view.lg.padding },
123
+ xl: { paddingVertical: theme.sizes.view.xl.padding },
124
+ },
125
+ paddingHorizontal: {
126
+ none: { paddingHorizontal: 0 },
127
+ xs: { paddingHorizontal: theme.sizes.view.xs.padding },
128
+ sm: { paddingHorizontal: theme.sizes.view.sm.padding },
129
+ md: { paddingHorizontal: theme.sizes.view.md.padding },
130
+ lg: { paddingHorizontal: theme.sizes.view.lg.padding },
131
+ xl: { paddingHorizontal: theme.sizes.view.xl.padding },
132
+ },
133
+ margin: {
134
+ none: { margin: 0 },
135
+ xs: { margin: theme.sizes.view.xs.padding },
136
+ sm: { margin: theme.sizes.view.sm.padding },
137
+ md: { margin: theme.sizes.view.md.padding },
138
+ lg: { margin: theme.sizes.view.lg.padding },
139
+ xl: { margin: theme.sizes.view.xl.padding },
140
+ },
141
+ marginVertical: {
142
+ none: { marginVertical: 0 },
143
+ xs: { marginVertical: theme.sizes.view.xs.padding },
144
+ sm: { marginVertical: theme.sizes.view.sm.padding },
145
+ md: { marginVertical: theme.sizes.view.md.padding },
146
+ lg: { marginVertical: theme.sizes.view.lg.padding },
147
+ xl: { marginVertical: theme.sizes.view.xl.padding },
148
+ },
149
+ marginHorizontal: {
150
+ none: { marginHorizontal: 0 },
151
+ xs: { marginHorizontal: theme.sizes.view.xs.padding },
152
+ sm: { marginHorizontal: theme.sizes.view.sm.padding },
153
+ md: { marginHorizontal: theme.sizes.view.md.padding },
154
+ lg: { marginHorizontal: theme.sizes.view.lg.padding },
155
+ xl: { marginHorizontal: theme.sizes.view.xl.padding },
121
156
  },
157
+ },
158
+ _web: {
159
+ display: 'flex',
160
+ flexDirection: 'column',
161
+ boxSizing: 'border-box',
162
+ },
122
163
  }),
123
164
  }));
@@ -20,6 +20,7 @@ const Card = forwardRef<IdealystElement, CardProps>(({
20
20
  variant,
21
21
  radius = 'md',
22
22
  intent: _intent,
23
+ background,
23
24
  clickable = false,
24
25
  onPress,
25
26
  onClick,
@@ -93,6 +94,7 @@ const Card = forwardRef<IdealystElement, CardProps>(({
93
94
  radius,
94
95
  clickable,
95
96
  disabled,
97
+ background,
96
98
  gap,
97
99
  padding,
98
100
  paddingVertical,
package/src/Card/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Intent, Size } from '@idealyst/theme';
1
+ import { Intent, Size, Surface } from '@idealyst/theme';
2
2
  import type { ReactNode } from 'react';
3
3
  import type { StyleProp, ViewStyle } from 'react-native';
4
4
  import { ContainerStyleProps } from '../utils/viewStyleProps';
@@ -9,6 +9,7 @@ import type { LayoutChangeEvent } from '../hooks/useWebLayout';
9
9
  export type CardIntentVariant = Intent;
10
10
  export type CardType = 'default' | 'outlined' | 'elevated' | 'filled';
11
11
  export type CardRadiusVariant = 'none' | Size;
12
+ export type CardBackgroundVariant = Surface;
12
13
 
13
14
  /**
14
15
  * Container component for grouping related content with visual separation.
@@ -40,6 +41,11 @@ export interface CardProps extends ContainerStyleProps, InteractiveAccessibility
40
41
  */
41
42
  intent?: CardIntentVariant;
42
43
 
44
+ /**
45
+ * The background color from the surface palette (screen, primary, secondary, tertiary, inverse, etc.)
46
+ */
47
+ background?: CardBackgroundVariant;
48
+
43
49
  /**
44
50
  * Whether the card is clickable
45
51
  */
@@ -1,5 +1,5 @@
1
1
  import { forwardRef } from 'react';
2
- import { View as RNView, ScrollView as RNScrollView } from 'react-native';
2
+ import { View as RNView, ScrollView as RNScrollView, KeyboardAvoidingView, Platform } from 'react-native';
3
3
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
4
4
  import { ScreenProps } from './types';
5
5
  import { screenStyles } from './Screen.styles';
@@ -10,6 +10,7 @@ const Screen = forwardRef<IdealystElement, ScreenProps>(({
10
10
  background = 'screen',
11
11
  safeArea = true,
12
12
  scrollable = true,
13
+ avoidKeyboard = true,
13
14
  contentInset,
14
15
  onLayout,
15
16
  // Spacing variants from ContainerStyleProps
@@ -54,6 +55,13 @@ const Screen = forwardRef<IdealystElement, ScreenProps>(({
54
55
  paddingRight: insets.right,
55
56
  } : undefined;
56
57
 
58
+ // Wrapper component for keyboard avoidance
59
+ const KeyboardWrapper = avoidKeyboard ? KeyboardAvoidingView : RNView;
60
+ const keyboardWrapperProps = avoidKeyboard ? {
61
+ behavior: Platform.OS === 'ios' ? 'padding' as const : 'height' as const,
62
+ style: { flex: 1 },
63
+ } : { style: { flex: 1 } };
64
+
57
65
  if (scrollable) {
58
66
  // Content styles applied via View wrapper for Unistyles reactivity
59
67
  // (contentContainerStyle isn't reactive, only style prop is)
@@ -65,25 +73,30 @@ const Screen = forwardRef<IdealystElement, ScreenProps>(({
65
73
  } : safeAreaStyle;
66
74
 
67
75
  return (
68
- <RNScrollView
69
- ref={ref as any}
70
- nativeID={id}
71
- style={[screenStyle, style]}
72
- contentContainerStyle={{ flexGrow: 1 }}
73
- testID={testID}
74
- onLayout={onLayout}
75
- >
76
- <RNView style={[contentInsetStyle, { flex: 1 }]}>
77
- {children}
78
- </RNView>
79
- </RNScrollView>
76
+ <KeyboardWrapper {...keyboardWrapperProps}>
77
+ <RNScrollView
78
+ ref={ref as any}
79
+ nativeID={id}
80
+ style={[screenStyle, style]}
81
+ contentContainerStyle={{ flexGrow: 1 }}
82
+ testID={testID}
83
+ onLayout={onLayout}
84
+ keyboardShouldPersistTaps="handled"
85
+ >
86
+ <RNView style={[contentInsetStyle, { flex: 1 }]}>
87
+ {children}
88
+ </RNView>
89
+ </RNScrollView>
90
+ </KeyboardWrapper>
80
91
  );
81
92
  }
82
93
 
83
94
  return (
84
- <RNView ref={ref as any} nativeID={id} style={[screenStyle, safeAreaStyle, style]} testID={testID} onLayout={onLayout}>
85
- {children}
86
- </RNView>
95
+ <KeyboardWrapper {...keyboardWrapperProps}>
96
+ <RNView ref={ref as any} nativeID={id} style={[screenStyle, safeAreaStyle, style]} testID={testID} onLayout={onLayout}>
97
+ {children}
98
+ </RNView>
99
+ </KeyboardWrapper>
87
100
  );
88
101
  });
89
102
 
@@ -51,6 +51,14 @@ export interface ScreenProps extends ContainerStyleProps {
51
51
  */
52
52
  scrollable?: boolean;
53
53
 
54
+ /**
55
+ * Whether to avoid the keyboard on native platforms (iOS/Android).
56
+ * When enabled, content will shift up when the keyboard appears.
57
+ * @default true
58
+ * @platform native
59
+ */
60
+ avoidKeyboard?: boolean;
61
+
54
62
  /**
55
63
  * Called when the layout of the screen changes.
56
64
  * Provides the new width, height, x, and y coordinates.
@@ -104,6 +104,12 @@ export const selectStyles = defineStyle('Select', (theme: Theme) => ({
104
104
  triggerText: (_props: SelectDynamicProps) => ({
105
105
  color: theme.colors.text.primary,
106
106
  flex: 1,
107
+ textAlign: 'left' as const,
108
+ overflow: 'hidden' as const,
109
+ _web: {
110
+ whiteSpace: 'nowrap',
111
+ textOverflow: 'ellipsis',
112
+ },
107
113
  variants: {
108
114
  size: {
109
115
  fontSize: theme.sizes.$select.fontSize,
@@ -113,6 +119,12 @@ export const selectStyles = defineStyle('Select', (theme: Theme) => ({
113
119
 
114
120
  placeholder: (_props: SelectDynamicProps) => ({
115
121
  color: theme.colors.text.secondary,
122
+ textAlign: 'left' as const,
123
+ overflow: 'hidden' as const,
124
+ _web: {
125
+ whiteSpace: 'nowrap',
126
+ textOverflow: 'ellipsis',
127
+ },
116
128
  variants: {
117
129
  size: {
118
130
  fontSize: theme.sizes.$select.fontSize,
@@ -37,13 +37,10 @@ function _createIntentVariants(theme: Theme) {
37
37
  * TextArea styles with static styles and variants.
38
38
  */
39
39
  export const textAreaStyles = defineStyle('TextArea', (theme: Theme) => ({
40
- container: {
40
+ container: (_props: TextAreaVariants) => ({
41
41
  display: 'flex' as const,
42
42
  flexDirection: 'column' as const,
43
43
  gap: 4,
44
- borderWidth: 1,
45
- borderColor: theme.colors.border.primary,
46
- borderRadius: theme.radii.md,
47
44
  variants: {
48
45
  margin: {
49
46
  margin: theme.sizes.$view.padding,
@@ -55,9 +52,9 @@ export const textAreaStyles = defineStyle('TextArea', (theme: Theme) => ({
55
52
  marginHorizontal: theme.sizes.$view.padding,
56
53
  },
57
54
  },
58
- },
55
+ }),
59
56
 
60
- label: {
57
+ label: (_props: TextAreaVariants) => ({
61
58
  fontSize: 14,
62
59
  fontWeight: '500' as const,
63
60
  color: theme.colors.text.primary,
@@ -67,21 +64,31 @@ export const textAreaStyles = defineStyle('TextArea', (theme: Theme) => ({
67
64
  false: { opacity: 1 },
68
65
  },
69
66
  },
70
- },
67
+ }),
71
68
 
72
- textareaContainer: {
69
+ textareaContainer: (_props: TextAreaVariants) => ({
73
70
  position: 'relative' as const,
71
+ borderWidth: 1,
72
+ borderColor: theme.colors.border.primary,
73
+ borderRadius: theme.radii.md,
74
+ backgroundColor: theme.colors.surface.primary,
75
+ overflow: 'hidden' as const,
74
76
  variants: {
75
77
  disabled: {
76
78
  true: { opacity: 0.8 },
77
79
  false: { opacity: 1 },
78
80
  },
79
81
  },
80
- },
82
+ _web: {
83
+ border: `1px solid ${theme.colors.border.primary}`,
84
+ },
85
+ }),
81
86
 
82
- textarea: {
87
+ textarea: (_props: TextAreaVariants) => ({
83
88
  width: '100%',
84
89
  color: theme.colors.text.primary,
90
+ backgroundColor: 'transparent',
91
+ borderWidth: 0,
85
92
  variants: {
86
93
  size: {
87
94
  fontSize: theme.sizes.$textarea.fontSize,
@@ -107,13 +114,15 @@ export const textAreaStyles = defineStyle('TextArea', (theme: Theme) => ({
107
114
  _web: {
108
115
  fontFamily: 'inherit',
109
116
  outline: 'none',
117
+ border: 'none',
110
118
  transition: 'border-color 0.2s ease, box-shadow 0.2s ease',
111
119
  boxSizing: 'border-box',
112
120
  overflowY: 'hidden',
121
+ resize: 'none',
113
122
  },
114
- },
123
+ }),
115
124
 
116
- helperText: {
125
+ helperText: (_props: TextAreaVariants) => ({
117
126
  fontSize: 12,
118
127
  variants: {
119
128
  hasError: {
@@ -121,17 +130,17 @@ export const textAreaStyles = defineStyle('TextArea', (theme: Theme) => ({
121
130
  false: { color: theme.colors.text.secondary },
122
131
  },
123
132
  },
124
- },
133
+ }),
125
134
 
126
- footer: {
135
+ footer: (_props: TextAreaVariants) => ({
127
136
  display: 'flex' as const,
128
137
  flexDirection: 'row' as const,
129
138
  justifyContent: 'space-between' as const,
130
139
  alignItems: 'center' as const,
131
140
  gap: 4,
132
- },
141
+ }),
133
142
 
134
- characterCount: {
143
+ characterCount: (_props: TextAreaVariants) => ({
135
144
  fontSize: 12,
136
145
  color: theme.colors.text.secondary,
137
146
  variants: {
@@ -153,5 +162,5 @@ export const textAreaStyles = defineStyle('TextArea', (theme: Theme) => ({
153
162
  },
154
163
  },
155
164
  ] as CompoundVariants<keyof TextAreaVariants>,
156
- },
165
+ }),
157
166
  }));