rcgit

/ karakeep

Commit 2b720405

SHA 2b720405ae3a19ac78fbf3e7231394364ba83c99
Author MohamedBassem <me at mbassem dot com>
Author Date 2024-03-11 01:37 +0000
Committer MohamedBassem <me at mbassem dot com>
Commit Date 2024-03-11 01:37 +0000
Parent(s) 00dd4d0b549f (diff)
Tree 9fb5ab84f827

patch snapshot

mobile: Prepare to merge into main repo
File + - Graph
D .eslintrc.js +0 -4
D .gitignore +0 -35
D .npmrc +0 -1
D app.json +0 -34
D app/_layout.tsx +0 -14
D app/index.tsx +0 -12
D app/signin.tsx +0 -24
D assets/adaptive-icon.png +0 -0
D assets/favicon.png +0 -0
D assets/icon.png +0 -0
D assets/splash.png +0 -0
D babel.config.js +0 -9
D bun.lockb +0 -0
D components/Logo.tsx +0 -11
D components/ui/Button.tsx +0 -81
D components/ui/Input.tsx +0 -28
D globals.css +0 -80
D lib/utils.ts +0 -6
D metro.config.js +0 -7
D nativewind-env.d.ts +0 -1
D package.json +0 -41
A packages/mobile/.eslintrc.js +4 -0
A packages/mobile/.gitignore +35 -0
A packages/mobile/.npmrc +1 -0
A packages/mobile/app.json +34 -0
A packages/mobile/app/_layout.tsx +14 -0
A packages/mobile/app/index.tsx +12 -0
A packages/mobile/app/signin.tsx +24 -0
A packages/mobile/assets/adaptive-icon.png +0 -0
A packages/mobile/assets/favicon.png +0 -0
A packages/mobile/assets/icon.png +0 -0
A packages/mobile/assets/splash.png +0 -0
A packages/mobile/babel.config.js +9 -0
A packages/mobile/bun.lockb +0 -0
A packages/mobile/components/Logo.tsx +11 -0
A packages/mobile/components/ui/Button.tsx +81 -0
A packages/mobile/components/ui/Input.tsx +28 -0
A packages/mobile/globals.css +80 -0
A packages/mobile/lib/utils.ts +6 -0
A packages/mobile/metro.config.js +7 -0
A packages/mobile/nativewind-env.d.ts +1 -0
A packages/mobile/package.json +41 -0
A packages/mobile/tailwind.config.js +71 -0
A packages/mobile/tsconfig.json +10 -0
D tailwind.config.js +0 -71
D tsconfig.json +0 -10
46 file(s) changed, 469 insertions(+), 469 deletions(-)

.eslintrc.js

diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index 53beac49..00000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,4 +0,0 @@
-module.exports = {
-  root: true,
-  extends: ["universe/native"],
-};

.gitignore

diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 05647d55..00000000
--- a/.gitignore
+++ /dev/null
@@ -1,35 +0,0 @@
-# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
-
-# dependencies
-node_modules/
-
-# Expo
-.expo/
-dist/
-web-build/
-
-# Native
-*.orig.*
-*.jks
-*.p8
-*.p12
-*.key
-*.mobileprovision
-
-# Metro
-.metro-health-check*
-
-# debug
-npm-debug.*
-yarn-debug.*
-yarn-error.*
-
-# macOS
-.DS_Store
-*.pem
-
-# local env files
-.env*.local
-
-# typescript
-*.tsbuildinfo

.npmrc

diff --git a/.npmrc b/.npmrc
deleted file mode 100644
index d67f3748..00000000
--- a/.npmrc
+++ /dev/null
@@ -1 +0,0 @@
-node-linker=hoisted

app.json

diff --git a/app.json b/app.json
deleted file mode 100644
index 9ebea4ea..00000000
--- a/app.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-  "expo": {
-    "name": "hoarder-mobile",
-    "slug": "hoarder-mobile",
-    "scheme": "hoarder",
-    "version": "1.0.0",
-    "orientation": "portrait",
-    "icon": "./assets/icon.png",
-    "userInterfaceStyle": "light",
-    "splash": {
-      "image": "./assets/splash.png",
-      "resizeMode": "contain",
-      "backgroundColor": "#ffffff"
-    },
-    "assetBundlePatterns": [
-      "**/*"
-    ],
-    "ios": {
-      "supportsTablet": true
-    },
-    "android": {
-      "adaptiveIcon": {
-        "foregroundImage": "./assets/adaptive-icon.png",
-        "backgroundColor": "#ffffff"
-      }
-    },
-    "web": {
-      "favicon": "./assets/favicon.png"
-    },
-    "plugins": [
-      "expo-router"
-    ]
-  }
-}

