create-better-t-stack 2.8.3 → 2.9.0

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 (62) hide show
  1. package/README.md +17 -16
  2. package/dist/index.js +5 -5
  3. package/package.json +1 -1
  4. package/templates/auth/native/unistyles/app/(drawer)/index.tsx.hbs +179 -0
  5. package/templates/auth/native/unistyles/components/sign-in.tsx.hbs +134 -0
  6. package/templates/auth/native/unistyles/components/sign-up.tsx.hbs +152 -0
  7. package/templates/frontend/native/{package.json → nativewind/package.json} +1 -1
  8. package/templates/frontend/native/unistyles/_gitignore +24 -0
  9. package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/_layout.tsx +34 -0
  10. package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/index.tsx +29 -0
  11. package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/two.tsx +29 -0
  12. package/templates/frontend/native/unistyles/app/(drawer)/_layout.tsx +59 -0
  13. package/templates/frontend/native/unistyles/app/(drawer)/index.tsx.hbs +115 -0
  14. package/templates/frontend/native/unistyles/app/+html.tsx +48 -0
  15. package/templates/frontend/native/unistyles/app/+not-found.tsx +34 -0
  16. package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +77 -0
  17. package/templates/frontend/native/unistyles/app/modal.tsx +29 -0
  18. package/templates/frontend/native/unistyles/app.json +44 -0
  19. package/templates/frontend/native/unistyles/babel.config.js +20 -0
  20. package/templates/frontend/native/unistyles/breakpoints.ts +9 -0
  21. package/templates/frontend/native/unistyles/components/container.tsx +20 -0
  22. package/templates/frontend/native/unistyles/components/header-button.tsx +31 -0
  23. package/templates/frontend/native/unistyles/components/tabbar-icon.tsx +15 -0
  24. package/templates/frontend/native/unistyles/expo-env.d.ts +3 -0
  25. package/templates/frontend/native/unistyles/index.js +2 -0
  26. package/templates/frontend/native/unistyles/metro.config.js +20 -0
  27. package/templates/frontend/native/unistyles/package.json +47 -0
  28. package/templates/frontend/native/unistyles/theme.ts +35 -0
  29. package/templates/frontend/native/unistyles/tsconfig.json +12 -0
  30. package/templates/frontend/native/unistyles/unistyles.ts +27 -0
  31. package/templates/frontend/react/web-base/src/components/header.tsx.hbs +5 -6
  32. /package/templates/auth/native/{lib/auth-client.ts → native-base/lib/auth-client.ts.hbs} +0 -0
  33. /package/templates/auth/native/{app → nativewind/app}/(drawer)/index.tsx.hbs +0 -0
  34. /package/templates/auth/native/{components → nativewind/components}/sign-in.tsx.hbs +0 -0
  35. /package/templates/auth/native/{components → nativewind/components}/sign-up.tsx.hbs +0 -0
  36. /package/templates/frontend/native/{assets → native-base/assets}/adaptive-icon.png +0 -0
  37. /package/templates/frontend/native/{assets → native-base/assets}/favicon.png +0 -0
  38. /package/templates/frontend/native/{assets → native-base/assets}/icon.png +0 -0
  39. /package/templates/frontend/native/{assets → native-base/assets}/splash.png +0 -0
  40. /package/templates/frontend/native/{_gitignore → nativewind/_gitignore} +0 -0
  41. /package/templates/frontend/native/{app → nativewind/app}/(drawer)/(tabs)/_layout.tsx +0 -0
  42. /package/templates/frontend/native/{app → nativewind/app}/(drawer)/(tabs)/index.tsx +0 -0
  43. /package/templates/frontend/native/{app → nativewind/app}/(drawer)/(tabs)/two.tsx +0 -0
  44. /package/templates/frontend/native/{app → nativewind/app}/(drawer)/_layout.tsx +0 -0
  45. /package/templates/frontend/native/{app → nativewind/app}/(drawer)/index.tsx.hbs +0 -0
  46. /package/templates/frontend/native/{app → nativewind/app}/+html.tsx +0 -0
  47. /package/templates/frontend/native/{app → nativewind/app}/+not-found.tsx +0 -0
  48. /package/templates/frontend/native/{app → nativewind/app}/_layout.tsx.hbs +0 -0
  49. /package/templates/frontend/native/{app → nativewind/app}/modal.tsx +0 -0
  50. /package/templates/frontend/native/{app-env.d.ts → nativewind/app-env.d.ts} +0 -0
  51. /package/templates/frontend/native/{app.json → nativewind/app.json} +0 -0
  52. /package/templates/frontend/native/{babel.config.js → nativewind/babel.config.js} +0 -0
  53. /package/templates/frontend/native/{components → nativewind/components}/container.tsx +0 -0
  54. /package/templates/frontend/native/{components → nativewind/components}/header-button.tsx +0 -0
  55. /package/templates/frontend/native/{components → nativewind/components}/tabbar-icon.tsx +0 -0
  56. /package/templates/frontend/native/{global.css → nativewind/global.css} +0 -0
  57. /package/templates/frontend/native/{lib → nativewind/lib}/android-navigation-bar.tsx +0 -0
  58. /package/templates/frontend/native/{lib → nativewind/lib}/constants.ts +0 -0
  59. /package/templates/frontend/native/{lib → nativewind/lib}/use-color-scheme.ts +0 -0
  60. /package/templates/frontend/native/{metro.config.js → nativewind/metro.config.js} +0 -0
  61. /package/templates/frontend/native/{tailwind.config.js → nativewind/tailwind.config.js} +0 -0
  62. /package/templates/frontend/native/{tsconfig.json → nativewind/tsconfig.json} +0 -0
