create-expo-stack 2.6.5 → 2.7.0-next.2960d96

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.
Files changed (43) hide show
  1. package/README.md +23 -9
  2. package/build/templates/base/App.tsx.ejs +3 -0
  3. package/build/templates/base/babel.config.js.ejs +1 -1
  4. package/build/templates/base/package.json.ejs +40 -3
  5. package/build/templates/base/prettier.config.js.ejs +1 -1
  6. package/build/templates/base/tsconfig.json.ejs +10 -6
  7. package/build/templates/packages/expo-router/drawer/app/_layout.tsx.ejs +4 -1
  8. package/build/templates/packages/expo-router/stack/app/_layout.tsx.ejs +4 -1
  9. package/build/templates/packages/expo-router/tabs/app/_layout.tsx.ejs +4 -1
  10. package/build/templates/packages/nativewindui/app/+not-found.tsx.ejs +18 -0
  11. package/build/templates/packages/nativewindui/app/_layout.tsx.ejs +85 -0
  12. package/build/templates/packages/nativewindui/app/index.tsx.ejs +652 -0
  13. package/build/templates/packages/nativewindui/app/modal.tsx.ejs +32 -0
  14. package/build/templates/packages/nativewindui/components/nativewind-ui/ActivityIndicator.tsx.ejs +10 -0
  15. package/build/templates/packages/nativewindui/components/nativewind-ui/Avatar.tsx.ejs +139 -0
  16. package/build/templates/packages/nativewindui/components/nativewind-ui/DatePicker.android.tsx.ejs +66 -0
  17. package/build/templates/packages/nativewindui/components/nativewind-ui/DatePicker.tsx.ejs +10 -0
  18. package/build/templates/packages/nativewindui/components/nativewind-ui/Picker.tsx.ejs +39 -0
  19. package/build/templates/packages/nativewindui/components/nativewind-ui/ProgressIndicator.tsx.ejs +95 -0
  20. package/build/templates/packages/nativewindui/components/nativewind-ui/SegmentedControl.tsx.ejs +22 -0
  21. package/build/templates/packages/nativewindui/components/nativewind-ui/Sheet.tsx.ejs +59 -0
  22. package/build/templates/packages/nativewindui/components/nativewind-ui/Slider.tsx.ejs +28 -0
  23. package/build/templates/packages/nativewindui/components/nativewind-ui/Text.tsx.ejs +55 -0
  24. package/build/templates/packages/nativewindui/components/nativewind-ui/ThemeToggle.tsx.ejs +39 -0
  25. package/build/templates/packages/nativewindui/components/nativewind-ui/Toggle.tsx.ejs +17 -0
  26. package/build/templates/packages/nativewindui/expo-env.d.ts.ejs +3 -0
  27. package/build/templates/packages/nativewindui/global.css.ejs +91 -0
  28. package/build/templates/packages/nativewindui/lib/cn.ts.ejs +6 -0
  29. package/build/templates/packages/nativewindui/lib/useColorScheme.tsx.ejs +14 -0
  30. package/build/templates/packages/nativewindui/lib/useHeaderSearchBar.tsx.ejs +31 -0
  31. package/build/templates/packages/nativewindui/metro.config.js.ejs +10 -0
  32. package/build/templates/packages/nativewindui/nativewind-env.d.ts.ejs +1 -0
  33. package/build/templates/packages/nativewindui/tailwind.config.js.ejs +66 -0
  34. package/build/templates/packages/nativewindui/theme/colors.ts.ejs +71 -0
  35. package/build/templates/packages/nativewindui/theme/index.ts.ejs +29 -0
  36. package/build/templates/packages/react-navigation/App.tsx.ejs +4 -1
  37. package/build/types/types.d.ts +4 -2
  38. package/build/types.js +2 -1
  39. package/build/utilities/configureProjectFiles.js +265 -197
  40. package/build/utilities/generateProjectFiles.js +18 -13
  41. package/build/utilities/printOutput.js +5 -5
  42. package/build/utilities/runCLI.js +99 -22
  43. package/package.json +67 -67