app/_layout.tsx

diff --git a/app/_layout.tsx b/app/_layout.tsx
deleted file mode 100644
index 7403c6ff..00000000
--- a/app/_layout.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import "@/globals.css";
-
-import { Slot } from "expo-router";
-import { StatusBar } from "expo-status-bar";
-import { View } from "react-native";
-
-export default function RootLayout() {
-  return (
-    <View className="w-full h-full bg-white">
-      <Slot />
-      <StatusBar style="auto" />
-    </View>
-  );
-}

app/index.tsx

diff --git a/app/index.tsx b/app/index.tsx
deleted file mode 100644
index e352ba54..00000000
--- a/app/index.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Link } from "expo-router";
-import { View } from "react-native";
-
-export default function App() {
-  return (
-    <View className="flex-1 items-center justify-center bg-white">
-      <Link href="/signin" className="">
-        Signin
-      </Link>
-    </View>
-  );
-}

app/signin.tsx

diff --git a/app/signin.tsx b/app/signin.tsx
deleted file mode 100644
index f500e36e..00000000
--- a/app/signin.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { View, Text } from "react-native";
-
-import Logo from "@/components/Logo";
-import { Button } from "@/components/ui/Button";
-import { Input } from "@/components/ui/Input";
-
-export default function Signin() {
-  return (
-    <View className="container justify-center h-full flex flex-col gap-2">
-      <View className="items-center">
-        <Logo />
-      </View>
-      <View className="gap-2">
-        <Text className="font-bold">Email</Text>
-        <Input className="w-full" placeholder="Email" />
-      </View>
-      <View className="gap-2">
-        <Text className="font-bold">Password</Text>
-        <Input className="w-full" placeholder="Password" secureTextEntry />
-      </View>
-      <Button className="w-full" label="Sign In" />
-    </View>
-  );
-}

Binary file assets/adaptive-icon.png changed

Binary file assets/favicon.png changed

Binary file assets/icon.png changed

Binary file assets/splash.png changed

babel.config.js

diff --git a/babel.config.js b/babel.config.js
deleted file mode 100644
index f3c649bb..00000000
--- a/babel.config.js
+++ /dev/null
@@ -1,9 +0,0 @@
-module.exports = function (api) {
-  api.cache(true);
-  return {
-    presets: [
-      ["babel-preset-expo", { jsxImportSource: "nativewind" }],
-      "nativewind/babel",
-    ],
-  };
-};

Binary file bun.lockb changed

components/Logo.tsx

diff --git a/components/Logo.tsx b/components/Logo.tsx
deleted file mode 100644
index a15d8561..00000000
--- a/components/Logo.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { PackageOpen } from "lucide-react-native";
-import { View, Text } from "react-native";
-
-export default function Logo() {
-  return (
-    <View className="flex flex-row gap-2 justify-center items-center ">
-      <PackageOpen color="black" size={70} />
-      <Text className="text-5xl">Hoarder</Text>
-    </View>
-  );
-}

components/ui/Button.tsx

diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx
deleted file mode 100644
index 4c3cbc69..00000000
--- a/components/ui/Button.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import { type VariantProps, cva } from "class-variance-authority";
-import { Text, TouchableOpacity } from "react-native";
-
-import { cn } from "@/lib/utils";
-
-const buttonVariants = cva(
-  "flex flex-row items-center justify-center rounded-md",
-  {
-    variants: {
-      variant: {
-        default: "bg-primary",
-        secondary: "bg-secondary",
-        destructive: "bg-destructive",
-        ghost: "bg-slate-700",
-        link: "text-primary underline-offset-4",
-      },
-      size: {
-        default: "h-10 px-4",
-        sm: "h-8 px-2",
-        lg: "h-12 px-8",
-      },
-    },
-    defaultVariants: {
-      variant: "default",
-      size: "default",
-    },
-  },
-);
-
-const buttonTextVariants = cva("text-center font-medium", {
-  variants: {
-    variant: {
-      default: "text-primary-foreground",
-      secondary: "text-secondary-foreground",
-      destructive: "text-destructive-foreground",
-      ghost: "text-primary-foreground",
-      link: "text-primary-foreground underline",
-    },
-    size: {
-      default: "text-base",
-      sm: "text-sm",
-      lg: "text-xl",
-    },
-  },
-  defaultVariants: {
-    variant: "default",
-    size: "default",
-  },
-});
-
-interface ButtonProps
-  extends React.ComponentPropsWithoutRef<typeof TouchableOpacity>,
-    VariantProps<typeof buttonVariants> {
-  label: string;
-  labelClasses?: string;
-}
-function Button({
-  label,
-  labelClasses,
-  className,
-  variant,
-  size,
-  ...props
-}: ButtonProps) {
-  return (
-    <TouchableOpacity
-      className={cn(buttonVariants({ variant, size, className }))}
-      {...props}
-    >
-      <Text
-        className={cn(
-          buttonTextVariants({ variant, size, className: labelClasses }),
-        )}
-      >
-        {label}
-      </Text>
-    </TouchableOpacity>
-  );
-}
-
-export { Button, buttonVariants, buttonTextVariants };

