From 8963f777ee696a7c31e1b5774f574063bb16806b Mon Sep 17 00:00:00 2001 From: Seven Date: Mon, 9 Mar 2026 06:41:01 +0700 Subject: [PATCH] Add PostCSS configuration and skills lock file - Created a new PostCSS configuration file to integrate Tailwind CSS. - Added a skills lock file containing various Expo skills with their respective source and computed hashes. --- .agents/skills/building-native-ui/SKILL.md | 321 ++++++++++ .../references/animations.md | 220 +++++++ .../building-native-ui/references/controls.md | 270 ++++++++ .../references/form-sheet.md | 253 ++++++++ .../references/gradients.md | 106 +++ .../building-native-ui/references/icons.md | 213 ++++++ .../building-native-ui/references/media.md | 198 ++++++ .../references/route-structure.md | 229 +++++++ .../building-native-ui/references/search.md | 248 +++++++ .../building-native-ui/references/storage.md | 121 ++++ .../building-native-ui/references/tabs.md | 433 +++++++++++++ .../references/toolbar-and-headers.md | 284 ++++++++ .../references/visual-effects.md | 197 ++++++ .../references/webgpu-three.md | 605 ++++++++++++++++++ .../references/zoom-transitions.md | 158 +++++ .agents/skills/expo-api-routes/SKILL.md | 368 +++++++++++ .agents/skills/expo-cicd-workflows/SKILL.md | 92 +++ .../expo-cicd-workflows/scripts/fetch.js | 109 ++++ .../expo-cicd-workflows/scripts/package.json | 11 + .../expo-cicd-workflows/scripts/validate.js | 84 +++ .agents/skills/expo-deployment/SKILL.md | 190 ++++++ .../references/app-store-metadata.md | 479 ++++++++++++++ .../references/ios-app-store.md | 355 ++++++++++ .../expo-deployment/references/play-store.md | 246 +++++++ .../expo-deployment/references/testflight.md | 58 ++ .../expo-deployment/references/workflows.md | 200 ++++++ .agents/skills/expo-dev-client/SKILL.md | 164 +++++ .agents/skills/expo-tailwind-setup/SKILL.md | 480 ++++++++++++++ .agents/skills/native-data-fetching/SKILL.md | 507 +++++++++++++++ .../references/expo-router-loaders.md | 341 ++++++++++ .agents/skills/upgrading-expo/SKILL.md | 129 ++++ .../references/expo-av-to-audio.md | 132 ++++ .../references/expo-av-to-video.md | 160 +++++ .../upgrading-expo/references/native-tabs.md | 124 ++++ .../references/new-architecture.md | 79 +++ .../upgrading-expo/references/react-19.md | 79 +++ .../references/react-compiler.md | 59 ++ .agents/skills/use-dom/SKILL.md | 417 ++++++++++++ .github/copilot-instructions.md | 50 ++ app/_layout.tsx | 37 +- global.css | 31 + global.d.ts | 1 + index.js | 9 +- metro.config.js | 6 +- package.json | 18 +- pnpm-lock.yaml | 553 +++++++++++++--- postcss.config.mjs | 5 + skills-lock.json | 50 ++ 48 files changed, 9360 insertions(+), 119 deletions(-) create mode 100644 .agents/skills/building-native-ui/SKILL.md create mode 100644 .agents/skills/building-native-ui/references/animations.md create mode 100644 .agents/skills/building-native-ui/references/controls.md create mode 100644 .agents/skills/building-native-ui/references/form-sheet.md create mode 100644 .agents/skills/building-native-ui/references/gradients.md create mode 100644 .agents/skills/building-native-ui/references/icons.md create mode 100644 .agents/skills/building-native-ui/references/media.md create mode 100644 .agents/skills/building-native-ui/references/route-structure.md create mode 100644 .agents/skills/building-native-ui/references/search.md create mode 100644 .agents/skills/building-native-ui/references/storage.md create mode 100644 .agents/skills/building-native-ui/references/tabs.md create mode 100644 .agents/skills/building-native-ui/references/toolbar-and-headers.md create mode 100644 .agents/skills/building-native-ui/references/visual-effects.md create mode 100644 .agents/skills/building-native-ui/references/webgpu-three.md create mode 100644 .agents/skills/building-native-ui/references/zoom-transitions.md create mode 100644 .agents/skills/expo-api-routes/SKILL.md create mode 100644 .agents/skills/expo-cicd-workflows/SKILL.md create mode 100644 .agents/skills/expo-cicd-workflows/scripts/fetch.js create mode 100644 .agents/skills/expo-cicd-workflows/scripts/package.json create mode 100644 .agents/skills/expo-cicd-workflows/scripts/validate.js create mode 100644 .agents/skills/expo-deployment/SKILL.md create mode 100644 .agents/skills/expo-deployment/references/app-store-metadata.md create mode 100644 .agents/skills/expo-deployment/references/ios-app-store.md create mode 100644 .agents/skills/expo-deployment/references/play-store.md create mode 100644 .agents/skills/expo-deployment/references/testflight.md create mode 100644 .agents/skills/expo-deployment/references/workflows.md create mode 100644 .agents/skills/expo-dev-client/SKILL.md create mode 100644 .agents/skills/expo-tailwind-setup/SKILL.md create mode 100644 .agents/skills/native-data-fetching/SKILL.md create mode 100644 .agents/skills/native-data-fetching/references/expo-router-loaders.md create mode 100644 .agents/skills/upgrading-expo/SKILL.md create mode 100644 .agents/skills/upgrading-expo/references/expo-av-to-audio.md create mode 100644 .agents/skills/upgrading-expo/references/expo-av-to-video.md create mode 100644 .agents/skills/upgrading-expo/references/native-tabs.md create mode 100644 .agents/skills/upgrading-expo/references/new-architecture.md create mode 100644 .agents/skills/upgrading-expo/references/react-19.md create mode 100644 .agents/skills/upgrading-expo/references/react-compiler.md create mode 100644 .agents/skills/use-dom/SKILL.md create mode 100644 .github/copilot-instructions.md create mode 100644 global.css create mode 100644 global.d.ts create mode 100644 postcss.config.mjs create mode 100644 skills-lock.json diff --git a/.agents/skills/building-native-ui/SKILL.md b/.agents/skills/building-native-ui/SKILL.md new file mode 100644 index 0000000..9a9df4c --- /dev/null +++ b/.agents/skills/building-native-ui/SKILL.md @@ -0,0 +1,321 @@ +--- +name: building-native-ui +description: Complete guide for building beautiful apps with Expo Router. Covers fundamentals, styling, components, navigation, animations, patterns, and native tabs. +version: 1.0.1 +license: MIT +--- + +# Expo UI Guidelines + +## References + +Consult these resources as needed: + +``` +references/ + animations.md Reanimated: entering, exiting, layout, scroll-driven, gestures + controls.md Native iOS: Switch, Slider, SegmentedControl, DateTimePicker, Picker + form-sheet.md Form sheets in expo-router: configuration, footers and background interaction. + gradients.md CSS gradients via experimental_backgroundImage (New Arch only) + icons.md SF Symbols via expo-image (sf: source), names, animations, weights + media.md Camera, audio, video, and file saving + route-structure.md Route conventions, dynamic routes, groups, folder organization + search.md Search bar with headers, useSearch hook, filtering patterns + storage.md SQLite, AsyncStorage, SecureStore + tabs.md NativeTabs, migration from JS tabs, iOS 26 features + toolbar-and-headers.md Stack headers and toolbar buttons, menus, search (iOS only) + visual-effects.md Blur (expo-blur) and liquid glass (expo-glass-effect) + webgpu-three.md 3D graphics, games, GPU visualizations with WebGPU and Three.js + zoom-transitions.md Apple Zoom: fluid zoom transitions with Link.AppleZoom (iOS 18+) +``` + +## Running the App + +**CRITICAL: Always try Expo Go first before creating custom builds.** + +Most Expo apps work in Expo Go without any custom native code. Before running `npx expo run:ios` or `npx expo run:android`: + +1. **Start with Expo Go**: Run `npx expo start` and scan the QR code with Expo Go +2. **Check if features work**: Test your app thoroughly in Expo Go +3. **Only create custom builds when required** - see below + +### When Custom Builds Are Required + +You need `npx expo run:ios/android` or `eas build` ONLY when using: + +- **Local Expo modules** (custom native code in `modules/`) +- **Apple targets** (widgets, app clips, extensions via `@bacons/apple-targets`) +- **Third-party native modules** not included in Expo Go +- **Custom native configuration** that can't be expressed in `app.json` + +### When Expo Go Works + +Expo Go supports a huge range of features out of the box: + +- All `expo-*` packages (camera, location, notifications, etc.) +- Expo Router navigation +- Most UI libraries (reanimated, gesture handler, etc.) +- Push notifications, deep links, and more + +**If you're unsure, try Expo Go first.** Creating custom builds adds complexity, slower iteration, and requires Xcode/Android Studio setup. + +## Code Style + +- Be cautious of unterminated strings. Ensure nested backticks are escaped; never forget to escape quotes correctly. +- Always use import statements at the top of the file. +- Always use kebab-case for file names, e.g. `comment-card.tsx` +- Always remove old route files when moving or restructuring navigation +- Never use special characters in file names +- Configure tsconfig.json with path aliases, and prefer aliases over relative imports for refactors. + +## Routes + +See `./references/route-structure.md` for detailed route conventions. + +- Routes belong in the `app` directory. +- Never co-locate components, types, or utilities in the app directory. This is an anti-pattern. +- Ensure the app always has a route that matches "/", it may be inside a group route. + +## Library Preferences + +- Never use modules removed from React Native such as Picker, WebView, SafeAreaView, or AsyncStorage +- Never use legacy expo-permissions +- `expo-audio` not `expo-av` +- `expo-video` not `expo-av` +- `expo-image` with `source="sf:name"` for SF Symbols, not `expo-symbols` or `@expo/vector-icons` +- `react-native-safe-area-context` not react-native SafeAreaView +- `process.env.EXPO_OS` not `Platform.OS` +- `React.use` not `React.useContext` +- `expo-image` Image component instead of intrinsic element `img` +- `expo-glass-effect` for liquid glass backdrops + +## Responsiveness + +- Always wrap root component in a scroll view for responsiveness +- Use `` instead of `` for smarter safe area insets +- `contentInsetAdjustmentBehavior="automatic"` should be applied to FlatList and SectionList as well +- Use flexbox instead of Dimensions API +- ALWAYS prefer `useWindowDimensions` over `Dimensions.get()` to measure screen size + +## Behavior + +- Use expo-haptics conditionally on iOS to make more delightful experiences +- Use views with built-in haptics like `` from React Native and `@react-native-community/datetimepicker` +- When a route belongs to a Stack, its first child should almost always be a ScrollView with `contentInsetAdjustmentBehavior="automatic"` set +- When adding a `ScrollView` to the page it should almost always be the first component inside the route component +- Prefer `headerSearchBarOptions` in Stack.Screen options to add a search bar +- Use the `` prop on text containing data that could be copied +- Consider formatting large numbers like 1.4M or 38k +- Never use intrinsic elements like 'img' or 'div' unless in a webview or Expo DOM component + +# Styling + +Follow Apple Human Interface Guidelines. + +## General Styling Rules + +- Prefer flex gap over margin and padding styles +- Prefer padding over margin where possible +- Always account for safe area, either with stack headers, tabs, or ScrollView/FlatList `contentInsetAdjustmentBehavior="automatic"` +- Ensure both top and bottom safe area insets are accounted for +- Inline styles not StyleSheet.create unless reusing styles is faster +- Add entering and exiting animations for state changes +- Use `{ borderCurve: 'continuous' }` for rounded corners unless creating a capsule shape +- ALWAYS use a navigation stack title instead of a custom text element on the page +- When padding a ScrollView, use `contentContainerStyle` padding and gap instead of padding on the ScrollView itself (reduces clipping) +- CSS and Tailwind are not supported - use inline styles + +## Text Styling + +- Add the `selectable` prop to every `` element displaying important data or error messages +- Counters should use `{ fontVariant: 'tabular-nums' }` for alignment + +## Shadows + +Use CSS `boxShadow` style prop. NEVER use legacy React Native shadow or elevation styles. + +```tsx + +``` + +'inset' shadows are supported. + +# Navigation + +## Link + +Use `` from 'expo-router' for navigation between routes. + +```tsx +import { Link } from 'expo-router'; + +// Basic link + + +// Wrapping custom components + + ... + +``` + +Whenever possible, include a `` to follow iOS conventions. Add context menus and previews frequently to enhance navigation. + +## Stack + +- ALWAYS use `_layout.tsx` files to define stacks +- Use Stack from 'expo-router/stack' for native navigation stacks + +### Page Title + +Set the page title in Stack.Screen options: + +```tsx + +``` + +## Context Menus + +Add long press context menus to Link components: + +```tsx +import { Link } from "expo-router"; + + + + + + + + + + + + {}} /> + {}} + /> + + +; +``` + +## Link Previews + +Use link previews frequently to enhance navigation: + +```tsx + + + + + + + + +``` + +Link preview can be used with context menus. + +## Modal + +Present a screen as a modal: + +```tsx + +``` + +Prefer this to building a custom modal component. + +## Sheet + +Present a screen as a dynamic form sheet: + +```tsx + +``` + +- Using `contentStyle: { backgroundColor: "transparent" }` makes the background liquid glass on iOS 26+. + +## Common route structure + +A standard app layout with tabs and stacks inside each tab: + +``` +app/ + _layout.tsx — + (index,search)/ + _layout.tsx — + index.tsx — Main list + search.tsx — Search view +``` + +```tsx +// app/_layout.tsx +import { NativeTabs, Icon, Label } from "expo-router/unstable-native-tabs"; +import { Theme } from "../components/theme"; + +export default function Layout() { + return ( + + + + + + + + + + ); +} +``` + +Create a shared group route so both tabs can push common screens: + +```tsx +// app/(index,search)/_layout.tsx +import { Stack } from "expo-router/stack"; +import { PlatformColor } from "react-native"; + +export default function Layout({ segment }) { + const screen = segment.match(/\((.*)\)/)?.[1]!; + const titles: Record = { index: "Items", search: "Search" }; + + return ( + + + + + ); +} +``` diff --git a/.agents/skills/building-native-ui/references/animations.md b/.agents/skills/building-native-ui/references/animations.md new file mode 100644 index 0000000..657cad8 --- /dev/null +++ b/.agents/skills/building-native-ui/references/animations.md @@ -0,0 +1,220 @@ +# Animations + +Use Reanimated v4. Avoid React Native's built-in Animated API. + +## Entering and Exiting Animations + +Use Animated.View with entering and exiting animations. Layout animations can animate state changes. + +```tsx +import Animated, { + FadeIn, + FadeOut, + LinearTransition, +} from "react-native-reanimated"; + +function App() { + return ( + + ); +} +``` + +## On-Scroll Animations + +Create high-performance scroll animations using Reanimated's hooks: + +```tsx +import Animated, { + useAnimatedRef, + useScrollViewOffset, + useAnimatedStyle, + interpolate, +} from "react-native-reanimated"; + +function Page() { + const ref = useAnimatedRef(); + const scroll = useScrollViewOffset(ref); + + const style = useAnimatedStyle(() => ({ + opacity: interpolate(scroll.value, [0, 30], [0, 1], "clamp"), + })); + + return ( + + + + ); +} +``` + +## Common Animation Presets + +### Entering Animations + +- `FadeIn`, `FadeInUp`, `FadeInDown`, `FadeInLeft`, `FadeInRight` +- `SlideInUp`, `SlideInDown`, `SlideInLeft`, `SlideInRight` +- `ZoomIn`, `ZoomInUp`, `ZoomInDown` +- `BounceIn`, `BounceInUp`, `BounceInDown` + +### Exiting Animations + +- `FadeOut`, `FadeOutUp`, `FadeOutDown`, `FadeOutLeft`, `FadeOutRight` +- `SlideOutUp`, `SlideOutDown`, `SlideOutLeft`, `SlideOutRight` +- `ZoomOut`, `ZoomOutUp`, `ZoomOutDown` +- `BounceOut`, `BounceOutUp`, `BounceOutDown` + +### Layout Animations + +- `LinearTransition` — Smooth linear interpolation +- `SequencedTransition` — Sequenced property changes +- `FadingTransition` — Fade between states + +## Customizing Animations + +```tsx + +``` + +### Modifiers + +```tsx +// Duration in milliseconds +FadeIn.duration(300); + +// Delay before starting +FadeIn.delay(100); + +// Spring physics +FadeIn.springify(); +FadeIn.springify().damping(15).stiffness(100); + +// Easing curves +FadeIn.easing(Easing.bezier(0.25, 0.1, 0.25, 1)); + +// Chaining +FadeInDown.duration(400).delay(200).springify(); +``` + +## Shared Value Animations + +For imperative control over animations: + +```tsx +import { + useSharedValue, + withSpring, + withTiming, +} from "react-native-reanimated"; + +const offset = useSharedValue(0); + +// Spring animation +offset.value = withSpring(100); + +// Timing animation +offset.value = withTiming(100, { duration: 300 }); + +// Use in styles +const style = useAnimatedStyle(() => ({ + transform: [{ translateX: offset.value }], +})); +``` + +## Gesture Animations + +Combine with React Native Gesture Handler: + +```tsx +import { Gesture, GestureDetector } from "react-native-gesture-handler"; +import Animated, { + useSharedValue, + useAnimatedStyle, + withSpring, +} from "react-native-reanimated"; + +function DraggableBox() { + const translateX = useSharedValue(0); + const translateY = useSharedValue(0); + + const gesture = Gesture.Pan() + .onUpdate((e) => { + translateX.value = e.translationX; + translateY.value = e.translationY; + }) + .onEnd(() => { + translateX.value = withSpring(0); + translateY.value = withSpring(0); + }); + + const style = useAnimatedStyle(() => ({ + transform: [ + { translateX: translateX.value }, + { translateY: translateY.value }, + ], + })); + + return ( + + + + ); +} +``` + +## Keyboard Animations + +Animate with keyboard height changes: + +```tsx +import Animated, { + useAnimatedKeyboard, + useAnimatedStyle, +} from "react-native-reanimated"; + +function KeyboardAwareView() { + const keyboard = useAnimatedKeyboard(); + + const style = useAnimatedStyle(() => ({ + paddingBottom: keyboard.height.value, + })); + + return {/* content */}; +} +``` + +## Staggered List Animations + +Animate list items with delays: + +```tsx +{ + items.map((item, index) => ( + + + + )); +} +``` + +## Best Practices + +- Add entering and exiting animations for state changes +- Use layout animations when items are added/removed from lists +- Use `useAnimatedStyle` for scroll-driven animations +- Prefer `interpolate` with "clamp" for bounded values +- You can't pass PlatformColors to reanimated views or styles; use static colors instead +- Keep animations under 300ms for responsive feel +- Use spring animations for natural movement +- Avoid animating layout properties (width, height) when possible — prefer transforms diff --git a/.agents/skills/building-native-ui/references/controls.md b/.agents/skills/building-native-ui/references/controls.md new file mode 100644 index 0000000..762fe20 --- /dev/null +++ b/.agents/skills/building-native-ui/references/controls.md @@ -0,0 +1,270 @@ +# Native Controls + +Native iOS controls provide built-in haptics, accessibility, and platform-appropriate styling. + +## Switch + +Use for binary on/off settings. Has built-in haptics. + +```tsx +import { Switch } from "react-native"; +import { useState } from "react"; + +const [enabled, setEnabled] = useState(false); + +; +``` + +### Customization + +```tsx + +``` + +## Segmented Control + +Use for non-navigational tabs or mode selection. Avoid changing default colors. + +```tsx +import SegmentedControl from "@react-native-segmented-control/segmented-control"; +import { useState } from "react"; + +const [index, setIndex] = useState(0); + + setIndex(nativeEvent.selectedSegmentIndex)} +/>; +``` + +### Rules + +- Maximum 4 options — use a picker for more +- Keep labels short (1-2 words) +- Avoid custom colors — native styling adapts to dark mode + +### With Icons (iOS 14+) + +```tsx + setIndex(nativeEvent.selectedSegmentIndex)} +/> +``` + +## Slider + +Continuous value selection. + +```tsx +import Slider from "@react-native-community/slider"; +import { useState } from "react"; + +const [value, setValue] = useState(0.5); + +; +``` + +### Customization + +```tsx + +``` + +### Discrete Steps + +```tsx + +``` + +## Date/Time Picker + +Compact pickers with popovers. Has built-in haptics. + +```tsx +import DateTimePicker from "@react-native-community/datetimepicker"; +import { useState } from "react"; + +const [date, setDate] = useState(new Date()); + + { + if (selectedDate) setDate(selectedDate); + }} + mode="datetime" +/>; +``` + +### Modes + +- `date` — Date only +- `time` — Time only +- `datetime` — Date and time + +### Display Styles + +```tsx +// Compact inline (default) + + +// Spinner wheel + + +// Full calendar + +``` + +### Time Intervals + +```tsx + +``` + +### Min/Max Dates + +```tsx + +``` + +## Stepper + +Increment/decrement numeric values. + +```tsx +import { Stepper } from "react-native"; +import { useState } from "react"; + +const [count, setCount] = useState(0); + +; +``` + +## TextInput + +Native text input with various keyboard types. + +```tsx +import { TextInput } from "react-native"; + + +``` + +### Keyboard Types + +```tsx +// Email + + +// Phone + + +// Number + + +// Password + + +// Search + +``` + +### Multiline + +```tsx + +``` + +## Picker (Wheel) + +For selection from many options (5+ items). + +```tsx +import { Picker } from "@react-native-picker/picker"; +import { useState } from "react"; + +const [selected, setSelected] = useState("js"); + + + + + + +; +``` + +## Best Practices + +- **Haptics**: Switch and DateTimePicker have built-in haptics — don't add extra +- **Accessibility**: Native controls have proper accessibility labels by default +- **Dark Mode**: Avoid custom colors — native styling adapts automatically +- **Spacing**: Use consistent padding around controls (12-16pt) +- **Labels**: Place labels above or to the left of controls +- **Grouping**: Group related controls in sections with headers diff --git a/.agents/skills/building-native-ui/references/form-sheet.md b/.agents/skills/building-native-ui/references/form-sheet.md new file mode 100644 index 0000000..1ed80fb --- /dev/null +++ b/.agents/skills/building-native-ui/references/form-sheet.md @@ -0,0 +1,253 @@ +# Form Sheets in Expo Router + +This skill covers implementing form sheets with footers using Expo Router's Stack navigator and react-native-screens. + +## Overview + +Form sheets are modal presentations that appear as a card sliding up from the bottom of the screen. They're ideal for: + +- Quick actions and confirmations +- Settings panels +- Login/signup flows +- Action sheets with custom content + +**Requirements:** + +- Expo Router Stack navigator + +## Basic Usage + +### Form Sheet with Footer + +Configure the Stack.Screen with transparent backgrounds and sheet presentation: + +```tsx +// app/_layout.tsx +import { Stack } from "expo-router"; + +export default function Layout() { + return ( + + + + + + + ); +} +``` + +### Form Sheet Screen Content + +> Requires Expo SDK 55 or later. + +Use `flex: 1` to allow the content to fill available space, enabling footer positioning: + +```tsx +// app/about.tsx +import { View, Text, StyleSheet } from "react-native"; + +export default function AboutSheet() { + return ( + + {/* Main content */} + + Sheet Content + + + {/* Footer - stays at bottom */} + + Footer Content + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + content: { + flex: 1, + padding: 16, + }, + footer: { + padding: 16, + }, +}); +``` + +### Formsheet with interactive content below + +Use `sheetLargestUndimmedDetentIndex` (zero-indexed) to keep content behind the form sheet interactive — e.g. letting users pan a map beneath it. Setting it to `1` allows interaction at the first two detents but dims on the third. + +```tsx +// app/_layout.tsx +import { Stack } from 'expo-router'; + +export default function Layout() { + return ( + + + + + ) +} +``` + +## Key Options + +| Option | Type | Description | +| --------------------- | ---------- | ----------------------------------------------------------- | +| `presentation` | `string` | Set to `'formSheet'` for sheet presentation | +| `sheetGrabberVisible` | `boolean` | Shows the drag handle at the top of the sheet | +| `sheetAllowedDetents` | `number[]` | Array of detent heights (0-1 range, e.g., `[0.25]` for 25%) | +| `headerTransparent` | `boolean` | Makes header background transparent | +| `contentStyle` | `object` | Style object for the screen content container | +| `title` | `string` | Screen title (set to `''` for no title) | + +## Common Detent Values + +- `[0.25]` - Quarter sheet (compact actions) +- `[0.5]` - Half sheet (medium content) +- `[0.75]` - Three-quarter sheet (detailed forms) +- `[0.25, 0.5, 1]` - Multiple stops (expandable sheet) + +## Complete Example + +```tsx +// _layout.tsx +import { Stack } from "expo-router"; + +export default function Layout() { + return ( + + + + + + + + + ); +} +``` + +```tsx +// app/confirm.tsx +import { View, Text, Pressable, StyleSheet } from "react-native"; +import { router } from "expo-router"; + +export default function ConfirmSheet() { + return ( + + + Confirm Action + + Are you sure you want to proceed? + + + + + router.back()}> + Cancel + + router.back()}> + Confirm + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + content: { + flex: 1, + padding: 20, + alignItems: "center", + justifyContent: "center", + }, + title: { + fontSize: 18, + fontWeight: "600", + marginBottom: 8, + }, + description: { + fontSize: 14, + color: "#666", + textAlign: "center", + }, + footer: { + flexDirection: "row", + padding: 16, + gap: 12, + }, + cancelButton: { + flex: 1, + padding: 14, + borderRadius: 10, + backgroundColor: "#f0f0f0", + alignItems: "center", + }, + cancelText: { + fontSize: 16, + fontWeight: "500", + }, + confirmButton: { + flex: 1, + padding: 14, + borderRadius: 10, + backgroundColor: "#007AFF", + alignItems: "center", + }, + confirmText: { + fontSize: 16, + fontWeight: "500", + color: "white", + }, +}); +``` + +## Troubleshooting + +### Content not filling sheet + +Make sure the root View uses `flex: 1`: + +```tsx +{/* content */} +``` + +### Sheet background showing through + +Set `contentStyle: { backgroundColor: 'transparent' }` in options and style your content container with the desired background color instead. diff --git a/.agents/skills/building-native-ui/references/gradients.md b/.agents/skills/building-native-ui/references/gradients.md new file mode 100644 index 0000000..329600d --- /dev/null +++ b/.agents/skills/building-native-ui/references/gradients.md @@ -0,0 +1,106 @@ +# CSS Gradients + +> **New Architecture Only**: CSS gradients require React Native's New Architecture (Fabric). They are not available in the old architecture or Expo Go. + +Use CSS gradients with the `experimental_backgroundImage` style property. + +## Linear Gradients + +```tsx +// Top to bottom + + +// Left to right + + +// Diagonal + + +// Using degrees + +``` + +## Radial Gradients + +```tsx +// Circle at center + + +// Ellipse + + +// Positioned + +``` + +## Multiple Gradients + +Stack multiple gradients by comma-separating them: + +```tsx + +``` + +## Common Patterns + +### Overlay on Image + +```tsx + + + + +``` + +### Frosted Glass Effect + +```tsx + +``` + +### Button Gradient + +```tsx + + Submit + +``` + +## Important Notes + +- Do NOT use `expo-linear-gradient` — use CSS gradients instead +- Gradients are strings, not objects +- Use `rgba()` for transparency, or `transparent` keyword +- Color stops use percentages (0%, 50%, 100%) +- Direction keywords: `to top`, `to bottom`, `to left`, `to right`, `to top left`, etc. +- Degree values: `45deg`, `90deg`, `135deg`, etc. diff --git a/.agents/skills/building-native-ui/references/icons.md b/.agents/skills/building-native-ui/references/icons.md new file mode 100644 index 0000000..eebf674 --- /dev/null +++ b/.agents/skills/building-native-ui/references/icons.md @@ -0,0 +1,213 @@ +# Icons (SF Symbols) + +Use SF Symbols for native feel. Never use FontAwesome or Ionicons. + +## Basic Usage + +```tsx +import { SymbolView } from "expo-symbols"; +import { PlatformColor } from "react-native"; + +; +``` + +## Props + +```tsx + +``` + +## Common Icons + +### Navigation & Actions +- `house.fill` - home +- `gear` - settings +- `magnifyingglass` - search +- `plus` - add +- `xmark` - close +- `chevron.left` - back +- `chevron.right` - forward +- `arrow.left` - back arrow +- `arrow.right` - forward arrow + +### Media +- `play.fill` - play +- `pause.fill` - pause +- `stop.fill` - stop +- `backward.fill` - rewind +- `forward.fill` - fast forward +- `speaker.wave.2.fill` - volume +- `speaker.slash.fill` - mute + +### Camera +- `camera` - camera +- `camera.fill` - camera filled +- `arrow.triangle.2.circlepath` - flip camera +- `photo` - gallery/photos +- `bolt` - flash +- `bolt.slash` - flash off + +### Communication +- `message` - message +- `message.fill` - message filled +- `envelope` - email +- `envelope.fill` - email filled +- `phone` - phone +- `phone.fill` - phone filled +- `video` - video call +- `video.fill` - video call filled + +### Social +- `heart` - like +- `heart.fill` - liked +- `star` - favorite +- `star.fill` - favorited +- `hand.thumbsup` - thumbs up +- `hand.thumbsdown` - thumbs down +- `person` - profile +- `person.fill` - profile filled +- `person.2` - people +- `person.2.fill` - people filled + +### Content Actions +- `square.and.arrow.up` - share +- `square.and.arrow.down` - download +- `doc.on.doc` - copy +- `trash` - delete +- `pencil` - edit +- `folder` - folder +- `folder.fill` - folder filled +- `bookmark` - bookmark +- `bookmark.fill` - bookmarked + +### Status & Feedback +- `checkmark` - success/done +- `checkmark.circle.fill` - completed +- `xmark.circle.fill` - error/failed +- `exclamationmark.triangle` - warning +- `info.circle` - info +- `questionmark.circle` - help +- `bell` - notification +- `bell.fill` - notification filled + +### Misc +- `ellipsis` - more options +- `ellipsis.circle` - more in circle +- `line.3.horizontal` - menu/hamburger +- `slider.horizontal.3` - filters +- `arrow.clockwise` - refresh +- `location` - location +- `location.fill` - location filled +- `map` - map +- `mappin` - pin +- `clock` - time +- `calendar` - calendar +- `link` - link +- `nosign` - block/prohibited + +## Animated Symbols + +```tsx + +``` + +### Animation Effects + +- `bounce` - Bouncy animation +- `pulse` - Pulsing effect +- `variableColor` - Color cycling +- `scale` - Scale animation + +```tsx +// Bounce with direction +animationSpec={{ + effect: { type: "bounce", direction: "up" } // up | down +}} + +// Pulse +animationSpec={{ + effect: { type: "pulse" } +}} + +// Variable color (multicolor symbols) +animationSpec={{ + effect: { + type: "variableColor", + cumulative: true, + reversing: true + } +}} +``` + +## Symbol Weights + +```tsx +// Lighter weights + + + + +// Default + + +// Heavier weights + + + + + +``` + +## Symbol Scales + +```tsx + + // default + +``` + +## Multicolor Symbols + +Some symbols support multiple colors: + +```tsx + +``` + +## Finding Symbol Names + +1. Use the SF Symbols app on macOS (free from Apple) +2. Search at https://developer.apple.com/sf-symbols/ +3. Symbol names use dot notation: `square.and.arrow.up` + +## Best Practices + +- Always use SF Symbols over vector icon libraries +- Match symbol weight to nearby text weight +- Use `.fill` variants for selected/active states +- Use PlatformColor for tint to support dark mode +- Keep icons at consistent sizes (16, 20, 24, 32) diff --git a/.agents/skills/building-native-ui/references/media.md b/.agents/skills/building-native-ui/references/media.md new file mode 100644 index 0000000..50c0ffb --- /dev/null +++ b/.agents/skills/building-native-ui/references/media.md @@ -0,0 +1,198 @@ +# Media + +## Camera + +- Hide navigation headers when there's a full screen camera +- Ensure to flip the camera with `mirror` to emulate social apps +- Use liquid glass buttons on cameras +- Icons: `arrow.triangle.2.circlepath` (flip), `photo` (gallery), `bolt` (flash) +- Eagerly request camera permission +- Lazily request media library permission + +```tsx +import React, { useRef, useState } from "react"; +import { View, TouchableOpacity, Text, Alert } from "react-native"; +import { CameraView, CameraType, useCameraPermissions } from "expo-camera"; +import * as MediaLibrary from "expo-media-library"; +import * as ImagePicker from "expo-image-picker"; +import * as Haptics from "expo-haptics"; +import { SymbolView } from "expo-symbols"; +import { PlatformColor } from "react-native"; +import { GlassView } from "expo-glass-effect"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; + +function Camera({ onPicture }: { onPicture: (uri: string) => Promise }) { + const [permission, requestPermission] = useCameraPermissions(); + const cameraRef = useRef(null); + const [type, setType] = useState("back"); + const { bottom } = useSafeAreaInsets(); + + if (!permission?.granted) { + return ( + + Camera access is required + + + Grant Permission + + + + ); + } + + const takePhoto = async () => { + await Haptics.selectionAsync(); + if (!cameraRef.current) return; + const photo = await cameraRef.current.takePictureAsync({ quality: 0.8 }); + await onPicture(photo.uri); + }; + + const selectPhoto = async () => { + await Haptics.selectionAsync(); + const result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: "images", + allowsEditing: false, + quality: 0.8, + }); + if (!result.canceled && result.assets?.[0]) { + await onPicture(result.assets[0].uri); + } + }; + + return ( + + + + + + + + + setType(t => t === "back" ? "front" : "back")} icon="arrow.triangle.2.circlepath" /> + + + + ); +} +``` + +## Audio Playback + +Use `expo-audio` not `expo-av`: + +```tsx +import { useAudioPlayer } from 'expo-audio'; + +const player = useAudioPlayer({ uri: 'https://stream.nightride.fm/rektory.mp3' }); + +