@@ -0,0 +1,139 @@
1
+ import * as React from 'react';
2
+ import {
3
+ ImageErrorEventData,
4
+ ImageLoadEventData,
5
+ NativeSyntheticEvent,
6
+ Image as RNImage,
7
+ View,
8
+ } from 'react-native';
9
+
10
+ import { cn } from '~/lib/cn';
11
+
12
+ interface AvatarRootProps {
13
+ alt: string;
14
+ }
15
+
16
+ interface AvatarImageProps {
17
+ children?: React.ReactNode;
18
+ onLoadingStatusChange?: (status: 'error' | 'loaded') => void;
19
+ }
20
+
21
+ type AvatarState = 'loading' | 'error' | 'loaded';
22
+
23
+ interface IRootContext extends AvatarRootProps {
24
+ status: AvatarState;
25
+ setStatus: (status: AvatarState) => void;
26
+ }
27
+
28
+ const RootContext = React.createContext<IRootContext | null>(null);
29
+
30
+ const Avatar = React.forwardRef<
31
+ React.ElementRef<typeof View>,
32
+ React.ComponentPropsWithoutRef<typeof View> & AvatarRootProps
33
+ >(({ alt, className, ...viewProps }, ref) => {
34
+ const [status, setStatus] = React.useState<AvatarState>('loading');
35
+ return (
36
+ <RootContext.Provider value={{ alt, status, setStatus }}>
37
+ <View
38
+ ref={ref}
39
+ className={cn(
40
+ 'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
41
+ className
42
+ )}
43
+ {...viewProps}
44
+ />
45
+ </RootContext.Provider>
46
+ );
47
+ });
48
+
49
+ Avatar.displayName = 'Avatar';
50
+
51
+ function useRootContext() {
52
+ const context = React.useContext(RootContext);
53
+ if (!context) {
54
+ throw new Error(
55
+ 'Avatar compound components cannot be rendered outside the Avatar component'
56
+ );
57
+ }
58
+ return context;
59
+ }
60
+
61
+ const AvatarImage = React.forwardRef<
62
+ React.ElementRef<typeof RNImage>,
63
+ Omit<React.ComponentPropsWithoutRef<typeof RNImage>, 'alt'> & AvatarImageProps
64
+ >(
65
+ (
66
+ {
67
+ onLoad: onLoadProps,
68
+ onError: onErrorProps,
69
+ onLoadingStatusChange,
70
+ className,
71
+ ...props
72
+ },
73
+ ref
74
+ ) => {
75
+ const { alt, setStatus, status } = useRootContext();
76
+
77
+ const onLoad = React.useCallback(
78
+ (e: NativeSyntheticEvent<ImageLoadEventData>) => {
79
+ setStatus('loaded');
80
+ onLoadingStatusChange?.('loaded');
81
+ onLoadProps?.(e);
82
+ },
83
+ [onLoadProps]
84
+ );
85
+
86
+ const onError = React.useCallback(
87
+ (e: NativeSyntheticEvent<ImageErrorEventData>) => {
88
+ setStatus('error');
89
+ onLoadingStatusChange?.('error');
90
+ onErrorProps?.(e);
91
+ },
92
+ [onErrorProps]
93
+ );
94
+
95
+ if (status === 'error') {
96
+ return null;
97
+ }
98
+
99
+ return (
100
+ <RNImage
101
+ ref={ref}
102
+ alt={alt}
103
+ onLoad={onLoad}
104
+ onError={onError}
105
+ className={cn('aspect-square h-full w-full', className)}
106
+ {...props}
107
+ />
108
+ );
109
+ }
110
+ );
111
+
112
+ AvatarImage.displayName = 'AvatarImage';
113
+
114
+ const AvatarFallback = React.forwardRef<
115
+ React.ElementRef<typeof View>,
116
+ React.ComponentPropsWithoutRef<typeof View>
117
+ >(({ className, ...props }, ref) => {
118
+ const { alt, status } = useRootContext();
119
+
120
+ if (status !== 'error') {
121
+ return null;
122
+ }
123
+ return (
124
+ <View
125
+ ref={ref}
126
+ role={'img'}
127
+ aria-label={alt}
128
+ className={cn(
129
+ 'flex h-full w-full items-center justify-center rounded-full bg-muted',
130
+ className
131
+ )}
132
+ {...props}
133
+ />
134
+ );
135
+ });
136
+
137
+ AvatarFallback.displayName = 'AvatarFallback';
138
+
139
+ export { Avatar, AvatarFallback, AvatarImage };
@@ -0,0 +1,66 @@
1
+ import DateTimePicker, {
2
+ DateTimePickerAndroid,
3
+ } from '@react-native-community/datetimepicker';
4
+ import * as React from 'react';
5
+ import { Pressable, View } from 'react-native';
6
+
7
+ import { Text } from '~/components/Text';
8
+
9
+ export function DatePicker(
10
+ props: React.ComponentProps<typeof DateTimePicker> & {
11
+ mode: 'date' | 'time' | 'datetime';
12
+ }
13
+ ) {
14
+ const show = (currentMode: 'time' | 'date') => () => {
15
+ DateTimePickerAndroid.open({
16
+ value: props.value,
17
+ onChange: props.onChange,
18
+ mode: currentMode,
19
+ minimumDate: props.minimumDate,
20
+ maximumDate: props.maximumDate,
21
+ });
22
+ };
23
+
24
+ return (
25
+ <View className='flex-row gap-2.5'>
26
+ {props.mode.includes('date') && (
27
+ <View className='relative pt-1.5'>
28
+ <Pressable
29
+ onPress={show('date')}
30
+ className='border border-border active:opacity-80 px-5 py-3 rounded-md'
31
+ >
32
+ <Text variant='subhead'>
33
+ {new Intl.DateTimeFormat('en-US', {
34
+ dateStyle: 'medium',
35
+ }).format(props.value)}
36
+ </Text>
37
+ </Pressable>
38
+ <View className='absolute top-0 left-2 bg-card px-1'>
39
+ <Text variant='caption2' className='text-[10px] opacity-60'>
40
+ Date
41
+ </Text>
42
+ </View>
43
+ </View>
44
+ )}
45
+ {props.mode.includes('time') && (
46
+ <View className='relative pt-1.5'>
47
+ <Pressable
48
+ onPress={show('time')}
49
+ className='border border-border active:opacity-80 px-5 py-3 rounded-md'
50
+ >
51
+ <Text variant='subhead'>
52
+ {new Intl.DateTimeFormat('en-US', {
53
+ timeStyle: 'short',
54
+ }).format(props.value)}
55
+ </Text>
56
+ </Pressable>
57
+ <View className='absolute top-0 left-2 bg-card px-1'>
58
+ <Text variant='caption2' className='text-[10px] opacity-60'>
59
+ Time
60
+ </Text>
61
+ </View>
62
+ </View>
63
+ )}
64
+ </View>
65
+ );
66
+ }
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import DateTimePicker from '@react-native-community/datetimepicker';
3
+
4
+ export function DatePicker(
5
+ props: React.ComponentProps<typeof DateTimePicker> & {
6
+ mode: 'date' | 'time' | 'datetime';
7
+ }
8
+ ) {
9
+ return <DateTimePicker {...props} />;
10
+ }
@@ -0,0 +1,39 @@
1
+ import { Picker as RNPicker } from '@react-native-picker/picker';
2
+ import { View } from 'react-native';
3
+
4
+ import { useColorScheme } from '~/lib/useColorScheme';
5
+ import { cn } from '~/lib/cn';
6
+
7
+ export function Picker<T>({
8
+ mode = 'dropdown',
9
+ style,
10
+ dropdownIconColor,
11
+ dropdownIconRippleColor,
12
+ className,
13
+ ...props
14
+ }: React.ComponentPropsWithoutRef<typeof RNPicker<T>>) {
15
+ const { colors } = useColorScheme();
16
+ return (
17
+ <View
18
+ className={cn(
19
+ 'bg-background border border-background ios:shadow-sm ios:shadow-black/5 rounded-md',
20
+ className
21
+ )}
22
+ >
23
+ <RNPicker
24
+ mode={mode}
25
+ style={
26
+ style ?? {
27
+ backgroundColor: colors.root,
28
+ borderRadius: 8,
29
+ }
30
+ }
31
+ dropdownIconColor={dropdownIconColor ?? colors.foreground}
32
+ dropdownIconRippleColor={dropdownIconRippleColor ?? colors.foreground}
33
+ {...props}
34
+ />
35
+ </View>
36
+ );
37
+ }
38
+
39
+ export const PickerItem = RNPicker.Item;
@@ -0,0 +1,95 @@
1
+ import * as React from 'react';
2
+ import { View } from 'react-native';
3
+ import Animated, {
4
+ Extrapolation,
5
+ interpolate,
6
+ useAnimatedStyle,
7
+ useDerivedValue,
8
+ withSpring,
9
+ } from 'react-native-reanimated';
10
+
11
+ import { cn } from '~/lib/cn';
12
+
13
+ const DEFAULT_MAX = 100;
14
+
15
+ const ProgressIndicator = React.forwardRef<
16
+ React.ElementRef<typeof View>,
17
+ React.ComponentPropsWithoutRef<typeof View> & {
18
+ value?: number;
19
+ max?: number;
20
+ getValueLabel?: (value: number, max: number) => string;
21
+ }
22
+ >(
23
+ (
24
+ {
25
+ value: valueProp,
26
+ max: maxProp,
27
+ getValueLabel = defaultGetValueLabel,
28
+ className,
29
+ children,
30
+ ...props
31
+ },
32
+ ref
33
+ ) => {
34
+ const max = maxProp ?? DEFAULT_MAX;
35
+ const value = isValidValueNumber(valueProp, max) ? valueProp : 0;
36
+ const progress = useDerivedValue(() => value ?? 0);
37
+
38
+ const indicator = useAnimatedStyle(() => {
39
+ return {
40
+ width: withSpring(
41
+ `${interpolate(
42
+ progress.value,
43
+ [0, 100],
44
+ [1, 100],
45
+ Extrapolation.CLAMP
46
+ )}%`,
47
+ { overshootClamping: true }
48
+ ),
49
+ };
50
+ });
51
+
52
+ return (
53
+ <View
54
+ role='progressbar'
55
+ ref={ref}
56
+ aria-valuemax={max}
57
+ aria-valuemin={0}
58
+ aria-valuenow={value}
59
+ aria-valuetext={getValueLabel(value, max)}
60
+ accessibilityValue={{
61
+ min: 0,
62
+ max,
63
+ now: value,
64
+ text: getValueLabel(value, max),
65
+ }}
66
+ className={cn(
67
+ 'relative h-1 w-full overflow-hidden rounded-full',
68
+ className
69
+ )}
70
+ {...props}
71
+ >
72
+ <View className='absolute left-0 top-0 right-0 bottom-0 bg-muted opacity-20' />
73
+ <Animated.View
74
+ role='presentation'
75
+ style={indicator}
76
+ className={cn('h-full bg-primary')}
77
+ />
78
+ </View>
79
+ );
80
+ }
81
+ );
82
+
83
+ ProgressIndicator.displayName = 'ProgressIndicator';
84
+
85
+ export { ProgressIndicator };
86
+
87
+ function defaultGetValueLabel(value: number, max: number) {
88
+ return `${Math.round((value / max) * 100)}%`;
89
+ }
90
+
91
+ function isValidValueNumber(value: any, max: number): value is number {
92
+ return (
93
+ typeof value === 'number' && !isNaN(value) && value <= max && value >= 0
94
+ );
95
+ }
@@ -0,0 +1,22 @@
1
+ import RNSegmentedControl from '@react-native-segmented-control/segmented-control';
2
+ import { Platform } from 'react-native';
3
+
4
+ import { useColorScheme } from '~/lib/useColorScheme';
5
+
6
+ export function SegmentedControl(
7
+ props: React.ComponentPropsWithoutRef<typeof RNSegmentedControl>
8
+ ) {
9
+ const { colorScheme, colors } = useColorScheme();
10
+ return (
11
+ <RNSegmentedControl
12
+ backgroundColor={
13
+ Platform.OS === 'android'
14
+ ? colorScheme === 'dark'
15
+ ? colors.background
16
+ : colors.grey6
17
+ : undefined
18
+ }
19
+ {...props}
20
+ />
21
+ );
22
+ }
@@ -0,0 +1,59 @@
1
+ import {
2
+ BottomSheetBackdrop,
3
+ BottomSheetBackdropProps,
4
+ BottomSheetModal,
5
+ } from '@gorhom/bottom-sheet';
6
+ import * as React from 'react';
7
+
8
+ import { useColorScheme } from '~/lib/useColorScheme';
9
+
10
+ const Sheet = React.forwardRef<
11
+ BottomSheetModal,
12
+ React.ComponentPropsWithoutRef<typeof BottomSheetModal>
13
+ >(
14
+ (
15
+ { index = 0, backgroundStyle, style, handleIndicatorStyle, ...props },
16
+ ref
17
+ ) => {
18
+ const { colors } = useColorScheme();
19
+
20
+ const renderBackdrop = React.useCallback(
21
+ (props: BottomSheetBackdropProps) => (
22
+ <BottomSheetBackdrop {...props} disappearsOnIndex={-1} />
23
+ ),
24
+ []
25
+ );
26
+ return (
27
+ <BottomSheetModal
28
+ ref={ref}
29
+ index={0}
30
+ backgroundStyle={
31
+ backgroundStyle ?? {
32
+ backgroundColor: colors.card,
33
+ }
34
+ }
35
+ style={
36
+ style ?? {
37
+ borderWidth: 1,
38
+ borderColor: colors.grey5,
39
+ borderTopStartRadius: 16,
40
+ borderTopEndRadius: 16,
41
+ }
42
+ }
43
+ handleIndicatorStyle={
44
+ handleIndicatorStyle ?? {
45
+ backgroundColor: colors.grey4,
46
+ }
47
+ }
48
+ backdropComponent={renderBackdrop}
49
+ {...props}
50
+ />
51
+ );
52
+ }
53
+ );
54
+
55
+ function useSheetRef() {
56
+ return React.useRef<BottomSheetModal>(null);
57
+ }
58
+
59
+ export { Sheet, useSheetRef };
@@ -0,0 +1,28 @@
1
+ import RNSlider from '@react-native-community/slider';
2
+ import { Platform } from 'react-native';
3
+
4
+ import { useColorScheme } from '~/lib/useColorScheme';
5
+ import { COLORS } from '~/theme/colors';
6
+
7
+ export function Slider({
8
+ thumbTintColor,
9
+ minimumTrackTintColor,
10
+ maximumTrackTintColor,
11
+ ...props
12
+ }: React.ComponentPropsWithoutRef<typeof RNSlider>) {
13
+ const { colors } = useColorScheme();
14
+ return (
15
+ <RNSlider
16
+ thumbTintColor={
17
+ thumbTintColor ?? Platform.OS === 'ios' ? COLORS.white : colors.blue
18
+ }
19
+ minimumTrackTintColor={minimumTrackTintColor ?? colors.blue}
20
+ maximumTrackTintColor={
21
+ maximumTrackTintColor ?? Platform.OS === 'android'
22
+ ? colors.blue
23
+ : undefined
24
+ }
25
+ {...props}
26
+ />
27
+ );
28
+ }
@@ -0,0 +1,55 @@
1
+ import { VariantProps, cva } from 'class-variance-authority';
2
+ import { cssInterop } from 'nativewind';
3
+ import * as React from 'react';
4
+ import { UITextView } from 'react-native-uitextview';
5
+
6
+ import { cn } from '~/lib/cn';
7
+
8
+ cssInterop(UITextView, { className: 'style' });
9
+
10
+ const textVariants = cva('text-foreground', {
11
+ variants: {
12
+ variant: {
13
+ largeTitle: 'text-4xl',
14
+ title1: 'text-2xl',
15
+ title2: 'text-[22px] leading-7',
16
+ title3: 'text-xl',
17
+ heading: 'text-[17px] leading-6 font-semibold',
18
+ body: 'text-[17px] leading-6',
19
+ callout: 'text-base',
20
+ subhead: 'text-[15px] leading-6',
21
+ footnote: 'text-[13px] leading-5',
22
+ caption1: 'text-xs',
23
+ caption2: 'text-[11px] leading-4',
24
+ },
25
+ color: {
26
+ primary: '',
27
+ secondary: 'text-secondary-foreground/90',
28
+ tertiary: 'text-muted-foreground/90',
29
+ quarternary: 'text-muted-foreground/50',
30
+ },
31
+ },
32
+ defaultVariants: {
33
+ variant: 'body',
34
+ color: 'primary',
35
+ },
36
+ });
37
+
38
+ const TextClassContext = React.createContext<string | undefined>(undefined);
39
+
40
+ function Text({
41
+ className,
42
+ variant,
43
+ color,
44
+ ...props
45
+ }: React.ComponentPropsWithoutRef<typeof UITextView> & VariantProps<typeof textVariants>) {
46
+ const textClassName = React.useContext(TextClassContext);
47
+ return (
48
+ <UITextView
49
+ className={cn(textVariants({ variant, color }), textClassName, className)}
50
+ {...props}
51
+ />
52
+ );
53
+ }
54
+
55
+ export { Text, TextClassContext, textVariants };
@@ -0,0 +1,39 @@
1
+ import { Icon } from '@roninoss/icons';
2
+ import { Appearance, Pressable, View } from 'react-native';
3
+ import Animated, { LayoutAnimationConfig, ZoomInRotate } from 'react-native-reanimated';
4
+
5
+ import { cn } from '~/lib/cn';
6
+ import { useColorScheme } from '~/lib/useColorScheme';
7
+ import { COLORS } from '~/theme/colors';
8
+
9
+ export function ThemeToggle() {
10
+ const { colorScheme, toggleColorScheme } = useColorScheme();
11
+ return (
12
+ <LayoutAnimationConfig skipEntering>
13
+ <Animated.View
14
+ className="items-center justify-center"
15
+ key={`toggle-${colorScheme}`}
16
+ entering={ZoomInRotate}>
17
+ <Pressable
18
+ onPress={() => {
19
+ // ColorScheme is set with `Appearance` as well for the iOS header search placeholder text: https://github.com/software-mansion/react-native-screens/discussions/1758
20
+ Appearance.setColorScheme(colorScheme === 'dark' ? 'light' : 'dark');
21
+ toggleColorScheme();
22
+ }}
23
+ className="opacity-80">
24
+ {colorScheme === 'dark'
25
+ ? ({ pressed }) => (
26
+ <View className={cn('px-0.5', pressed && 'opacity-50')}>
27
+ <Icon namingScheme="sfSymbol" name="moon.stars" color={COLORS.white} />
28
+ </View>
29
+ )
30
+ : ({ pressed }) => (
31
+ <View className={cn('px-0.5', pressed && 'opacity-50')}>
32
+ <Icon namingScheme="sfSymbol" name="sun.min" color={COLORS.black} />
33
+ </View>
34
+ )}
35
+ </Pressable>
36
+ </Animated.View>
37
+ </LayoutAnimationConfig>
38
+ );
39
+ }
@@ -0,0 +1,17 @@
1
+ import { useColorScheme } from '~/lib/useColorScheme';
2
+ import { COLORS } from '~/theme/colors';
3
+ import { Switch } from 'react-native';
4
+
5
+ export function Toggle(props: React.ComponentPropsWithoutRef<typeof Switch>) {
6
+ const { colors } = useColorScheme();
7
+ return (
8
+ <Switch
9
+ trackColor={{
10
+ true: colors.blue,
11
+ false: colors.grey,
12
+ }}
13
+ thumbColor={COLORS.white}
14
+ {...props}
15
+ />
16
+ );
17
+ }
@@ -0,0 +1,3 @@
1
+ /// <reference types="expo/types" />
2
+
3
+ // NOTE: This file should not be edited and should be in your git ignore
@@ -0,0 +1,91 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ :root {
7
+ --background: 242 242 247;
8
+ --foreground: 0 0 0;
9
+ --card: 255 255 255;
10
+ --card-foreground: 8 28 30;
11
+ --popover: 230 230 235;
12
+ --popover-foreground: 0 0 0;
13
+ --primary: 0 123 254;
14
+ --primary-foreground: 255 255 255;
15
+ --secondary: 45 175 231;
16
+ --secondary-foreground: 255 255 255;
17
+ --muted: 175 176 180;
18
+ --muted-foreground: 142 142 147;
19
+ --accent: 255 40 84;
20
+ --accent-foreground: 255 255 255;
21
+ --destructive: 255 56 43;
22
+ --destructive-foreground: 255 255 255;
23
+ --border: 230 230 235;
24
+ --input: 210 210 215;
25
+ --ring: 230 230 235;
26
+
27
+ --android-background: 249 249 255;
28
+ --android-foreground: 0 0 0;
29
+ --android-card: 255 255 255;
30
+ --android-card-foreground: 24 28 35;
31
+ --android-popover: 215 217 228;
32
+ --android-popover-foreground: 0 0 0;
33
+ --android-primary: 0 112 233;
34
+ --android-primary-foreground: 255 255 255;
35
+ --android-secondary: 176 201 255;
36
+ --android-secondary-foreground: 20 55 108;
37
+ --android-muted: 193 198 215;
38
+ --android-muted-foreground: 113 119 134;
39
+ --android-accent: 169 73 204;
40
+ --android-accent-foreground: 255 255 255;
41
+ --android-destructive: 254 67 54;
42
+ --android-destructive-foreground: 255 255 255;
43
+ --android-border: 215 217 228;
44
+ --android-input: 210 210 215;
45
+ --android-ring: 215 217 228;
46
+ }
47
+
48
+ @media (prefers-color-scheme: dark) {
49
+ :root {
50
+ --background: 0 0 0;
51
+ --foreground: 255 255 255;
52
+ --card: 21 21 24;
53
+ --card-foreground: 255 255 255;
54
+ --popover: 40 40 42;
55
+ --popover-foreground: 255 255 255;
56
+ --primary: 3 133 255;
57
+ --primary-foreground: 255 255 255;
58
+ --secondary: 100 211 254;
59
+ --secondary-foreground: 255 255 255;
60
+ --muted: 70 70 73;
61
+ --muted-foreground: 142 142 147;
62
+ --accent: 255 52 95;
63
+ --accent-foreground: 255 255 255;
64
+ --destructive: 254 67 54;
65
+ --destructive-foreground: 255 255 255;
66
+ --border: 40 40 42;
67
+ --input: 55 55 57;
68
+ --ring: 40 40 42;
69
+
70
+ --android-background: 0 0 0;
71
+ --android-foreground: 255 255 255;
72
+ --android-card: 16 19 27;
73
+ --android-card-foreground: 224 226 237;
74
+ --android-popover: 39 42 50;
75
+ --android-popover-foreground: 224 226 237;
76
+ --android-primary: 3 133 255;
77
+ --android-primary-foreground: 255 255 255;
78
+ --android-secondary: 28 60 114;
79
+ --android-secondary-foreground: 189 209 255;
80
+ --android-muted: 216 226 255;
81
+ --android-muted-foreground: 139 144 160;
82
+ --android-accent: 83 0 111;
83
+ --android-accent-foreground: 238 177 255;
84
+ --android-destructive: 147 0 10;
85
+ --android-destructive-foreground: 255 255 255;
86
+ --android-border: 39 42 50;
87
+ --android-input: 55 55 57;
88
+ --android-ring: 39 42 50;
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,14 @@
1
+ import { useColorScheme as useNativewindColorScheme } from 'nativewind';
2
+
3
+ import { COLORS } from '~/theme/colors';
4
+
5
+ export function useColorScheme() {
6
+ const { colorScheme, setColorScheme, toggleColorScheme } = useNativewindColorScheme();
7
+ return {
8
+ colorScheme: colorScheme ?? 'light',
9
+ isDarkColorScheme: colorScheme === 'dark',
10
+ setColorScheme,
11
+ toggleColorScheme,
12
+ colors: COLORS[colorScheme ?? 'light'],
13
+ };
14
+ }