components/ui/Input.tsx

diff --git a/components/ui/Input.tsx b/components/ui/Input.tsx
deleted file mode 100644
index 6fc90b8f..00000000
--- a/components/ui/Input.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { forwardRef } from "react";
-import { Text, TextInput, View } from "react-native";
-
-import { cn } from "@/lib/utils";
-
-export interface InputProps
-  extends React.ComponentPropsWithoutRef<typeof TextInput> {
-  label?: string;
-  labelClasses?: string;
-  inputClasses?: string;
-}
-
-const Input = forwardRef<React.ElementRef<typeof TextInput>, InputProps>(
-  ({ className, label, labelClasses, inputClasses, ...props }, ref) => (
-    <View className={cn("flex flex-col gap-1.5", className)}>
-      {label && <Text className={cn("text-base", labelClasses)}>{label}</Text>}
-      <TextInput
-        className={cn(
-          inputClasses,
-          "border border-input py-2.5 px-4 rounded-lg",
-        )}
-        {...props}
-      />
-    </View>
-  ),
-);
-
-export { Input };

globals.css

diff --git a/globals.css b/globals.css
deleted file mode 100644
index de1cf559..00000000
--- a/globals.css
+++ /dev/null
@@ -1,80 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-@layer base {
-  :root {
-    --background: 0 0% 100%;
-    --foreground: 222.2 47.4% 11.2%;
-
-    --muted: 210 40% 96.1%;
-    --muted-foreground: 215.4 16.3% 46.9%;
-
-    --popover: 0 0% 100%;
-    --popover-foreground: 222.2 47.4% 11.2%;
-
-    --border: 214.3 31.8% 91.4%;
-    --input: 214.3 31.8% 91.4%;
-
-    --card: 0 0% 100%;
-    --card-foreground: 222.2 47.4% 11.2%;
-
-    --primary: 222.2 47.4% 11.2%;
-    --primary-foreground: 210 40% 98%;
-
-    --secondary: 210 40% 96.1%;
-    --secondary-foreground: 222.2 47.4% 11.2%;
-
-    --accent: 210 40% 96.1%;
-    --accent-foreground: 222.2 47.4% 11.2%;
-
-    --destructive: 0 100% 50%;
-    --destructive-foreground: 210 40% 98%;
-
-    --ring: 215 20.2% 65.1%;
-
-    --radius: 0.5rem;
-  }
-
-  .dark:root {
-    --background: 224 71% 4%;
-    --foreground: 213 31% 91%;
-
-    --muted: 223 47% 11%;
-    --muted-foreground: 215.4 16.3% 56.9%;
-
-    --accent: 216 34% 17%;
-    --accent-foreground: 210 40% 98%;
-
-    --popover: 224 71% 4%;
-    --popover-foreground: 215 20.2% 65.1%;
-
-    --border: 216 34% 17%;
-    --input: 216 34% 17%;
-
-    --card: 224 71% 4%;
-    --card-foreground: 213 31% 91%;
-
-    --primary: 210 40% 98%;
-    --primary-foreground: 222.2 47.4% 1.2%;
-
-    --secondary: 222.2 47.4% 11.2%;
-    --secondary-foreground: 210 40% 98%;
-
-    --destructive: 0 63% 31%;
-    --destructive-foreground: 210 40% 98%;
-
-    --ring: 216 34% 17%;
-
-    --radius: 0.5rem;
-  }
-}
-
-@layer base {
-  * {
-    @apply border-border;
-  }
-  body {
-    @apply bg-background text-foreground;
-  }
-}

lib/utils.ts

diff --git a/lib/utils.ts b/lib/utils.ts
deleted file mode 100644
index 365058ce..00000000
--- a/lib/utils.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { type ClassValue, clsx } from "clsx";
-import { twMerge } from "tailwind-merge";
-
-export function cn(...inputs: ClassValue[]) {
-  return twMerge(clsx(inputs));
-}

metro.config.js

diff --git a/metro.config.js b/metro.config.js
deleted file mode 100644
index bbd30d1d..00000000
--- a/metro.config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const { getDefaultConfig } = require("expo/metro-config");
-const { withNativeWind } = require("nativewind/metro");
-
-/** @type {import('expo/metro-config').MetroConfig} */
-const config = getDefaultConfig(__dirname);
-
-module.exports = withNativeWind(config, { input: "./globals.css" });

