diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..caccf6c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,28 @@ +## Runtime & Framework +- Expo SDK `~54.0.23` with `newArchEnabled: true`, typed routes, and React Compiler enabled. +- React `19.1.0`, React Native `0.81.5`, web output via `react-native-web ~0.21.0` and `react-dom 19.1.0`. + +## Routing & Navigation +- `expo-router ~6.0.14` as the entry point (`main: expo-router/entry`) and registered in app.json plugins. +- React Navigation 7.x stack: `@react-navigation/native ^7.1.8`, `@react-navigation/bottom-tabs ^7.4.0`, `@react-navigation/elements ^2.6.3`, plus `react-native-screens ~4.16.0` and `react-native-safe-area-context ~5.4.0`. + +## Styling +- NativeWind `^4.2.1`; Babel uses `babel-preset-expo` with `jsxImportSource: "nativewind"` and `nativewind/babel`; Metro is wrapped with `withNativeWind(config, { input: "./global.css" })`. +- TailwindCSS `^3.4.18`; `global.css` contains `@tailwind` directives. `tailwind.config.js` currently scans only `App.tsx` and `components/**/*`, so expand `content` if writing classes in `app/**/*`, `hooks/**/*`, etc. + +## Animation & Gestures +- `react-native-reanimated ~3.17.5` (use updated hooks such as `useScrollViewOffset`). +- `react-native-gesture-handler ~2.28.0`, `expo-haptics ~15.0.7`, `expo-image ~3.0.10`. + +## State & Utilities +- Zustand `^5.0.8`. + +## Tooling +- TypeScript `~5.9.2` with `strict: true` and alias `@/* -> ./*`; `nativewind-env.d.ts` adds typings. +- ESLint `^9.25.0` + `eslint-config-expo ~10.0.0`, formatting with `prettier-plugin-tailwindcss ^0.5.14`. +- Metro config only customizes NativeWind; Babel preset remains `babel-preset-expo`. + +## Platform Config +- app.json: slug/name `kopi-shop`, scheme `kopishop`, portrait orientation, automatic UI mode. +- Splash and icons handled by `expo-splash-screen` plugin and Android adaptive icon assets. +- Web bundler is Metro with `"output": "static"`. diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 54e11d0..d10b14a 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -1,35 +1,175 @@ import { Tabs } from 'expo-router'; import React from 'react'; +import { Platform, Pressable, StyleSheet, View } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import type { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs'; import { HapticTab } from '@/components/haptic-tab'; import { IconSymbol } from '@/components/ui/icon-symbol'; import { Colors } from '@/constants/theme'; import { useColorScheme } from '@/hooks/use-color-scheme'; +const BAR_SIDE_INSET = 12; +const BAR_RADIUS = 22; +const BAR_HEIGHT_IOS = 86; +const BAR_HEIGHT_ANDROID = 72; +const BAR_BOTTOM_OFFSET = -6; +const BAR_PADDING_TOP = 4; +const BAR_PADDING_BOTTOM_EXTRA = 4; +const TAB_ICON_SIZE = 18; +const TAB_LABEL_SIZE = 10; +const TAB_ITEM_PADDING_VERTICAL = 0; + +const KOPI_SIZE = 82; +const KOPI_ICON_SIZE = 38; +const KOPI_LIFT = 20; +const KOPI_BORDER_WIDTH = 6; + +const KopiTabButton = ({ onPress, accessibilityState }: BottomTabBarButtonProps) => { + const colorScheme = useColorScheme(); + const theme = colorScheme ?? 'light'; + const isFocused = accessibilityState?.selected; + + return ( + [ + styles.kopiButtonContainer, + { transform: [{ scale: pressed ? 0.95 : 1 }] }, + ]}> + + + + + ); +}; + export default function TabLayout() { const colorScheme = useColorScheme(); + const insets = useSafeAreaInsets(); + const theme = colorScheme ?? 'light'; return ( ( + + ), + tabBarStyle: { + position: 'absolute', + left: BAR_SIDE_INSET, + right: BAR_SIDE_INSET, + backgroundColor: 'transparent', + height: (Platform.OS === 'ios' ? BAR_HEIGHT_IOS : BAR_HEIGHT_ANDROID) + insets.bottom, + paddingBottom: insets.bottom + BAR_PADDING_BOTTOM_EXTRA, + paddingTop: BAR_PADDING_TOP, + bottom: BAR_BOTTOM_OFFSET, + borderTopWidth: 0, + }, + tabBarLabelStyle: { + fontSize: TAB_LABEL_SIZE, + fontWeight: '600', + }, + tabBarItemStyle: { + flex: 1, + paddingVertical: TAB_ITEM_PADDING_VERTICAL, + }, }}> , + tabBarIcon: ({ color }) => , + }} + /> + , + }} + /> + null, + tabBarButton: (props) => , + tabBarItemStyle: { flex: 1, marginTop: -KOPI_LIFT }, }} /> , + title: 'Orders', + tabBarIcon: ({ color }) => , + }} + /> + , }} /> ); } + +const styles = StyleSheet.create({ + kopiButtonContainer: { + justifyContent: 'center', + alignItems: 'center', + marginTop: -KOPI_LIFT, + }, + kopiButtonCircle: { + width: KOPI_SIZE, + height: KOPI_SIZE, + borderRadius: KOPI_SIZE / 2, + justifyContent: 'center', + alignItems: 'center', + borderWidth: KOPI_BORDER_WIDTH, + shadowOffset: { + width: 0, + height: 6, + }, + shadowOpacity: 0.25, + shadowRadius: 6, + elevation: 8, + }, + tabBarBackground: { + flex: 1, + borderTopLeftRadius: BAR_RADIUS, + borderTopRightRadius: BAR_RADIUS, + borderTopWidth: 1, + borderWidth: 1, + shadowOffset: { width: 0, height: -2 }, + shadowOpacity: 0.08, + shadowRadius: 6, + elevation: 4, + overflow: 'hidden', + }, +}); diff --git a/app/(tabs)/explore.tsx b/app/(tabs)/explore.tsx deleted file mode 100644 index 71518f9..0000000 --- a/app/(tabs)/explore.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { Image } from 'expo-image'; -import { Platform, StyleSheet } from 'react-native'; - -import { Collapsible } from '@/components/ui/collapsible'; -import { ExternalLink } from '@/components/external-link'; -import ParallaxScrollView from '@/components/parallax-scroll-view'; -import { ThemedText } from '@/components/themed-text'; -import { ThemedView } from '@/components/themed-view'; -import { IconSymbol } from '@/components/ui/icon-symbol'; -import { Fonts } from '@/constants/theme'; - -export default function TabTwoScreen() { - return ( - - }> - - - Explore - - - This app includes example code to help you get started. - - - This app has two screens:{' '} - app/(tabs)/index.tsx and{' '} - app/(tabs)/explore.tsx - - - The layout file in app/(tabs)/_layout.tsx{' '} - sets up the tab navigator. - - - Learn more - - - - - You can open this project on Android, iOS, and the web. To open the web version, press{' '} - w in the terminal running this project. - - - - - For static images, you can use the @2x and{' '} - @3x suffixes to provide files for - different screen densities - - - - Learn more - - - - - This template has light and dark mode support. The{' '} - useColorScheme() hook lets you inspect - what the user's current color scheme is, and so you can adjust UI colors accordingly. - - - Learn more - - - - - This template includes an example of an animated component. The{' '} - components/HelloWave.tsx component uses - the powerful{' '} - - react-native-reanimated - {' '} - library to create a waving hand animation. - - {Platform.select({ - ios: ( - - The components/ParallaxScrollView.tsx{' '} - component provides a parallax effect for the header image. - - ), - })} - - - ); -} - -const styles = StyleSheet.create({ - headerImage: { - color: '#808080', - bottom: -90, - left: -35, - position: 'absolute', - }, - titleContainer: { - flexDirection: 'row', - gap: 8, - }, -}); diff --git a/app/(tabs)/favorites.tsx b/app/(tabs)/favorites.tsx new file mode 100644 index 0000000..1498f9d --- /dev/null +++ b/app/(tabs)/favorites.tsx @@ -0,0 +1,9 @@ +import { Text, View } from 'react-native'; + +export default function FavoritesScreen() { + return ( + + Favorites Screen + + ); +} diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 786b736..c24b44e 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,98 +1,9 @@ -import { Image } from 'expo-image'; -import { Platform, StyleSheet } from 'react-native'; - -import { HelloWave } from '@/components/hello-wave'; -import ParallaxScrollView from '@/components/parallax-scroll-view'; -import { ThemedText } from '@/components/themed-text'; -import { ThemedView } from '@/components/themed-view'; -import { Link } from 'expo-router'; +import { Text, View } from 'react-native'; export default function HomeScreen() { return ( - - }> - - Welcome! - - - - Step 1: Try it - - Edit app/(tabs)/index.tsx to see changes. - Press{' '} - - {Platform.select({ - ios: 'cmd + d', - android: 'cmd + m', - web: 'F12', - })} - {' '} - to open developer tools. - - - - - - Step 2: Explore - - - - alert('Action pressed')} /> - alert('Share pressed')} - /> - - alert('Delete pressed')} - /> - - - - - - {`Tap the Explore tab to learn more about what's included in this starter app.`} - - - - Step 3: Get a fresh start - - {`When you're ready, run `} - npm run reset-project to get a fresh{' '} - app directory. This will move the current{' '} - app to{' '} - app-example. - - - + + Home Screen + ); } - -const styles = StyleSheet.create({ - titleContainer: { - flexDirection: 'row', - alignItems: 'center', - gap: 8, - }, - stepContainer: { - gap: 8, - marginBottom: 8, - }, - reactLogo: { - height: 178, - width: 290, - bottom: 0, - left: 0, - position: 'absolute', - }, -}); diff --git a/app/(tabs)/kopi.tsx b/app/(tabs)/kopi.tsx new file mode 100644 index 0000000..36ecf97 --- /dev/null +++ b/app/(tabs)/kopi.tsx @@ -0,0 +1,9 @@ +import { Text, View } from 'react-native'; + +export default function KopiScreen() { + return ( + + Kopi Screen + + ); +} diff --git a/app/(tabs)/me.tsx b/app/(tabs)/me.tsx new file mode 100644 index 0000000..2e6add0 --- /dev/null +++ b/app/(tabs)/me.tsx @@ -0,0 +1,9 @@ +import { Text, View } from 'react-native'; + +export default function MeScreen() { + return ( + + Me Screen + + ); +} diff --git a/app/(tabs)/orders.tsx b/app/(tabs)/orders.tsx new file mode 100644 index 0000000..f3bfadc --- /dev/null +++ b/app/(tabs)/orders.tsx @@ -0,0 +1,9 @@ +import { Text, View } from 'react-native'; + +export default function OrdersScreen() { + return ( + + Orders Screen + + ); +} diff --git a/components/ui/icon-symbol.tsx b/components/ui/icon-symbol.tsx index b7ece6b..d8c6078 100644 --- a/components/ui/icon-symbol.tsx +++ b/components/ui/icon-symbol.tsx @@ -1,7 +1,7 @@ // Fallback for using MaterialIcons on Android and web. import MaterialIcons from '@expo/vector-icons/MaterialIcons'; -import { SymbolWeight, SymbolViewProps } from 'expo-symbols'; +import { SymbolViewProps, SymbolWeight } from 'expo-symbols'; import { ComponentProps } from 'react'; import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native'; @@ -18,6 +18,10 @@ const MAPPING = { 'paperplane.fill': 'send', 'chevron.left.forwardslash.chevron.right': 'code', 'chevron.right': 'chevron-right', + 'cup.and.saucer.fill': 'coffee', + 'list.bullet.clipboard.fill': 'receipt', + 'person.fill': 'person', + 'star.fill': 'star', } as IconMapping; /** diff --git a/constants/theme.ts b/constants/theme.ts index f06facd..6786d5b 100644 --- a/constants/theme.ts +++ b/constants/theme.ts @@ -5,25 +5,36 @@ import { Platform } from 'react-native'; -const tintColorLight = '#0a7ea4'; -const tintColorDark = '#fff'; + +const Palette = { + milkCoffee: '#F5F5F0', // Creamy Milk + darkCoffee: '#4A3B32', // Dark Coffee + lightCoffee: '#D7CCC8', // Light Coffee + mediumCoffee: '#8D6E63', // Medium Coffee + grey: '#BCAAA4', + white: '#FFFFFF', + black: '#000000', +}; export const Colors = { light: { - text: '#11181C', - background: '#fff', - tint: tintColorLight, - icon: '#687076', - tabIconDefault: '#687076', - tabIconSelected: tintColorLight, + text: Palette.darkCoffee, + background: Palette.milkCoffee, + tint: Palette.darkCoffee, + icon: Palette.mediumCoffee, + tabIconDefault: Palette.grey, + tabIconSelected: Palette.darkCoffee, + // Add palette for direct access if needed, though usually we use the semantic names above + ...Palette, }, dark: { text: '#ECEDEE', background: '#151718', - tint: tintColorDark, + tint: Palette.lightCoffee, icon: '#9BA1A6', tabIconDefault: '#9BA1A6', - tabIconSelected: tintColorDark, + tabIconSelected: Palette.lightCoffee, + ...Palette, }, }; diff --git a/global.css b/global.css index bd6213e..3cc8733 100644 --- a/global.css +++ b/global.css @@ -1,3 +1,13 @@ @tailwind base; @tailwind components; -@tailwind utilities; \ No newline at end of file +@tailwind utilities; + +@layer base { + :root { + --color-milk-coffee: #F5F5F0; + --color-dark-coffee: #4A3B32; + --color-light-coffee: #D7CCC8; + --color-medium-coffee: #8D6E63; + --color-grey: #BCAAA4; + } +} \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 5a6c92c..5b6bf81 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,10 +1,22 @@ /** @type {import('tailwindcss').Config} */ module.exports = { // NOTE: Update this to include the paths to all files that contain Nativewind classes. - content: ["./App.tsx", "./components/**/*.{js,jsx,ts,tsx}"], +content: [ + './App.tsx', + './app/**/*.{js,jsx,ts,tsx}', + './components/**/*.{js,jsx,ts,tsx}', +], presets: [require("nativewind/preset")], theme: { - extend: {}, + extend: { + colors: { + 'milk-coffee': 'var(--color-milk-coffee)', + 'dark-coffee': 'var(--color-dark-coffee)', + 'light-coffee': 'var(--color-light-coffee)', + 'medium-coffee': 'var(--color-medium-coffee)', + 'grey': 'var(--color-grey)', + }, + }, }, plugins: [], } \ No newline at end of file