@tcbs/react-native-mazic-ui 0.1.1 → 0.1.2

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
@@ -13,13 +13,13 @@
13
13
  "react-native-vector-icons"
14
14
  ],
15
15
  "author": "TechCraft By Subrata <subraatakumar@gmail.com>",
16
- "version": "0.1.1",
17
- "publishConfig": {
16
+ "version": "0.1.2",
17
+ "publishConfig": {
18
18
  "access": "public"
19
19
  },
20
20
  "main": "lib/commonjs/index.js",
21
21
  "module": "lib/module/index.js",
22
- "types": "lib/types/index.d.ts",
22
+ "types": "lib/index.d.ts",
23
23
  "react-native": "src/index.ts",
24
24
  "files": [
25
25
  "lib/",
@@ -1,4 +1,5 @@
1
1
  import React, { useMemo } from 'react';
2
+ import { Appearance } from 'react-native';
2
3
  import { TouchableOpacity, Text, View , StyleProp, ViewStyle, TextStyle } from 'react-native';
3
4
  import AntDesign from 'react-native-vector-icons/AntDesign';
4
5
  import Feather from 'react-native-vector-icons/Feather';
@@ -33,7 +34,8 @@ const FONT_SIZES: Record<ButtonSize, number> = {
33
34
  [BUTTON_SIZE.SMALL]: 14,
34
35
  };
35
36
 
36
- const BORDER_RADIUSES: Record<ButtonSize, number> = {
37
+ // Support for BORDER_RADIUS.NONE and BORDER_RADIUS.FULL (50%)
38
+ const BORDER_RADIUSES: Record<ButtonSize, number | string> = {
37
39
  [BUTTON_SIZE.LARGE]: BORDER_RADIUS.MEDIUM,
38
40
  [BUTTON_SIZE.MEDIUM]: BORDER_RADIUS.SMALL,
39
41
  [BUTTON_SIZE.SMALL]: BORDER_RADIUS.SMALL,
@@ -72,25 +74,35 @@ export const TcbsButton: React.FC<TcbsButtonProps> = ({
72
74
  accessibilityHint,
73
75
  accessibilityRole = 'button',
74
76
  accessibilityState,
75
- themeColor,
76
- screenBgColor,
77
77
  }) => {
78
- // Use theme from store if not provided as prop
79
- const { colors, mode } = useTcbsColorStore();
80
- const effectiveThemeColor = themeColor ?? colors[mode];
78
+ // Use themeColors from store if not provided as prop
79
+ const { themeColors, tcbsTheme } = useTcbsColorStore();
80
+ const effectiveThemeColor = themeColors;
81
81
  // Normalize colors: if only one color is set, use it for all
82
82
  const normalizedColors = {
83
83
  btnColor: effectiveThemeColor?.btnColor ?? effectiveThemeColor?.themeColor ?? '#007AFF',
84
84
  btnBorderColor: effectiveThemeColor?.btnBorderColor ?? effectiveThemeColor?.btnColor ?? '#007AFF',
85
85
  btnIconColor: effectiveThemeColor?.btnIconColor,
86
- btnTextColor: effectiveThemeColor?.btnTextColor ?? effectiveThemeColor?.btnTxtColor ,
86
+ btnTextColor: effectiveThemeColor?.btnTextColor ?? effectiveThemeColor?.btnTxtColor,
87
87
  themeColor: effectiveThemeColor?.themeColor ?? effectiveThemeColor?.btnColor ?? '#007AFF',
88
88
  };
89
89
 
90
90
  const buttonStyle = useMemo<StyleProp<ViewStyle>>(() => {
91
+ const height = HEIGHTS[size];
92
+ let computedBorderRadius: number | string;
93
+ if (borderRadius === BORDER_RADIUS.NONE) {
94
+ computedBorderRadius = 0;
95
+ } else if (borderRadius === BORDER_RADIUS.FULL) {
96
+ computedBorderRadius = height / 2;
97
+ } else if (borderRadius !== undefined) {
98
+ computedBorderRadius = borderRadius;
99
+ } else {
100
+ computedBorderRadius = BORDER_RADIUSES[size];
101
+ }
102
+
91
103
  const baseStyle: ViewStyle = {
92
- height: HEIGHTS[size],
93
- borderRadius: borderRadius ?? BORDER_RADIUSES[size],
104
+ height,
105
+ borderRadius: computedBorderRadius,
94
106
  alignItems: 'center',
95
107
  justifyContent: 'center',
96
108
  opacity: disabled ? 0.6 : 1,
@@ -129,10 +141,20 @@ export const TcbsButton: React.FC<TcbsButtonProps> = ({
129
141
  }, [size, variant, normalizedColors, style, disabled, borderRadius]);
130
142
 
131
143
  const themedTextStyle = useMemo<TextStyle>(() => {
132
- const baseTextColor =
133
- variant === BUTTON_VARIANT.PRIMARY
144
+ let baseTextColor;
145
+ if (variant === BUTTON_VARIANT.PRIMARY) {
146
+ baseTextColor = normalizedColors.btnTextColor || '#FFFFFF';
147
+ } else if (variant === BUTTON_VARIANT.NO_BORDER) {
148
+ let colorScheme = tcbsTheme;
149
+ if (tcbsTheme === 'system') {
150
+ colorScheme = Appearance.getColorScheme() || 'light';
151
+ }
152
+ baseTextColor = colorScheme === 'dark'
134
153
  ? normalizedColors.btnTextColor || '#FFFFFF'
135
- : variant === BUTTON_VARIANT.NO_BORDER && mode === 'dark' ? normalizedColors.btnTextColor || "#FFFFFF" : normalizedColors?.btnColor || normalizedColors?.themeColor || "#FFFFFF";
154
+ : normalizedColors?.btnColor || '#007AFF';
155
+ } else {
156
+ baseTextColor = normalizedColors?.btnColor || '#FFFFFF';
157
+ }
136
158
 
137
159
  return {
138
160
  color: baseTextColor,
@@ -140,13 +162,13 @@ export const TcbsButton: React.FC<TcbsButtonProps> = ({
140
162
  fontWeight: '700',
141
163
  ...(textStyle as TextStyle),
142
164
  };
143
- }, [size, variant, normalizedColors, textStyle]);
165
+ }, [size, variant, normalizedColors, textStyle, tcbsTheme]);
144
166
 
145
167
  const renderIcon = (IconComponent: IconComponentType) => (
146
168
  <IconComponent
147
169
  name={iconName!}
148
170
  size={iconSize || FONT_SIZES[size] * 2}
149
- color={iconColor || normalizedColors.btnIconColor || themedTextStyle.color}
171
+ color={iconColor || themedTextStyle.color}
150
172
  style={
151
173
  iconPosition === 'top'
152
174
  ? { marginBottom: 2 }
@@ -230,6 +252,5 @@ export const TcbsButton: React.FC<TcbsButtonProps> = ({
230
252
  );
231
253
  };
232
254
 
233
- // Export constants for use in consuming applications
234
255
  export { BUTTON_SIZE, BUTTON_VARIANT, BORDER_RADIUS };
235
256
  export type { ButtonSize, ButtonVariant, IconGroupType, IconPosition };
@@ -17,12 +17,15 @@ export const BUTTON_VARIANT = {
17
17
  PRIMARY: 'primary',
18
18
  SECONDARY: 'secondary',
19
19
  NO_BORDER: 'no_border',
20
+
20
21
  } as const;
21
22
 
22
23
  export const BORDER_RADIUS = {
23
24
  SMALL: 8,
24
25
  MEDIUM: 12,
25
26
  LARGE: 16,
27
+ NONE: 0,
28
+ FULL: '50%',
26
29
  } as const;
27
30
 
28
31
  export type ButtonSize = (typeof BUTTON_SIZE)[keyof typeof BUTTON_SIZE];
@@ -0,0 +1,123 @@
1
+ import React from 'react';
2
+ import { Modal, Pressable, TouchableOpacity, View, Text, StyleSheet } from 'react-native';
3
+ import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
4
+ import Ionicons from 'react-native-vector-icons/Ionicons';
5
+ import Entypo from 'react-native-vector-icons/Entypo';
6
+ import { useTcbsColorStore } from '../store/themeStore';
7
+ import { BUTTON_VARIANT, TcbsButton } from './TcbsButton';
8
+
9
+ export interface ThemeModalProps {
10
+ visible: boolean;
11
+ onClose: () => void;
12
+ }
13
+
14
+ export const ThemeModal: React.FC<ThemeModalProps> = ({ visible, onClose }) => {
15
+ const { tcbsTheme, setTcbsTheme, themeColors } = useTcbsColorStore();
16
+ // You can customize these colors or get them from your theme
17
+ const colors = {
18
+ menuCardBkgColor: themeColors.screenBgColor || '#fff',
19
+ textDark: themeColors.btnTextColor || '#222',
20
+ textGray: '#888',
21
+ };
22
+
23
+ return (
24
+ <Modal
25
+ transparent={true}
26
+ animationType="fade"
27
+ visible={visible}
28
+ onRequestClose={onClose}
29
+ >
30
+ <Pressable style={styles.modalOverlay} onPress={onClose}>
31
+ <Pressable style={[styles.modalCard, { backgroundColor: colors.menuCardBkgColor }]}
32
+ onPress={() => {}} // Prevent closing when pressing inside card
33
+ >
34
+ <View style={styles.modalClose}>
35
+ <TcbsButton
36
+ onPress={onClose}
37
+ iconName="close"
38
+ iconPosition="left"
39
+ variant={BUTTON_VARIANT.NO_BORDER}
40
+ iconSize={22}
41
+ accessibilityLabel="Close"
42
+ style={{ padding: 8, marginRight: 0, minWidth: 0, minHeight: 0, alignSelf: 'flex-end' }}
43
+ />
44
+ </View>
45
+ <Text style={[styles.modalTitle, { color: colors.textDark }]}>Theme</Text>
46
+ <Text style={[styles.modalSubtitle, { color: colors.textGray }]}>Choose how the app looks on this device.</Text>
47
+ <View style={{ marginTop: 18 }}>
48
+ <TcbsButton
49
+ title="Light"
50
+ onPress={() => setTcbsTheme('light')}
51
+ style={{ marginBottom: 8 }}
52
+ variant={tcbsTheme === 'light' ? 'primary' : 'secondary'}
53
+ iconGroup="Ionicons"
54
+ iconName="sunny"
55
+ iconPosition="left"
56
+ textStyle={{ flex: 1, textAlign: 'center' }}
57
+ />
58
+ <TcbsButton
59
+ title="Dark"
60
+ onPress={() => setTcbsTheme('dark')}
61
+ style={{ marginBottom: 8 }}
62
+ variant={tcbsTheme === 'dark' ? 'primary' : 'secondary'}
63
+ iconGroup="Ionicons"
64
+ iconName="moon"
65
+ iconPosition="left"
66
+ textStyle={{ flex: 1, textAlign: 'center' }}
67
+ />
68
+ <TcbsButton
69
+ title="System (default)"
70
+ onPress={() => setTcbsTheme('system')}
71
+ variant={tcbsTheme === 'system' ? 'primary' : 'secondary'}
72
+ iconGroup="Ionicons"
73
+ iconName="settings"
74
+ iconPosition="left"
75
+ textStyle={{ flex: 1, textAlign: 'center' }}
76
+ />
77
+ </View>
78
+ </Pressable>
79
+ </Pressable>
80
+ </Modal>
81
+ );
82
+ };
83
+
84
+ const styles = StyleSheet.create({
85
+ modalOverlay: {
86
+ flex: 1,
87
+ backgroundColor: 'rgba(0,0,0,0.3)',
88
+ justifyContent: 'center',
89
+ alignItems: 'center',
90
+ },
91
+ modalCard: {
92
+ minWidth: 300,
93
+ borderRadius: 16,
94
+ padding: 24,
95
+ alignItems: 'stretch',
96
+ shadowColor: '#000',
97
+ shadowOpacity: 0.15,
98
+ shadowRadius: 12,
99
+ shadowOffset: { width: 0, height: 4 },
100
+ elevation: 4,
101
+ },
102
+ modalClose: {
103
+ position: 'absolute',
104
+ top: 8,
105
+ right: 8,
106
+ zIndex: 2,
107
+ },
108
+ modalTitle: {
109
+ fontSize: 20,
110
+ fontWeight: '700',
111
+ marginTop: 8,
112
+ marginBottom: 2,
113
+ textAlign: 'center',
114
+ },
115
+ modalSubtitle: {
116
+ fontSize: 14,
117
+ fontWeight: '400',
118
+ marginBottom: 8,
119
+ textAlign: 'center',
120
+ },
121
+ });
122
+
123
+ export default ThemeModal;
package/src/index.ts CHANGED
@@ -12,3 +12,4 @@ export {
12
12
  IconComponentType,
13
13
  } from './components/TcbsButton.types';
14
14
  export { useTcbsColorStore } from './store/themeStore';
15
+ export {ThemeModal} from './components/ThemeModal';
@@ -1,4 +1,13 @@
1
1
  import { create } from 'zustand';
2
+ import { Appearance } from 'react-native';
3
+ import { MMKV } from 'react-native-mmkv';
4
+
5
+ // MMKV instance for theme persistence
6
+ const storage = new MMKV();
7
+ const THEME_KEY = 'tcbsTheme';
8
+
9
+ // Store the listener subscription so we can remove it
10
+ let appearanceListener: { remove: () => void } | null = null;
2
11
 
3
12
  export type ThemeColor = {
4
13
  btnColor: string;
@@ -6,10 +15,30 @@ export type ThemeColor = {
6
15
  btnIconColor?: string;
7
16
  themeColor: string;
8
17
  btnTextColor: string;
18
+ tabBarIconActiveColor?: string;
19
+ tabBarIconInactiveColor?: string;
20
+ primaryColor?: string;
21
+ secondaryColor?: string;
22
+ tertiaryColor?: string;
9
23
  screenBgColor?: string;
24
+ modalBgColor?: string;
25
+ modalHeaderBgColor?: string;
26
+ modalCardBgColor?: string;
27
+ textPrimary?: string;
28
+ textSecondary?: string;
29
+ borderColor?: string;
30
+ dividerColor?: string;
31
+ inputBgColor?: string;
32
+ inputBorderColor?: string;
33
+ cardBgColor?: string;
34
+ cardBorderColor?: string;
35
+ accentColor?: string;
36
+ errorColor?: string;
37
+ successColor?: string;
38
+ warningColor?: string;
10
39
  };
11
40
 
12
- export type ThemeMode = 'light' | 'dark';
41
+ export type ThemeMode = 'light' | 'dark' | 'system';
13
42
 
14
43
  export type ThemeColors = {
15
44
  light: ThemeColor;
@@ -18,49 +47,277 @@ export type ThemeColors = {
18
47
 
19
48
  export interface ThemeStore {
20
49
  colors: ThemeColors;
21
- mode: ThemeMode;
50
+ tcbsTheme: ThemeMode;
51
+ themeColors: ThemeColor;
52
+ /**
53
+ * Returns the current theme as 'light' or 'dark' (never 'system').
54
+ * If tcbsTheme is 'system', resolves to the current system color scheme.
55
+ */
56
+ currentThemeMode: 'light' | 'dark';
22
57
  setTcbsColor: (colors: Partial<ThemeColor> & { light?: Partial<ThemeColor>; dark?: Partial<ThemeColor> }) => void;
23
58
  setTcbsTheme: (mode: ThemeMode) => void;
59
+ toggleTcbsTheme: () => void;
60
+ setMazicColor: (baseColor: string) => void;
24
61
  }
25
62
 
26
63
  const defaultColors: ThemeColors = {
27
64
  light: {
28
- // btnColor: '#007AFF',
29
- // btnBorderColor: '#007AFF',
30
- // btnIconColor: '#16a62bff',
31
- // themeColor: '#007AFF',
32
- // btnTextColor: '#FFFFFF',
65
+ // You can set initial defaults here if needed, or leave them empty
66
+ btnColor: '#007AFF',
67
+ btnBorderColor: '#007AFF',
68
+ btnIconColor: '#FFFFFF',
69
+ themeColor: '#007AFF',
70
+ btnTextColor: '#FFFFFF',
33
71
  },
34
72
  dark: {
35
- // btnColor: '#222222',
36
- // btnBorderColor: '#222222',
37
- // btnIconColor: '#FFFFFF',
38
- // themeColor: '#222222',
39
- // btnTextColor: '#FFFFFF',
73
+ btnColor: '#222222',
74
+ btnBorderColor: '#222222',
75
+ btnIconColor: '#FFFFFF',
76
+ themeColor: '#222222',
77
+ btnTextColor: '#FFFFFF',
40
78
  },
41
79
  };
42
80
 
43
- export const useTcbsColorStore = create<ThemeStore>((set: (fn: (state: ThemeStore) => Partial<ThemeStore>) => void) => ({
44
- colors: defaultColors,
45
- mode: 'light',
46
- setTcbsColor: (colors: Partial<ThemeColor> & { light?: Partial<ThemeColor>; dark?: Partial<ThemeColor> }) => {
47
- set((state: ThemeStore) => {
48
- let newColors = { ...state.colors };
49
- // If colors for both themes are provided
50
- if (colors.light || colors.dark) {
51
- if (colors.light) {
52
- newColors.light = { ...newColors.light, ...colors.light };
53
- }
54
- if (colors.dark) {
55
- newColors.dark = { ...newColors.dark, ...colors.dark };
81
+ // Try to load persisted theme, fallback to 'light'. If 'system', use current system color scheme.
82
+ const defaultTheme = storage.getString(THEME_KEY) as ThemeMode | undefined || 'light';
83
+
84
+ // Helper functions for color manipulation
85
+ const hexToRgb = (hex: string): { r: number; g: number; b: number } | null => {
86
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
87
+ return result ? {
88
+ r: parseInt(result[1], 16),
89
+ g: parseInt(result[2], 16),
90
+ b: parseInt(result[3], 16)
91
+ } : null;
92
+ };
93
+
94
+ const rgbToHex = (r: number, g: number, b: number): string => {
95
+ return '#' + [r, g, b].map(x => {
96
+ const hex = Math.round(x).toString(16);
97
+ return hex.length === 1 ? '0' + hex : hex;
98
+ }).join('');
99
+ };
100
+
101
+ const adjustBrightness = (hex: string, percent: number): string => {
102
+ const rgb = hexToRgb(hex);
103
+ if (!rgb) return hex;
104
+
105
+ const adjust = (value: number) => Math.max(0, Math.min(255, value + (255 * percent / 100)));
106
+ return rgbToHex(adjust(rgb.r), adjust(rgb.g), adjust(rgb.b));
107
+ };
108
+
109
+ // NEW HELPER: Determines if a color is dark enough to require white text
110
+ const isColorDark = (hex: string): boolean => {
111
+ const rgb = hexToRgb(hex);
112
+ if (!rgb) return true; // Default to white text if color is invalid
113
+ // Calculate Luma (YIQ method is good for perceived brightness)
114
+ // Formula: (R * 299 + G * 587 + B * 114) / 1000
115
+ const luma = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
116
+ // Threshold 0.45 is slightly lower than standard 0.5, favoring white text
117
+ return luma < 0.45;
118
+ };
119
+
120
+ const addAlpha = (hex: string, alpha: number): string => {
121
+ const alphaHex = Math.round(alpha * 255).toString(16).padStart(2, '0');
122
+ return hex + alphaHex;
123
+ };
124
+
125
+
126
+ export const useTcbsColorStore = create<ThemeStore>((set: (fn: (state: ThemeStore) => Partial<ThemeStore>) => void, get) => {
127
+ // Helper to get the current theme color
128
+ const getThemeColors = (theme: ThemeMode, colors: ThemeColors): ThemeColor => {
129
+ if (theme === 'light' || theme === 'dark') {
130
+ return colors[theme];
131
+ } else {
132
+ // system: use Appearance API
133
+ const colorScheme = Appearance.getColorScheme?.() || 'light';
134
+ return colors[colorScheme as 'light' | 'dark'] || colors.light;
135
+ }
136
+ };
137
+
138
+ // Initial state
139
+ const initialColors = defaultColors;
140
+ const initialTheme = defaultTheme;
141
+ const initialThemeColors = getThemeColors(initialTheme, initialColors);
142
+
143
+ // Listen to system theme changes if needed
144
+ if (initialTheme === 'system' && !appearanceListener) {
145
+ appearanceListener = Appearance.addChangeListener?.(({ colorScheme }) => {
146
+ set((state: ThemeStore) => ({
147
+ themeColors: state.colors[(colorScheme as 'light' | 'dark') || 'light']
148
+ }));
149
+ });
150
+ }
151
+
152
+ return {
153
+ colors: initialColors,
154
+ tcbsTheme: initialTheme,
155
+ themeColors: initialThemeColors,
156
+ get currentThemeMode() {
157
+ const state = get();
158
+ if (state.tcbsTheme === 'light' || state.tcbsTheme === 'dark') {
159
+ return state.tcbsTheme;
160
+ }
161
+ // system: use Appearance API
162
+ const colorScheme = Appearance.getColorScheme?.() || 'light';
163
+ return (colorScheme === 'dark' ? 'dark' : 'light');
164
+ },
165
+ setTcbsColor: (colors: Partial<ThemeColor> & { light?: Partial<ThemeColor>; dark?: Partial<ThemeColor> }) => {
166
+ set((state: ThemeStore) => {
167
+ let newColors = { ...state.colors };
168
+ // If colors for both themes are provided
169
+ if (colors.light || colors.dark) {
170
+ if (colors.light) {
171
+ newColors.light = { ...newColors.light, ...colors.light };
172
+ }
173
+ if (colors.dark) {
174
+ newColors.dark = { ...newColors.dark, ...colors.dark };
175
+ }
176
+ } else {
177
+ // If only one set, update both themes
178
+ newColors.light = { ...newColors.light, ...colors };
179
+ newColors.dark = { ...newColors.dark, ...colors };
56
180
  }
57
- } else {
58
- // If only one set, update both themes
59
- newColors.light = { ...newColors.light, ...colors };
60
- newColors.dark = { ...newColors.dark, ...colors };
181
+ // Update themeColors as well
182
+ const themeColors = getThemeColors(state.tcbsTheme, newColors);
183
+ return { colors: newColors, themeColors };
184
+ });
185
+ },
186
+ setTcbsTheme: (newTheme: ThemeMode) => {
187
+ // Persist user selection
188
+ storage.set(THEME_KEY, newTheme);
189
+ // Remove previous listener if exists
190
+ if (appearanceListener) {
191
+ appearanceListener.remove();
192
+ appearanceListener = null;
61
193
  }
62
- return { colors: newColors };
63
- });
64
- },
65
- setTcbsTheme: (mode: ThemeMode) => set((state) => ({ ...state, mode })),
66
- }));
194
+ // If new theme is system, add listener
195
+ if (newTheme === 'system') {
196
+ appearanceListener = Appearance.addChangeListener?.(({ colorScheme }) => {
197
+ set((state: ThemeStore) => ({
198
+ themeColors: state.colors[(colorScheme as 'light' | 'dark') || 'light']
199
+ }));
200
+ });
201
+ }
202
+ // Update themeColors as well
203
+ set((state: ThemeStore) => ({
204
+ tcbsTheme: newTheme,
205
+ themeColors: getThemeColors(newTheme, state.colors)
206
+ }));
207
+ },
208
+ toggleTcbsTheme: () => {
209
+ set((state: ThemeStore) => {
210
+ const themes: ThemeMode[] = ['light', 'dark', 'system'];
211
+ const currentIdx = themes.indexOf(state.tcbsTheme);
212
+ const nextTheme = themes[(currentIdx + 1) % themes.length];
213
+ // Note: setTcbsTheme is called outside the state update, but here it's called internally
214
+ // which is a common pattern in zustand actions.
215
+ // @ts-ignore
216
+ state.setTcbsTheme(nextTheme);
217
+ return {};
218
+ });
219
+ },
220
+
221
+ // REWRITTEN FUNCTION using hardcoded neutrals for better UI contrast
222
+ setMazicColor: (baseColor: string) => {
223
+ // Determine the best text color for the button based on the base color's brightness
224
+ // const buttonTextColor = isColorDark(baseColor) ? '#FFFFFF' : '#000000';
225
+ const buttonTextColor = '#FFFFFF' ;
226
+ const secondaryBaseColor = adjustBrightness(baseColor, -10); // A slightly darker shade for accents
227
+
228
+ // --- Light Theme Palette ---
229
+ const lightColors: ThemeColor = {
230
+ // Primary & Button Colors (baseColor is the accent)
231
+ btnColor: addAlpha(baseColor,1),
232
+ btnBorderColor: baseColor,
233
+ btnIconColor: buttonTextColor,
234
+ themeColor: baseColor,
235
+ btnTextColor: buttonTextColor,
236
+
237
+ tabBarIconActiveColor: buttonTextColor,
238
+ tabBarIconInactiveColor: addAlpha("#000000",0.4),
239
+
240
+ primaryColor: addAlpha(baseColor,1),
241
+ secondaryColor: addAlpha(baseColor,0.7),
242
+ tertiaryColor: addAlpha(baseColor,0.1),
243
+
244
+ // Backgrounds (Clean white/near-white neutrals)
245
+ screenBgColor: addAlpha(baseColor,0.1), // Pure white
246
+ modalBgColor: addAlpha('#000000', 0.5), // Standard dark overlay
247
+ modalHeaderBgColor: '#F0F0F0', // Light gray
248
+ modalCardBgColor: '#FAFAFA', // Off-white for cards/modals
249
+
250
+ // Text Colors (High contrast black/dark gray)
251
+ textPrimary: '#1F1F1F', // Very dark gray
252
+ textSecondary: '#6B7280', // Medium gray
253
+
254
+ // Borders & Dividers (Very subtle grays)
255
+ borderColor: '#E5E7EB',
256
+ dividerColor: '#F3F4F6',
257
+
258
+ // Inputs & Cards
259
+ inputBgColor: '#FFFFFF',
260
+ inputBorderColor: '#D1D5DB',
261
+ cardBgColor: '#FFFFFF',
262
+ cardBorderColor: '#E5E7EB',
263
+
264
+ // Status Colors (Standard, high-contrast semantic colors)
265
+ accentColor: secondaryBaseColor,
266
+ errorColor: '#DC2626',
267
+ successColor: '#16A34A',
268
+ warningColor: '#F59E0B',
269
+ };
270
+
271
+ // --- Dark Theme Palette ---
272
+ const darkColors: ThemeColor = {
273
+ // Primary & Button Colors
274
+ btnColor: addAlpha(baseColor,1),
275
+ btnBorderColor: baseColor,
276
+ btnIconColor: buttonTextColor,
277
+ themeColor: baseColor,
278
+ btnTextColor: buttonTextColor,
279
+
280
+ tabBarIconActiveColor: buttonTextColor,
281
+ tabBarIconInactiveColor: addAlpha("#000000",0.4),
282
+ primaryColor: addAlpha(baseColor,1),
283
+ secondaryColor: addAlpha(baseColor,0.7),
284
+ tertiaryColor: addAlpha(baseColor,0.2),
285
+
286
+ // Backgrounds (Clean dark/near-black neutrals)
287
+ screenBgColor: addAlpha(baseColor,0.8), // Very dark gray
288
+ modalBgColor: addAlpha('#000000', 0.8), // Darker overlay
289
+ modalHeaderBgColor: '#1F1F1F', // Slightly lighter dark gray
290
+ modalCardBgColor: '#2C2C2C', // Medium dark gray for cards/modals
291
+
292
+ // Text Colors (High contrast white/light gray)
293
+ textPrimary: '#FFFFFF', // Pure white
294
+ textSecondary: '#A0A0A0', // Light gray
295
+
296
+ // Borders & Dividers (Subtle dark grays)
297
+ borderColor: '#374151',
298
+ dividerColor: '#2C2C2C',
299
+
300
+ // Inputs & Cards
301
+ inputBgColor: '#1F1F1F',
302
+ inputBorderColor: '#374151',
303
+ cardBgColor: '#1F1F1F',
304
+ cardBorderColor: '#374151',
305
+
306
+ // Status Colors (Brighter semantic colors for dark background)
307
+ accentColor: secondaryBaseColor,
308
+ errorColor: '#EF4444',
309
+ successColor: '#22C55E',
310
+ warningColor: '#FBBF24',
311
+ };
312
+
313
+ set((state: ThemeStore) => {
314
+ const newColors = {
315
+ light: lightColors,
316
+ dark: darkColors,
317
+ };
318
+ const themeColors = getThemeColors(state.tcbsTheme, newColors);
319
+ return { colors: newColors, themeColors };
320
+ });
321
+ }
322
+ };
323
+ });