nativewind-env.d.ts

diff --git a/nativewind-env.d.ts b/nativewind-env.d.ts
deleted file mode 100644
index a13e3136..00000000
--- a/nativewind-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-/// <reference types="nativewind/types" />

package.json

diff --git a/package.json b/package.json
deleted file mode 100644
index 4dbd0cf5..00000000
--- a/package.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  "name": "hoarder-mobile",
-  "version": "1.0.0",
-  "main": "expo-router/entry",
-  "scripts": {
-    "start": "expo start",
-    "android": "expo start --android",
-    "ios": "expo start --ios",
-    "web": "expo start --web",
-    "lint": "eslint ."
-  },
-  "dependencies": {
-    "class-variance-authority": "^0.7.0",
-    "clsx": "^2.1.0",
-    "expo": "~50.0.11",
-    "expo-constants": "~15.4.5",
-    "expo-linking": "~6.2.2",
-    "expo-router": "~3.4.8",
-    "expo-status-bar": "~1.11.1",
-    "lucide-react-native": "^0.354.0",
-    "nativewind": "^4.0.1",
-    "react": "18.2.0",
-    "react-native": "0.73.4",
-    "react-native-reanimated": "^3.8.0",
-    "react-native-safe-area-context": "4.8.2",
-    "react-native-screens": "~3.29.0",
-    "react-native-svg": "^15.1.0",
-    "tailwind-merge": "^2.2.1"
-  },
-  "devDependencies": {
-    "@babel/core": "^7.20.0",
-    "@types/react": "~18.2.45",
-    "ajv": "latest",
-    "eslint": "^8.57.0",
-    "eslint-config-universe": "^12.0.0",
-    "prettier": "^3.2.5",
-    "tailwindcss": "3.3.2",
-    "typescript": "^5.1.3"
-  },
-  "private": true
-}

packages/mobile/.eslintrc.js

diff --git a/packages/mobile/.eslintrc.js b/packages/mobile/.eslintrc.js
new file mode 100644
index 00000000..53beac49
--- /dev/null
+++ b/packages/mobile/.eslintrc.js
@@ -0,0 +1,4 @@
+module.exports = {
+  root: true,
+  extends: ["universe/native"],
+};

packages/mobile/.gitignore

diff --git a/packages/mobile/.gitignore b/packages/mobile/.gitignore
new file mode 100644
index 00000000..05647d55
--- /dev/null
+++ b/packages/mobile/.gitignore
@@ -0,0 +1,35 @@
+# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
+
+# dependencies
+node_modules/
+
+# Expo
+.expo/
+dist/
+web-build/
+
+# Native
+*.orig.*
+*.jks
+*.p8
+*.p12
+*.key
+*.mobileprovision
+
+# Metro
+.metro-health-check*
+
+# debug
+npm-debug.*
+yarn-debug.*
+yarn-error.*
+
+# macOS
+.DS_Store
+*.pem
+
+# local env files
+.env*.local
+
+# typescript
+*.tsbuildinfo

packages/mobile/.npmrc

diff --git a/packages/mobile/.npmrc b/packages/mobile/.npmrc
new file mode 100644
index 00000000..d67f3748
--- /dev/null
+++ b/packages/mobile/.npmrc
@@ -0,0 +1 @@
+node-linker=hoisted

packages/mobile/app.json

diff --git a/packages/mobile/app.json b/packages/mobile/app.json
new file mode 100644
index 00000000..9ebea4ea
--- /dev/null
+++ b/packages/mobile/app.json
@@ -0,0 +1,34 @@
+{
+  "expo": {
+    "name": "hoarder-mobile",
+    "slug": "hoarder-mobile",
+    "scheme": "hoarder",
+    "version": "1.0.0",
+    "orientation": "portrait",
+    "icon": "./assets/icon.png",
+    "userInterfaceStyle": "light",
+    "splash": {
+      "image": "./assets/splash.png",
+      "resizeMode": "contain",
+      "backgroundColor": "#ffffff"
+    },
+    "assetBundlePatterns": [
+      "**/*"
+    ],
+    "ios": {
+      "supportsTablet": true
+    },
+    "android": {
+      "adaptiveIcon": {
+        "foregroundImage": "./assets/adaptive-icon.png",
+        "backgroundColor": "#ffffff"
+      }
+    },
+    "web": {
+      "favicon": "./assets/favicon.png"
+    },
+    "plugins": [
+      "expo-router"
+    ]
+  }
+}

packages/mobile/app/_layout.tsx

