diff --git a/src/components/caret.tsx b/src/components/caret.tsx
index 6a56278..ba2e482 100644
--- a/src/components/caret.tsx
+++ b/src/components/caret.tsx
@@ -1,11 +1,17 @@
import { useEffect, useRef } from 'react';
-import { StyleSheet, Animated } from 'react-native';
-import { DEFAULT_DARK_COLOR } from '../constants';
+import { StyleSheet, Animated, Platform } from 'react-native';
import { useTextInputOTP } from '../hooks/use-text-input-otp';
+import { useThemeColor } from '../hooks/use-theme-color';
+import { theme } from '../theme';
export function Caret() {
const opacity = useRef(new Animated.Value(0)).current;
+ const useNativeDriver = Platform.OS === 'ios' || Platform.OS === 'android';
const { caretColor } = useTextInputOTP();
+ const defaultBackgroundColor = useThemeColor({
+ light: theme.colorBlack,
+ dark: theme.colorWhite,
+ });
useEffect(() => {
Animated.loop(
@@ -13,12 +19,12 @@ export function Caret() {
Animated.timing(opacity, {
toValue: 0,
duration: 500,
- useNativeDriver: true,
+ useNativeDriver: false,
}),
Animated.timing(opacity, {
toValue: 1,
duration: 500,
- useNativeDriver: true,
+ useNativeDriver,
}),
])
).start();
@@ -29,7 +35,7 @@ export function Caret() {
testID="caret"
style={[
styles.caret,
- { opacity, backgroundColor: caretColor ?? DEFAULT_DARK_COLOR },
+ { opacity, backgroundColor: caretColor ?? defaultBackgroundColor },
]}
/>
);
@@ -37,8 +43,8 @@ export function Caret() {
const styles = StyleSheet.create({
caret: {
- width: 2,
- height: 16,
- borderRadius: 16,
+ width: theme.space2,
+ height: theme.space16,
+ borderRadius: theme.borderRadius16,
},
});
diff --git a/src/components/text-input-otp-separator.tsx b/src/components/text-input-otp-separator.tsx
index ade0d62..b0b06d2 100644
--- a/src/components/text-input-otp-separator.tsx
+++ b/src/components/text-input-otp-separator.tsx
@@ -1,20 +1,31 @@
import { View, StyleSheet } from 'react-native';
-import { DEFAULT_DARK_COLOR } from '../constants';
import type { TextInputOTPSeparatorProps } from '../types';
+import { useThemeColor } from '../hooks/use-theme-color';
+import { theme } from '../theme';
export function TextInputOTPSeparator({
separatorStyles,
}: TextInputOTPSeparatorProps) {
+ const defaultBackgroundColor = useThemeColor({
+ light: theme.colorBlack,
+ dark: theme.colorWhite,
+ });
+
return (
-
+
);
}
const styles = StyleSheet.create({
separator: {
- width: 10,
- height: 4,
- backgroundColor: DEFAULT_DARK_COLOR,
- borderRadius: 15,
+ width: theme.space10,
+ height: theme.space4,
+ borderRadius: theme.space16,
},
});
diff --git a/src/components/text-input-otp-slot.tsx b/src/components/text-input-otp-slot.tsx
index 00a8054..d0622f8 100644
--- a/src/components/text-input-otp-slot.tsx
+++ b/src/components/text-input-otp-slot.tsx
@@ -1,13 +1,15 @@
import { memo } from 'react';
import { Pressable, Text, StyleSheet } from 'react-native';
-import { Caret } from './caret';
import { useTextInputOTP } from '../hooks/use-text-input-otp';
import { useSlotBorderStyles } from '../hooks/use-slot-border-styles';
-import { DEFAULT_DARK_COLOR, SLOT_HEIGHT, SLOT_WIDTH } from '../constants';
+import { useThemeColor } from '../hooks/use-theme-color';
+import { SLOT_HEIGHT, SLOT_WIDTH } from '../constants';
import type {
TextInputOTPSlotInternalProps,
TextInputOTPSlotProps,
} from '../types';
+import { theme } from '../theme';
+import { Caret } from './caret';
function TextInputOTPSlotComponent({
index,
@@ -22,6 +24,11 @@ function TextInputOTPSlotComponent({
const { code, currentIndex, handlePress, caretHidden } = useTextInputOTP();
const isFocused = currentIndex === index;
const borderStyles = useSlotBorderStyles({ isFocused, isFirst, isLast });
+ const defaultTextColor = useThemeColor({
+ light: theme.colorBlack,
+ dark: theme.colorWhite,
+ });
+
const shouldRenderCaret = isFocused && !code[index] && !caretHidden;
return (
@@ -39,6 +46,7 @@ function TextInputOTPSlotComponent({
@@ -61,8 +69,7 @@ const styles = StyleSheet.create({
alignItems: 'center',
},
slotText: {
- color: DEFAULT_DARK_COLOR,
- fontSize: 14,
- fontWeight: 'bold',
+ fontSize: theme.fontSize14,
+ fontWeight: theme.fontWeightBold,
},
});
diff --git a/src/components/text-input-otp.tsx b/src/components/text-input-otp.tsx
index d8679b3..4ea3dd7 100644
--- a/src/components/text-input-otp.tsx
+++ b/src/components/text-input-otp.tsx
@@ -1,8 +1,9 @@
+import { forwardRef } from 'react';
import { View, StyleSheet } from 'react-native';
import { TextInputOTPProvider } from '../hooks/use-text-input-otp';
-import { TextInput } from './text-input';
-import { forwardRef } from 'react';
import type { TextInputOTPProps, TextInputOTPRef } from '../types';
+import { theme } from '../theme';
+import { TextInput } from './text-input';
export const TextInputOTP = forwardRef(
({ children, containerStyles, ...rest }, ref) => {
@@ -22,6 +23,6 @@ const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
- gap: 10,
+ gap: theme.space10,
},
});
diff --git a/src/constants.ts b/src/constants.ts
index b22701d..96b8c3f 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1,5 +1,3 @@
export const SLOT_WIDTH = 50;
export const SLOT_HEIGHT = 50;
export const FOCUSED_SLOT_HEIGHT = 54;
-export const DEFAULT_DARK_COLOR = '#030712';
-export const DEFAULT_LIGHT_COLOR = '#E4E7EC';
diff --git a/src/hooks/use-slot-border-styles.ts b/src/hooks/use-slot-border-styles.ts
index a8c3d11..fe739be 100644
--- a/src/hooks/use-slot-border-styles.ts
+++ b/src/hooks/use-slot-border-styles.ts
@@ -1,26 +1,33 @@
-import {
- DEFAULT_DARK_COLOR,
- DEFAULT_LIGHT_COLOR,
- FOCUSED_SLOT_HEIGHT,
- SLOT_HEIGHT,
-} from '../constants';
+import type { StyleProp, ViewStyle } from 'react-native';
+import { FOCUSED_SLOT_HEIGHT, SLOT_HEIGHT } from '../constants';
+import { theme } from '../theme';
import type { UseSlotBorderStylesProps } from '../types';
+import { useThemeColor } from './use-theme-color';
export function useSlotBorderStyles({
isFocused,
isFirst,
isLast,
-}: UseSlotBorderStylesProps) {
+}: UseSlotBorderStylesProps): StyleProp {
+ const darkBorder = useThemeColor({
+ light: theme.colorBlack,
+ dark: theme.colorWhite,
+ });
+ const lightBorder = useThemeColor({
+ light: theme.colorLightGrey,
+ dark: theme.colorDarkGrey,
+ });
+
return {
height: isFocused ? FOCUSED_SLOT_HEIGHT : SLOT_HEIGHT,
- borderColor: isFocused ? DEFAULT_DARK_COLOR : DEFAULT_LIGHT_COLOR,
- borderTopWidth: 2,
- borderBottomWidth: 2,
- borderLeftWidth: isFocused || isFirst ? 2 : 1,
- borderRightWidth: isFocused || isLast ? 2 : 1,
- borderTopLeftRadius: isFirst ? 8 : 0,
- borderTopRightRadius: isLast ? 8 : 0,
- borderBottomLeftRadius: isFirst ? 8 : 0,
- borderBottomRightRadius: isLast ? 8 : 0,
+ borderColor: isFocused ? darkBorder : lightBorder,
+ borderTopWidth: theme.space2,
+ borderBottomWidth: theme.space2,
+ borderLeftWidth: isFocused || isFirst ? theme.space2 : theme.space1,
+ borderRightWidth: isFocused || isLast ? theme.space2 : theme.space1,
+ borderTopLeftRadius: isFirst ? theme.borderRadius8 : theme.borderRadius0,
+ borderTopRightRadius: isLast ? theme.borderRadius8 : theme.borderRadius0,
+ borderBottomLeftRadius: isFirst ? theme.borderRadius8 : theme.borderRadius0,
+ borderBottomRightRadius: isLast ? theme.borderRadius8 : theme.borderRadius0,
};
}
diff --git a/src/hooks/use-theme-color.ts b/src/hooks/use-theme-color.ts
new file mode 100644
index 0000000..e34d4aa
--- /dev/null
+++ b/src/hooks/use-theme-color.ts
@@ -0,0 +1,6 @@
+import { useColorScheme } from 'react-native';
+
+export function useThemeColor(props: { light: T; dark: U }) {
+ const theme = useColorScheme() ?? 'light';
+ return props[theme];
+}
diff --git a/src/theme.ts b/src/theme.ts
new file mode 100644
index 0000000..8b3bac8
--- /dev/null
+++ b/src/theme.ts
@@ -0,0 +1,20 @@
+export const theme = {
+ colorBlack: '#030712',
+ colorWhite: '#E4E7EC',
+ colorLightGrey: '#E4E7EC',
+ colorDarkGrey: '#4b5563',
+
+ fontSize14: 14,
+
+ fontWeightBold: 'bold' as 'bold',
+
+ space1: 1,
+ space2: 2,
+ space4: 4,
+ space10: 10,
+ space16: 16,
+
+ borderRadius0: 0,
+ borderRadius8: 8,
+ borderRadius16: 16,
+};