10 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
user rools
每次开头请叫我哥哥
请严格遵守ui设计规范,使用响应式布局设计,不要硬编码 请使用项目里面封装好的ui组件,如果没有请提醒我
Project Overview
React Native + Expo mobile application built on Ignite CLI boilerplate (v11.4.0). Uses TypeScript, React Navigation 7, and NativeWind (Tailwind CSS).
Common Commands
pnpm install # Install dependencies
pnpm run start # Start Expo dev client
pnpm run android # Run on Android (expo run:android)
pnpm run ios # Run on iOS (expo run:ios)
pnpm run compile # TypeScript type check (tsc --noEmit)
pnpm run lint # ESLint with auto-fix
pnpm run lint:check # ESLint without fix
pnpm run test # Run Jest tests
pnpm run test:watch # Run Jest in watch mode
# EAS Builds (requires expo-cli)
pnpm run build:ios:sim # iOS simulator build
pnpm run build:android:sim # Android emulator build
pnpm run prebuild:clean # Regenerate native projects
Architecture
Directory Structure
app/- Main application sourcecomponents/- 12个基础 UI 组件(详见下方组件列表)screens/- Screen components connected to navigationscreens/DemoShowroomScreen/demos/- 组件演示页面(展示组件用法,非实际组件)navigators/- React Navigation configuration (AppNavigator, DemoNavigator)context/- React Context providers (AuthContext, ThemeContext, ToastContext)services/api/- API layer using Apisauce (authApi.tsfor auth,index.tsfor base API)theme/- Design tokens (colors, spacing, typography) and theme systemi18n/- Internationalization with i18next (8 languages: en, zh, ar, es, fr, hi, ja, ko)utils/- Utilities including MMKV storage wrapperconfig/- Environment-based configuration (dev/prod)devtools/- Reactotron configuration
Key Patterns
State Management: React Context + MMKV for persistence. No Redux.
Theming: Use useAppTheme() hook and ThemedStyle<T> pattern:
const $container: ThemedStyle<ViewStyle> = ({ colors, spacing }) => ({
backgroundColor: colors.background,
padding: spacing.lg,
})
Navigation: Two-layer structure - AppStack (auth/main screens) and DemoNavigator (bottom tabs). Type-safe routes defined in navigationTypes.ts. Auth screens: AuthWelcome, Login, Register, ForgotPassword. Main screens: Welcome, Profile, Settings, Security, Theme, Language, About, etc.
API Layer: Singleton Api class in services/api/index.ts using Apisauce with typed responses.
Styling: NativeWind (Tailwind CSS) configured. Use className prop for Tailwind styles.
Authentication: AuthContext provides full auth flow with step-based state machines for login, register, and password reset. Supports email/password and Google Sign-In. Uses JWT tokens stored in MMKV.
UI 设计规范
参考文件
| 类型 | 文件路径 | 说明 |
|---|---|---|
| 设计令牌 | app/theme/colors.ts |
颜色系统(浅色主题) |
app/theme/colorsDark.ts |
颜色系统(深色主题) | |
app/theme/spacing.ts |
间距系统 | |
app/theme/typography.ts |
字体排版 | |
app/theme/styles.ts |
全局样式($styles.container 等) | |
| UI 参考页面 | app/screens/DemoCommunityScreen.tsx |
最佳参考 - 标准页面结构、ListItem 用法、Section 标题 |
app/screens/WelcomeScreen.tsx |
useHeader hook 用法 | |
app/screens/ProfileScreen.tsx |
设置页面、用户卡片 | |
| 组件演示 | app/screens/DemoShowroomScreen/demos/ |
所有组件的用法示例(类似 Storybook) |
设计令牌
颜色语义(定义在 app/theme/colors.ts):
text: palette.neutral800 // 主要文本
textDim: palette.neutral600 // 次要文本
background: palette.neutral200 // 屏幕背景
border: palette.neutral400 // 边框
tint: palette.primary500 // 主色调(按钮、链接)
separator: palette.neutral300 // 分隔线
error: palette.angry500 // 错误
间距系统(app/theme/spacing.ts):
xxxs: 2, xxs: 4, xs: 8, sm: 12, md: 16, lg: 24, xl: 32, xxl: 48, xxxl: 64
字体大小(app/components/Text.tsx):
xxl: 36, xl: 24, lg: 20, md: 18, sm: 16(默认), xs: 14, xxs: 12
Header 使用规范
必须使用 useHeader hook,不要直接在 Screen 内部放置 <Header> 组件。
// ✅ 正确写法
import { useHeader } from "@/utils/useHeader"
useHeader({
title: translate("screenName:title"),
leftIcon: "back",
onLeftPress: () => navigation.goBack(),
}, [])
return (
<Screen safeAreaEdges={["bottom"]} contentContainerStyle={[$styles.container, themed($container)]}>
{/* 内容 */}
</Screen>
)
const $container: ThemedStyle<ViewStyle> = ({ spacing }) => ({
paddingTop: spacing.lg, // 覆盖 $styles.container 的 56px
})
原因: Header 组件默认添加顶部安全区域 padding,直接放在 Screen 内容中会导致 Header 上方出现额外空白。useHeader 通过 navigation.setOptions() 将 Header 设置为导航层的一部分。
容器间距规范
$styles.container(定义在 app/theme/styles.ts):
container: {
paddingTop: spacing.lg + spacing.xl, // = 56px(为无 Header 的 Tab 页面设计)
paddingHorizontal: spacing.lg, // = 24px
}
使用 useHeader 的页面需在 $container 中覆盖 paddingTop 为 spacing.lg (24px)。
不要在页面内容中再添加额外的 paddingTop 或 marginTop,避免重复间距。
ListItem 使用规范
参考: app/screens/DemoCommunityScreen.tsx
默认无分隔线,保持简洁风格:
<ListItem
tx="setting:option1"
leftIcon="settings"
rightIcon="caretRight"
onPress={handlePress}
/>
只有连续列表项需要视觉分隔时,才使用 bottomSeparator,且最后一项不需要:
<ListItem text="Item 1" bottomSeparator />
<ListItem text="Item 2" bottomSeparator />
<ListItem text="Item 3" />
Section 标题规范
参考: app/screens/DemoCommunityScreen.tsx 的 $sectionTitle 样式
使用 preset="subheading" 作为 section 标题:
<Text preset="subheading" tx="section:title" style={themed($sectionTitle)} />
const $sectionTitle: ThemedStyle<TextStyle> = ({ spacing }) => ({
marginTop: spacing.xxl, // 48px
})
页面结构模板
有 Header 的页面(如设置、详情页):
import { useHeader } from "@/utils/useHeader"
export const ExampleScreen: FC<Props> = ({ navigation }) => {
const { themed } = useAppTheme()
useHeader({
title: translate("exampleScreen:title"),
leftIcon: "back",
onLeftPress: () => navigation.goBack(),
}, [])
return (
<Screen
preset="scroll"
safeAreaEdges={["bottom"]}
contentContainerStyle={[$styles.container, themed($container)]}
>
{/* 内容 */}
</Screen>
)
}
const $container: ThemedStyle<ViewStyle> = ({ spacing }) => ({
paddingTop: spacing.lg,
})
无 Header 的 Tab 页面(参考 DemoCommunityScreen.tsx):
export const ExampleTabScreen: FC<Props> = () => {
const { themed } = useAppTheme()
return (
<Screen preset="scroll" contentContainerStyle={$styles.container}>
<Text preset="heading" tx="screen:title" style={themed($title)} />
<Text tx="screen:description" style={themed($description)} />
<Text preset="subheading" tx="screen:section1" style={themed($sectionTitle)} />
<ListItem ... />
<ListItem ... />
</Screen>
)
}
const $title: ThemedStyle<TextStyle> = ({ spacing }) => ({
marginBottom: spacing.sm,
})
const $description: ThemedStyle<TextStyle> = ({ spacing }) => ({
marginBottom: spacing.lg,
})
const $sectionTitle: ThemedStyle<TextStyle> = ({ spacing }) => ({
marginTop: spacing.xxl,
})
组件列表 (app/components/)
| 组件 | 用途 |
|---|---|
Screen.tsx |
屏幕容器(SafeArea + 键盘适配 + 滚动) |
Header.tsx |
页面头部导航栏 |
Card.tsx |
卡片容器 |
TextField.tsx |
文本输入框 |
Button.tsx |
按钮(default/filled/reversed 预设) |
EmptyState.tsx |
空状态占位 |
ListItem.tsx |
列表项 |
Icon.tsx |
图标 |
Text.tsx |
文本(heading/subheading/bold 等预设) |
AutoImage.tsx |
自适应图片 |
Toggle/ |
开关/复选框/单选框 |
Avatar.tsx |
用户头像(使用 expo-image 缓存) |
Modal.tsx |
模态框(bottom/center 预设) |
Dialog.tsx |
对话框(确认/提示) |
注意: screens/DemoShowroomScreen/demos/ 目录下的 Demo*.tsx 文件是组件演示页面,用于展示组件的各种用法和变体,类似 Storybook,不是实际组件。
自适应布局
Screen 组件预设:
preset="fixed"- 固定布局,不可滚动preset="scroll"- 始终可滚动preset="auto"- 智能判断:内容超出屏幕时才启用滚动
安全区域: 使用 useSafeAreaInsetsStyle hook 处理刘海屏
<Screen safeAreaEdges={["top", "bottom"]}>
{/* 内容自动避开刘海和底部指示条 */}
</Screen>
间距系统 (app/theme/spacing.ts):
xxxs: 2, xxs: 4, xs: 8, sm: 12, md: 16, lg: 24, xl: 32, xxl: 48, xxxl: 64
字体大小 (app/components/Text.tsx) - 固定像素值,无响应式:
xxl: 36, xl: 24, lg: 20, md: 18, sm: 16(默认), xs: 14, xxs: 12
Path Aliases
@/*→app/*@assets/*→assets/*
Import Restrictions (ESLint enforced)
- Use
Text,Button,TextInputfrom@/components, not fromreact-native - Use
SafeAreaViewfromreact-native-safe-area-context, not fromreact-native - Import named exports from
react(notimport React from 'react')
Testing
Jest with Testing Library for React Native. Test files use .test.ts(x) suffix. Setup in test/setup.ts.
Run single test:
pnpm run test -- --testPathPattern="storage"
Web Support
This app runs on web via Expo. Use pnpm run start and press w to open web. Note: React Navigation's headerRight may not work reliably on web - consider placing header elements directly inside Screen components if targeting web.