diff --git a/packages/mobile/app/_layout.tsx b/packages/mobile/app/_layout.tsx
new file mode 100644
index 00000000..7403c6ff
--- /dev/null
+++ b/packages/mobile/app/_layout.tsx
@@ -0,0 +1,14 @@
+import "@/globals.css";
+
+import { Slot } from "expo-router";
+import { StatusBar } from "expo-status-bar";
+import { View } from "react-native";
+
+export default function RootLayout() {
+  return (
+    <View className="w-full h-full bg-white">
+      <Slot />
+      <StatusBar style="auto" />
+    </View>
+  );
+}

packages/mobile/app/index.tsx

diff --git a/packages/mobile/app/index.tsx b/packages/mobile/app/index.tsx
new file mode 100644
index 00000000..e352ba54
--- /dev/null
+++ b/packages/mobile/app/index.tsx
@@ -0,0 +1,12 @@
+import { Link } from "expo-router";
+import { View } from "react-native";
+
+export default function App() {
+  return (
+    <View className="flex-1 items-center justify-center bg-white">
+      <Link href="/signin" className="">
+        Signin
+      </Link>
+    </View>
+  );
+}

packages/mobile/app/signin.tsx

diff --git a/packages/mobile/app/signin.tsx b/packages/mobile/app/signin.tsx
new file mode 100644
index 00000000..f500e36e
--- /dev/null
+++ b/packages/mobile/app/signin.tsx
@@ -0,0 +1,24 @@
+import { View, Text } from "react-native";
+
+import Logo from "@/components/Logo";
+import { Button } from "@/components/ui/Button";
+import { Input } from "@/components/ui/Input";
+
+export default function Signin() {
+  return (
+    <View className="container justify-center h-full flex flex-col gap-2">
+      <View className="items-center">
+        <Logo />
+      </View>
+      <View className="gap-2">
+        <Text className="font-bold">Email</Text>
+        <Input className="w-full" placeholder="Email" />
+      </View>
+      <View className="gap-2">
+        <Text className="font-bold">Password</Text>
+        <Input className="w-full" placeholder="Password" secureTextEntry />
+      </View>
+      <Button className="w-full" label="Sign In" />
+    </View>
+  );
+}

Binary file packages/mobile/assets/adaptive-icon.png changed

Binary file packages/mobile/assets/favicon.png changed

Binary file packages/mobile/assets/icon.png changed

Binary file packages/mobile/assets/splash.png changed

packages/mobile/babel.config.js

diff --git a/packages/mobile/babel.config.js b/packages/mobile/babel.config.js
new file mode 100644
index 00000000..f3c649bb
--- /dev/null
+++ b/packages/mobile/babel.config.js
@@ -0,0 +1,9 @@
+module.exports = function (api) {
+  api.cache(true);
+  return {
+    presets: [
+      ["babel-preset-expo", { jsxImportSource: "nativewind" }],
+      "nativewind/babel",
+    ],
+  };
+};

Binary file packages/mobile/bun.lockb changed

packages/mobile/components/Logo.tsx

diff --git a/packages/mobile/components/Logo.tsx b/packages/mobile/components/Logo.tsx
new file mode 100644
index 00000000..a15d8561
--- /dev/null
+++ b/packages/mobile/components/Logo.tsx
@@ -0,0 +1,11 @@
+import { PackageOpen } from "lucide-react-native";
+import { View, Text } from "react-native";
+
+export default function Logo() {
+  return (
+    <View className="flex flex-row gap-2 justify-center items-center ">
+      <PackageOpen color="black" size={70} />
+      <Text className="text-5xl">Hoarder</Text>
+    </View>
+  );
+}

packages/mobile/components/ui/Button.tsx