@@ -0,0 +1,134 @@
1
+ import { authClient } from "@/lib/auth-client";
2
+ {{#if (eq api "trpc")}}
3
+ import { queryClient } from "@/utils/trpc";
4
+ {{/if}}
5
+ {{#if (eq api "orpc")}}
6
+ import { queryClient } from "@/utils/orpc";
7
+ {{/if}}
8
+ import { useState } from "react";
9
+ import {
10
+ ActivityIndicator,
11
+ Text,
12
+ TextInput,
13
+ TouchableOpacity,
14
+ View,
15
+ } from "react-native";
16
+ import { StyleSheet } from "react-native-unistyles";
17
+
18
+ export function SignIn() {
19
+ const [email, setEmail] = useState("");
20
+ const [password, setPassword] = useState("");
21
+ const [isLoading, setIsLoading] = useState(false);
22
+ const [error, setError] = useState<string | null>(null);
23
+
24
+ const handleLogin = async () => {
25
+ setIsLoading(true);
26
+ setError(null);
27
+
28
+ await authClient.signIn.email(
29
+ {
30
+ email,
31
+ password,
32
+ },
33
+ {
34
+ onError: (error) => {
35
+ setError(error.error?.message || "Failed to sign in");
36
+ setIsLoading(false);
37
+ },
38
+ onSuccess: () => {
39
+ setEmail("");
40
+ setPassword("");
41
+ queryClient.refetchQueries();
42
+ },
43
+ onFinished: () => {
44
+ setIsLoading(false);
45
+ },
46
+ },
47
+ );
48
+ };
49
+
50
+ return (
51
+ <View style={styles.container}>
52
+ <Text style={styles.title}>Sign In</Text>
53
+
54
+ {error && (
55
+ <View style={styles.errorContainer}>
56
+ <Text style={styles.errorText}>{error}</Text>
57
+ </View>
58
+ )}
59
+
60
+ <TextInput
61
+ style={styles.input}
62
+ placeholder="Email"
63
+ value={email}
64
+ onChangeText={setEmail}
65
+ keyboardType="email-address"
66
+ autoCapitalize="none"
67
+ />
68
+
69
+ <TextInput
70
+ style={styles.input}
71
+ placeholder="Password"
72
+ value={password}
73
+ onChangeText={setPassword}
74
+ secureTextEntry
75
+ />
76
+
77
+ <TouchableOpacity
78
+ onPress={handleLogin}
79
+ disabled={isLoading}
80
+ style={styles.button}
81
+ >
82
+ {isLoading ? (
83
+ <ActivityIndicator size="small" color="#fff" />
84
+ ) : (
85
+ <Text style={styles.buttonText}>Sign In</Text>
86
+ )}
87
+ </TouchableOpacity>
88
+ </View>
89
+ );
90
+ }
91
+
92
+ const styles = StyleSheet.create((theme) => ({
93
+ container: {
94
+ marginTop: 24,
95
+ padding: 16,
96
+ borderRadius: 8,
97
+ borderWidth: 1,
98
+ borderColor: theme.colors.border,
99
+ },
100
+ title: {
101
+ fontSize: 18,
102
+ fontWeight: "600",
103
+ color: theme.colors.typography,
104
+ marginBottom: 16,
105
+ },
106
+ errorContainer: {
107
+ marginBottom: 16,
108
+ padding: 12,
109
+ borderRadius: 6,
110
+ },
111
+ errorText: {
112
+ color: theme.colors.destructive,
113
+ fontSize: 14,
114
+ },
115
+ input: {
116
+ marginBottom: 12,
117
+ padding: 16,
118
+ borderRadius: 6,
119
+ color: theme.colors.typography,
120
+ borderWidth: 1,
121
+ borderColor: theme.colors.border,
122
+ },
123
+ button: {
124
+ backgroundColor: theme.colors.primary,
125
+ padding: 16,
126
+ borderRadius: 6,
127
+ flexDirection: "row",
128
+ justifyContent: "center",
129
+ alignItems: "center",
130
+ },
131
+ buttonText: {
132
+ fontWeight: "500",
133
+ },
134
+ }));
@@ -0,0 +1,152 @@
1
+ import { authClient } from "@/lib/auth-client";
2
+ {{#if (eq api "trpc")}}
3
+ import { queryClient } from "@/utils/trpc";
4
+ {{/if}}
5
+ {{#if (eq api "orpc")}}
6
+ import { queryClient } from "@/utils/orpc";
7
+ {{/if}}
8
+ import { useState } from "react";
9
+ import {
10
+ ActivityIndicator,
11
+ Text,
12
+ TextInput,
13
+ TouchableOpacity,
14
+ View,
15
+ } from "react-native";
16
+ import { StyleSheet } from "react-native-unistyles";
17
+
18
+ export function SignUp() {
19
+ const [name, setName] = useState("");
20
+ const [email, setEmail] = useState("");
21
+ const [password, setPassword] = useState("");
22
+ const [isLoading, setIsLoading] = useState(false);
23
+ const [error, setError] = useState<string | null>(null);
24
+
25
+ const handleSignUp = async () => {
26
+ setIsLoading(true);
27
+ setError(null);
28
+
29
+ await authClient.signUp.email(
30
+ {
31
+ name,
32
+ email,
33
+ password,
34
+ },
35
+ {
36
+ onError: (error) => {
37
+ setError(error.error?.message || "Failed to sign up");
38
+ setIsLoading(false);
39
+ },
40
+ onSuccess: () => {
41
+ setName("");
42
+ setEmail("");
43
+ setPassword("");
44
+ queryClient.refetchQueries();
45
+ },
46
+ onFinished: () => {
47
+ setIsLoading(false);
48
+ },
49
+ },
50
+ );
51
+ };
52
+
53
+ return (
54
+ <View style={styles.container}>
55
+ <Text style={styles.title}>Create Account</Text>
56
+
57
+ {error && (
58
+ <View style={styles.errorContainer}>
59
+ <Text style={styles.errorText}>{error}</Text>
60
+ </View>
61
+ )}
62
+
63
+ <TextInput
64
+ style={styles.input}
65
+ placeholder="Name"
66
+ value={name}
67
+ onChangeText={setName}
68
+ />
69
+
70
+ <TextInput
71
+ style={styles.input}
72
+ placeholder="Email"
73
+ value={email}
74
+ onChangeText={setEmail}
75
+ keyboardType="email-address"
76
+ autoCapitalize="none"
77
+ />
78
+
79
+ <TextInput
80
+ style={styles.inputLast}
81
+ placeholder="Password"
82
+ value={password}
83
+ onChangeText={setPassword}
84
+ secureTextEntry
85
+ />
86
+
87
+ <TouchableOpacity
88
+ onPress={handleSignUp}
89
+ disabled={isLoading}
90
+ style={styles.button}
91
+ >
92
+ {isLoading ? (
93
+ <ActivityIndicator size="small" color="#fff" />
94
+ ) : (
95
+ <Text style={styles.buttonText}>Sign Up</Text>
96
+ )}
97
+ </TouchableOpacity>
98
+ </View>
99
+ );
100
+ }
101
+
102
+ const styles = StyleSheet.create((theme) => ({
103
+ container: {
104
+ marginTop: 24,
105
+ padding: 16,
106
+ borderRadius: 8,
107
+ borderWidth: 1,
108
+ borderColor: theme.colors.border,
109
+ },
110
+ title: {
111
+ fontSize: 18,
112
+ fontWeight: "600",
113
+ color: theme.colors.typography,
114
+ marginBottom: 16,
115
+ },
116
+ errorContainer: {
117
+ marginBottom: 16,
118
+ padding: 12,
119
+ borderRadius: 6,
120
+ },
121
+ errorText: {
122
+ color: theme.colors.destructive,
123
+ fontSize: 14,
124
+ },
125
+ input: {
126
+ marginBottom: 12,
127
+ padding: 16,
128
+ borderRadius: 6,
129
+ color: theme.colors.typography,
130
+ borderWidth: 1,
131
+ borderColor: theme.colors.border,
132
+ },
133
+ inputLast: {
134
+ marginBottom: 16,
135
+ padding: 16,
136
+ borderRadius: 6,
137
+ color: theme.colors.typography,
138
+ borderWidth: 1,
139
+ borderColor: theme.colors.border,
140
+ },
141
+ button: {
142
+ backgroundColor: theme.colors.primary,
143
+ padding: 16,
144
+ borderRadius: 6,
145
+ flexDirection: "row",
146
+ justifyContent: "center",
147
+ alignItems: "center",
148
+ },
149
+ buttonText: {
150
+ fontWeight: "500",
151
+ },
152
+ }));
@@ -25,7 +25,7 @@
25
25
  "expo-status-bar": "~2.2.3",
26
26
  "expo-system-ui": "~5.0.6",
27
27
  "expo-web-browser": "~14.1.6",
28
- "nativewind": "latest",
28
+ "nativewind": "^4.1.23",
29
29
  "react": "19.0.0",
30
30
  "react-dom": "19.0.0",
31
31
  "react-native": "0.79.1",
@@ -0,0 +1,24 @@
1
+ node_modules/
2
+ .expo/
3
+ dist/
4
+ npm-debug.*
5
+ *.jks
6
+ *.p8
7
+ *.p12
8
+ *.key
9
+ *.mobileprovision
10
+ *.orig.*
11
+ web-build/
12
+ # expo router
13
+ expo-env.d.ts
14
+
15
+
16
+
17
+ ios
18
+ android
19
+
20
+ # macOS
21
+ .DS_Store
22
+
23
+ # Temporary files created by Metro to check the health of the file watcher
24
+ .metro-health-check*
@@ -0,0 +1,34 @@
1
+ import { Tabs } from "expo-router";
2
+ import { useUnistyles } from "react-native-unistyles";
3
+
4
+ import { TabBarIcon } from "@/components/tabbar-icon";
5
+
6
+ export default function TabLayout() {
7
+ const { theme } = useUnistyles();
8
+
9
+ return (
10
+ <Tabs
11
+ screenOptions={{
12
+ headerShown: false,
13
+ tabBarStyle: {
14
+ backgroundColor: theme.colors.background,
15
+ },
16
+ }}
17
+ >
18
+ <Tabs.Screen
19
+ name="index"
20
+ options={{
21
+ title: "Tab One",
22
+ tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />,
23
+ }}
24
+ />
25
+ <Tabs.Screen
26
+ name="two"
27
+ options={{
28
+ title: "Tab Two",
29
+ tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />,
30
+ }}
31
+ />
32
+ </Tabs>
33
+ );
34
+ }
@@ -0,0 +1,29 @@
1
+ import { Stack } from "expo-router";
2
+ import { StyleSheet } from "react-native-unistyles";
3
+ import { Container } from "@/components/container";
4
+ import { Text, View } from "react-native";
5
+
6
+ export default function Home() {
7
+ return (
8
+ <>
9
+ <Stack.Screen options={{ title: "Tab One" }} />
10
+ <Container>
11
+ <View style={styles.container}>
12
+ <Text style={styles.text}>Tab One</Text>
13
+ </View>
14
+ </Container>
15
+ </>
16
+ );
17
+ }
18
+
19
+ const styles = StyleSheet.create((theme) => ({
20
+ text: {
21
+ color: theme.colors.typography,
22
+ },
23
+ container: {
24
+ flex: 1,
25
+ paddingBottom: 100,
26
+ justifyContent: "center",
27
+ alignItems: "center",
28
+ },
29
+ }));
@@ -0,0 +1,29 @@
1
+ import { Stack } from "expo-router";
2
+ import { StyleSheet } from "react-native-unistyles";
3
+ import { Container } from "@/components/container";
4
+ import { Text, View } from "react-native";
5
+
6
+ export default function Home() {
7
+ return (
8
+ <>
9
+ <Stack.Screen options={{ title: "Tab Two" }} />
10
+ <Container>
11
+ <View style={styles.container}>
12
+ <Text style={styles.text}>Tab Two</Text>
13
+ </View>
14
+ </Container>
15
+ </>
16
+ );
17
+ }
18
+
19
+ const styles = StyleSheet.create((theme) => ({
20
+ text: {
21
+ color: theme.colors.typography,
22
+ },
23
+ container: {
24
+ flex: 1,
25
+ paddingBottom: 100,
26
+ justifyContent: "center",
27
+ alignItems: "center",
28
+ },
29
+ }));
@@ -0,0 +1,59 @@
1
+ import { Ionicons, MaterialIcons } from "@expo/vector-icons";
2
+ import { Link } from "expo-router";
3
+ import { Drawer } from "expo-router/drawer";
4
+ import { useUnistyles } from "react-native-unistyles";
5
+
6
+ import { HeaderButton } from "../../components/header-button";
7
+
8
+ const DrawerLayout = () => {
9
+ const { theme } = useUnistyles();
10
+
11
+ return (
12
+ <Drawer
13
+ screenOptions={{
14
+ headerStyle: {
15
+ backgroundColor: theme.colors.background,
16
+ },
17
+ headerTitleStyle: {
18
+ color: theme.colors.typography,
19
+ },
20
+ headerTintColor: theme.colors.typography,
21
+ drawerStyle: {
22
+ backgroundColor: theme.colors.background,
23
+ },
24
+ drawerLabelStyle: {
25
+ color: theme.colors.typography,
26
+ },
27
+ drawerInactiveTintColor: theme.colors.typography,
28
+ }}
29
+ >
30
+ <Drawer.Screen
31
+ name="index"
32
+ options={{
33
+ headerTitle: "Home",
34
+ drawerLabel: "Home",
35
+ drawerIcon: ({ size, color }) => (
36
+ <Ionicons name="home-outline" size={size} color={color} />
37
+ ),
38
+ }}
39
+ />
40
+ <Drawer.Screen
41
+ name="(tabs)"
42
+ options={{
43
+ headerTitle: "Tabs",
44
+ drawerLabel: "Tabs",
45
+ drawerIcon: ({ size, color }) => (
46
+ <MaterialIcons name="border-bottom" size={size} color={color} />
47
+ ),
48
+ headerRight: () => (
49
+ <Link href="/modal" asChild>
50
+ <HeaderButton />
51
+ </Link>
52
+ ),
53
+ }}
54
+ />
55
+ </Drawer>
56
+ );
57
+ };
58
+
59
+ export default DrawerLayout;
@@ -0,0 +1,115 @@
1
+ import { ScrollView, Text, View } from "react-native";
2
+ import { StyleSheet } from "react-native-unistyles";
3
+ import { Container } from "@/components/container";
4
+
5
+ {{#if (eq api "orpc")}}
6
+ import { useQuery } from "@tanstack/react-query";
7
+ import { orpc } from "@/utils/orpc";
8
+ {{/if}}
9
+ {{#if (eq api "trpc")}}
10
+ import { useQuery } from "@tanstack/react-query";
11
+ import { trpc } from "@/utils/trpc";
12
+ {{/if}}
13
+ {{#if (eq backend "convex")}}
14
+ import { useQuery } from "convex/react";
15
+ import { api } from "@{{ projectName }}/backend/convex/_generated/api.js";
16
+ {{/if}}
17
+
18
+ export default function Home() {
19
+ {{#if (eq api "orpc")}}
20
+ const healthCheck = useQuery(orpc.healthCheck.queryOptions());
21
+ {{/if}}
22
+ {{#if (eq api "trpc")}}
23
+ const healthCheck = useQuery(trpc.healthCheck.queryOptions());
24
+ {{/if}}
25
+ {{#if (eq backend "convex")}}
26
+ const healthCheck = useQuery(api.healthCheck.get);
27
+ {{/if}}
28
+
29
+ return (
30
+ <Container>
31
+ <ScrollView contentContainerStyle={styles.pageContainer}>
32
+ <Text style={styles.headerTitle}>BETTER T STACK</Text>
33
+
34
+ <View style={styles.apiStatusCard}>
35
+ <Text style={styles.cardTitle}>API Status</Text>
36
+ <View style={styles.apiStatusRow}>
37
+ <View
38
+ style={[
39
+ styles.statusIndicatorDot,
40
+ {{#if (or (eq api "orpc") (eq api "trpc"))}}
41
+ healthCheck.data
42
+ ? styles.statusIndicatorGreen
43
+ : styles.statusIndicatorRed,
44
+ {{else}}
45
+ healthCheck === "OK"
46
+ ? styles.statusIndicatorGreen
47
+ : styles.statusIndicatorRed,
48
+ {{/if}}
49
+ ]}
50
+ />
51
+ <Text style={styles.statusText}>
52
+ {{#if (or (eq api "orpc") (eq api "trpc"))}}
53
+ {healthCheck.isLoading
54
+ ? "Checking..."
55
+ : healthCheck.data
56
+ ? "Connected"
57
+ : "Disconnected"}
58
+ {{/if}}
59
+ {{#if (eq backend "convex")}}
60
+ {healthCheck === undefined
61
+ ? "Checking..."
62
+ : healthCheck === "OK"
63
+ ? "Connected"
64
+ : "Error"}
65
+ {{/if}}
66
+ </Text>
67
+ </View>
68
+ </View>
69
+ </ScrollView>
70
+ </Container>
71
+ );
72
+ }
73
+
74
+ const styles = StyleSheet.create((theme) => ({
75
+ pageContainer: {
76
+ paddingHorizontal: 8,
77
+ },
78
+ headerTitle: {
79
+ color: theme?.colors?.typography,
80
+ fontSize: 30,
81
+ fontWeight: "bold",
82
+ marginBottom: 16,
83
+ },
84
+ apiStatusCard: {
85
+ marginBottom: 24,
86
+ borderRadius: 8,
87
+ borderWidth: 1,
88
+ borderColor: theme?.colors?.border,
89
+ padding: 16,
90
+ },
91
+ cardTitle: {
92
+ marginBottom: 12,
93
+ fontWeight: "500",
94
+ color: theme?.colors?.typography,
95
+ },
96
+ apiStatusRow: {
97
+ flexDirection: "row",
98
+ alignItems: "center",
99
+ gap: 8,
100
+ },
101
+ statusIndicatorDot: {
102
+ height: 12,
103
+ width: 12,
104
+ borderRadius: 9999,
105
+ },
106
+ statusIndicatorGreen: {
107
+ backgroundColor: theme.colors.success,
108
+ },
109
+ statusIndicatorRed: {
110
+ backgroundColor: theme.colors.destructive,
111
+ },
112
+ statusText: {
113
+ color: theme?.colors?.typography,
114
+ },
115
+ }));
@@ -0,0 +1,48 @@
1
+ import { ScrollViewStyleReset } from 'expo-router/html';
2
+
3
+ import '../unistyles';
4
+
5
+ // This file is web-only and used to configure the root HTML for every
6
+ // web page during static rendering.
7
+ // The contents of this function only run in Node.js environments and
8
+ // do not have access to the DOM or browser APIs.
9
+ export default function Root({ children }: { children: React.ReactNode }) {
10
+ return (
11
+ <html lang="en">
12
+ <head>
13
+ <meta charSet="utf-8" />
14
+ <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
15
+
16
+ {/*
17
+ This viewport disables scaling which makes the mobile website act more like a native app.
18
+ However this does reduce built-in accessibility. If you want to enable scaling, use this instead:
19
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
20
+ */}
21
+ <meta
22
+ name="viewport"
23
+ content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1.00001,viewport-fit=cover"
24
+ />
25
+ {/*
26
+ Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
27
+ However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
28
+ */}
29
+ <ScrollViewStyleReset />
30
+
31
+ {/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */}
32
+ <style dangerouslySetInnerHTML={{ __html: responsiveBackground }} />
33
+ {/* Add any additional <head> elements that you want globally available on web... */}
34
+ </head>
35
+ <body>{children}</body>
36
+ </html>
37
+ );
38
+ }
39
+
40
+ const responsiveBackground = `
41
+ body {
42
+ background-color: #fff;
43
+ }
44
+ @media (prefers-color-scheme: dark) {
45
+ body {
46
+ background-color: #000;
47
+ }
48
+ }`;
@@ -0,0 +1,34 @@
1
+ import { Link, Stack } from "expo-router";
2
+ import { Text } from "react-native";
3
+ import { StyleSheet } from "react-native-unistyles";
4
+
5
+ import { Container } from "@/components/container";
6
+
7
+ export default function NotFoundScreen() {
8
+ return (
9
+ <>
10
+ <Stack.Screen options={{ title: "Oops!" }} />
11
+ <Container>
12
+ <Text style={styles.title}>This screen doesn't exist.</Text>
13
+ <Link href="/" style={styles.link}>
14
+ <Text style={styles.linkText}>Go to home screen!</Text>
15
+ </Link>
16
+ </Container>
17
+ </>
18
+ );
19
+ }
20
+
21
+ const styles = StyleSheet.create((theme) => ({
22
+ title: {
23
+ fontSize: 20,
24
+ fontWeight: "bold",
25
+ color: theme.colors.typography,
26
+ },
27
+ link: {
28
+ marginTop: 16,
29
+ paddingVertical: 16,
30
+ },
31
+ linkText: {
32
+ fontSize: 14,
33
+ },
34
+ }));