diff --git a/packages/mobile/components/ui/Button.tsx b/packages/mobile/components/ui/Button.tsx
new file mode 100644
index 00000000..4c3cbc69
--- /dev/null
+++ b/packages/mobile/components/ui/Button.tsx
@@ -0,0 +1,81 @@
+import { type VariantProps, cva } from "class-variance-authority";
+import { Text, TouchableOpacity } from "react-native";
+
+import { cn } from "@/lib/utils";
+
+const buttonVariants = cva(
+  "flex flex-row items-center justify-center rounded-md",
+  {
+    variants: {
+      variant: {
+        default: "bg-primary",
+        secondary: "bg-secondary",
+        destructive: "bg-destructive",
+        ghost: "bg-slate-700",
+        link: "text-primary underline-offset-4",
+      },
+      size: {
+        default: "h-10 px-4",
+        sm: "h-8 px-2",
+        lg: "h-12 px-8",
+      },
+    },
+    defaultVariants: {
+      variant: "default",
+      size: "default",
+    },
+  },
+);
+
+const buttonTextVariants = cva("text-center font-medium", {
+  variants: {
+    variant: {
+      default: "text-primary-foreground",
+      secondary: "text-secondary-foreground",
+      destructive: "text-destructive-foreground",
+      ghost: "text-primary-foreground",
+      link: "text-primary-foreground underline",
+    },
+    size: {
+      default: "text-base",
+      sm: "text-sm",
+      lg: "text-xl",
+    },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+interface ButtonProps
+  extends React.ComponentPropsWithoutRef<typeof TouchableOpacity>,
+    VariantProps<typeof buttonVariants> {
+  label: string;
+  labelClasses?: string;
+}
+function Button({
+  label,
+  labelClasses,
+  className,
+  variant,
+  size,
+  ...props
+}: ButtonProps) {
+  return (
+    <TouchableOpacity
+      className={cn(buttonVariants({ variant, size, className }))}
+      {...props}
+    >
+      <Text
+        className={cn(
+          buttonTextVariants({ variant, size, className: labelClasses }),
+        )}
+      >
+        {label}
+      </Text>
+    </TouchableOpacity>
+  );
+}
+
+export { Button, buttonVariants, buttonTextVariants };

packages/mobile/components/ui/Input.tsx

diff --git a/packages/mobile/components/ui/Input.tsx b/packages/mobile/components/ui/Input.tsx
new file mode 100644
index 00000000..6fc90b8f
--- /dev/null
+++ b/packages/mobile/components/ui/Input.tsx
@@ -0,0 +1,28 @@
+import { forwardRef } from "react";
+import { Text, TextInput, View } from "react-native";
+
+import { cn } from "@/lib/utils";
+
+export interface InputProps
+  extends React.ComponentPropsWithoutRef<typeof TextInput> {
+  label?: string;
+  labelClasses?: string;
+  inputClasses?: string;
+}
+
+const Input = forwardRef<React.ElementRef<typeof TextInput>, InputProps>(
+  ({ className, label, labelClasses, inputClasses, ...props }, ref) => (
+    <View className={cn("flex flex-col gap-1.5", className)}>
+      {label && <Text className={cn("text-base", labelClasses)}>{label}</Text>}
+      <TextInput
+        className={cn(
+          inputClasses,
+          "border border-input py-2.5 px-4 rounded-lg",
+        )}
+        {...props}
+      />
+    </View>
+  ),
+);
+
+export { Input };

packages/mobile/globals.css

diff --git a/packages/mobile/globals.css b/packages/mobile/globals.css
new file mode 100644
index 00000000..de1cf559
--- /dev/null
+++ b/packages/mobile/globals.css
@@ -0,0 +1,80 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+  :root {
+    --background: 0 0% 100%;
+    --foreground: 222.2 47.4% 11.2%;
+
+    --muted: 210 40% 96.1%;
+    --muted-foreground: 215.4 16.3% 46.9%;
+
+    --popover: 0 0% 100%;
+    --popover-foreground: 222.2 47.4% 11.2%;
+
+    --border: 214.3 31.8% 91.4%;
+    --input: 214.3 31.8% 91.4%;
+
+    --card: 0 0% 100%;
+    --card-foreground: 222.2 47.4% 11.2%;
+
+    --primary: 222.2 47.4% 11.2%;
+    --primary-foreground: 210 40% 98%;
+
+    --secondary: 210 40% 96.1%;
+    --secondary-foreground: 222.2 47.4% 11.2%;
+
+    --accent: 210 40% 96.1%;
+    --accent-foreground: 222.2 47.4% 11.2%;
+
+    --destructive: 0 100% 50%;
+    --destructive-foreground: 210 40% 98%;
+
+    --ring: 215 20.2% 65.1%;
+
+    --radius: 0.5rem;
+  }
+
+  .dark:root {
+    --background: 224 71% 4%;
+    --foreground: 213 31% 91%;
+
+    --muted: 223 47% 11%;
+    --muted-foreground: 215.4 16.3% 56.9%;
+
+    --accent: 216 34% 17%;
+    --accent-foreground: 210 40% 98%;
+
+    --popover: 224 71% 4%;
+    --popover-foreground: 215 20.2% 65.1%;
+
+    --border: 216 34% 17%;
+    --input: 216 34% 17%;
+
+    --card: 224 71% 4%;
+    --card-foreground: 213 31% 91%;
+
+    --primary: 210 40% 98%;
+    --primary-foreground: 222.2 47.4% 1.2%;
+
+    --secondary: 222.2 47.4% 11.2%;
+    --secondary-foreground: 210 40% 98%;
+
+    --destructive: 0 63% 31%;
+    --destructive-foreground: 210 40% 98%;
+
+    --ring: 216 34% 17%;
+
+    --radius: 0.5rem;
+  }
+}
+
+@layer base {
+  * {
+    @apply border-border;
+  }
+  body {
+    @apply bg-background text-foreground;
+  }
+}

packages/mobile/lib/utils.ts

diff --git a/packages/mobile/lib/utils.ts b/packages/mobile/lib/utils.ts
new file mode 100644
index 00000000..365058ce
--- /dev/null
+++ b/packages/mobile/lib/utils.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from "clsx";
+import { twMerge } from "tailwind-merge";
+
+export function cn(...inputs: ClassValue[]) {
+  return twMerge(clsx(inputs));
+}

packages/mobile/metro.config.js

diff --git a/packages/mobile/metro.config.js b/packages/mobile/metro.config.js
new file mode 100644
index 00000000..bbd30d1d
--- /dev/null
+++ b/packages/mobile/metro.config.js
@@ -0,0 +1,7 @@
+const { getDefaultConfig } = require("expo/metro-config");
+const { withNativeWind } = require("nativewind/metro");
+
+/** @type {import('expo/metro-config').MetroConfig} */
+const config = getDefaultConfig(__dirname);
+
+module.exports = withNativeWind(config, { input: "./globals.css" });

packages/mobile/nativewind-env.d.ts

diff --git a/packages/mobile/nativewind-env.d.ts b/packages/mobile/nativewind-env.d.ts
new file mode 100644
index 00000000..a13e3136
--- /dev/null
+++ b/packages/mobile/nativewind-env.d.ts
@@ -0,0 +1 @@
+/// <reference types="nativewind/types" />

packages/mobile/package.json

diff --git a/packages/mobile/package.json b/packages/mobile/package.json
new file mode 100644
index 00000000..4dbd0cf5
--- /dev/null
+++ b/packages/mobile/package.json
@@ -0,0 +1,41 @@
+{
+  "name": "hoarder-mobile",
+  "version": "1.0.0",
+  "main": "expo-router/entry",
+  "scripts": {
+    "start": "expo start",
+    "android": "expo start --android",
+    "ios": "expo start --ios",
+    "web": "expo start --web",
+    "lint": "eslint ."
+  },
+  "dependencies": {
+    "class-variance-authority": "^0.7.0",
+    "clsx": "^2.1.0",
+    "expo": "~50.0.11",
+    "expo-constants": "~15.4.5",
+    "expo-linking": "~6.2.2",
+    "expo-router": "~3.4.8",
+    "expo-status-bar": "~1.11.1",
+    "lucide-react-native": "^0.354.0",
+    "nativewind": "^4.0.1",
+    "react": "18.2.0",
+    "react-native": "0.73.4",
+    "react-native-reanimated": "^3.8.0",
+    "react-native-safe-area-context": "4.8.2",
+    "react-native-screens": "~3.29.0",
+    "react-native-svg": "^15.1.0",
+    "tailwind-merge": "^2.2.1"
+  },
+  "devDependencies": {
+    "@babel/core": "^7.20.0",
+    "@types/react": "~18.2.45",
+    "ajv": "latest",
+    "eslint": "^8.57.0",
+    "eslint-config-universe": "^12.0.0",
+    "prettier": "^3.2.5",
+    "tailwindcss": "3.3.2",
+    "typescript": "^5.1.3"
+  },
+  "private": true
+}

packages/mobile/tailwind.config.js

diff --git a/packages/mobile/tailwind.config.js b/packages/mobile/tailwind.config.js
new file mode 100644
index 00000000..b49f9598
--- /dev/null
+++ b/packages/mobile/tailwind.config.js
@@ -0,0 +1,71 @@
+const { hairlineWidth } = require("nativewind/theme");
+
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+  content: ["./app/**/*.{js,jsx,ts,tsx}", "./components/**/*.{js,jsx,ts,tsx}"],
+  plugins: [],
+  presets: [require("nativewind/preset")],
+  theme: {
+    container: {
+      center: true,
+      padding: "2rem",
+      screens: {
+        "2xl": "1400px",
+      },
+    },
+    extend: {
+      colors: {
+        border: "hsl(var(--border))",
+        input: "hsl(var(--input))",
+        ring: "hsl(var(--ring))",
+        background: "hsl(var(--background))",
+        foreground: "hsl(var(--foreground))",
+        primary: {
+          DEFAULT: "hsl(var(--primary))",
+          foreground: "hsl(var(--primary-foreground))",
+        },
+        secondary: {
+          DEFAULT: "hsl(var(--secondary))",
+          foreground: "hsl(var(--secondary-foreground))",
+        },
+        destructive: {
+          DEFAULT: "hsl(var(--destructive))",
+          foreground: "hsl(var(--destructive-foreground))",
+        },
+        muted: {
+          DEFAULT: "hsl(var(--muted))",
+          foreground: "hsl(var(--muted-foreground))",
+        },
+        accent: {
+          DEFAULT: "hsl(var(--accent))",
+          foreground: "hsl(var(--accent-foreground))",
+        },
+        popover: {
+          DEFAULT: "hsl(var(--popover))",
+          foreground: "hsl(var(--popover-foreground))",
+        },
+        card: {
+          DEFAULT: "hsl(var(--card))",
+          foreground: "hsl(var(--card-foreground))",
+        },
+      },
+      borderWidth: {
+        hairline: hairlineWidth(),
+      },
+      keyframes: {
+        "accordion-down": {
+          from: { height: "0" },
+          to: { height: "var(--radix-accordion-content-height)" },
+        },
+        "accordion-up": {
+          from: { height: "var(--radix-accordion-content-height)" },
+          to: { height: "0" },
+        },
+      },
+      animation: {
+        "accordion-down": "accordion-down 0.2s ease-out",
+        "accordion-up": "accordion-up 0.2s ease-out",
+      },
+    },
+  },
+};

packages/mobile/tsconfig.json

diff --git a/packages/mobile/tsconfig.json b/packages/mobile/tsconfig.json
new file mode 100644
index 00000000..84d97cb0
--- /dev/null
+++ b/packages/mobile/tsconfig.json
@@ -0,0 +1,10 @@
+{
+  "extends": "expo/tsconfig.base",
+  "compilerOptions": {
+    "strict": true,
+    "baseUrl": ".",
+    "paths": {
+      "@/*": ["./*"]
+    }
+  }
+}

tailwind.config.js

diff --git a/tailwind.config.js b/tailwind.config.js
deleted file mode 100644
index b49f9598..00000000
--- a/tailwind.config.js
+++ /dev/null
@@ -1,71 +0,0 @@
-const { hairlineWidth } = require("nativewind/theme");
-
-/** @type {import('tailwindcss').Config} */
-module.exports = {
-  content: ["./app/**/*.{js,jsx,ts,tsx}", "./components/**/*.{js,jsx,ts,tsx}"],
-  plugins: [],
-  presets: [require("nativewind/preset")],
-  theme: {
-    container: {
-      center: true,
-      padding: "2rem",
-      screens: {
-        "2xl": "1400px",
-      },
-    },
-    extend: {
-      colors: {
-        border: "hsl(var(--border))",
-        input: "hsl(var(--input))",
-        ring: "hsl(var(--ring))",
-        background: "hsl(var(--background))",
-        foreground: "hsl(var(--foreground))",
-        primary: {
-          DEFAULT: "hsl(var(--primary))",
-          foreground: "hsl(var(--primary-foreground))",
-        },
-        secondary: {
-          DEFAULT: "hsl(var(--secondary))",
-          foreground: "hsl(var(--secondary-foreground))",
-        },
-        destructive: {
-          DEFAULT: "hsl(var(--destructive))",
-          foreground: "hsl(var(--destructive-foreground))",
-        },
-        muted: {
-          DEFAULT: "hsl(var(--muted))",
-          foreground: "hsl(var(--muted-foreground))",
-        },
-        accent: {
-          DEFAULT: "hsl(var(--accent))",
-          foreground: "hsl(var(--accent-foreground))",
-        },
-        popover: {
-          DEFAULT: "hsl(var(--popover))",
-          foreground: "hsl(var(--popover-foreground))",
-        },
-        card: {
-          DEFAULT: "hsl(var(--card))",
-          foreground: "hsl(var(--card-foreground))",
-        },
-      },
-      borderWidth: {
-        hairline: hairlineWidth(),
-      },
-      keyframes: {
-        "accordion-down": {
-          from: { height: "0" },
-          to: { height: "var(--radix-accordion-content-height)" },
-        },
-        "accordion-up": {
-          from: { height: "var(--radix-accordion-content-height)" },
-          to: { height: "0" },
-        },
-      },
-      animation: {
-        "accordion-down": "accordion-down 0.2s ease-out",
-        "accordion-up": "accordion-up 0.2s ease-out",
-      },
-    },
-  },
-};

tsconfig.json

diff --git a/tsconfig.json b/tsconfig.json
deleted file mode 100644
index 84d97cb0..00000000
--- a/tsconfig.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "extends": "expo/tsconfig.base",
-  "compilerOptions": {
-    "strict": true,
-    "baseUrl": ".",
-    "paths": {
-      "@/*": ["./*"]
-    }
-  }
-}