--- name: dev.cds-mobile description: USE THIS when asked to work on a new or existing (MOBILE) CDS React component in packages/mobile --- # CDS Mobile Package Guidelines Mobile-specific patterns for `@coinbase/cds-mobile`. ## Component Config Adoption (Mobile) Use this guidance when adding `ComponentConfigProvider` defaults for the specific component you are editing. ### Required implementation pattern 1. Register the component in `packages/mobile/src/core/componentConfig.ts` using its `*BaseProps`: ```ts import type { MyComponentBaseProps } from '../category/MyComponent'; export type ComponentConfig = { MyComponent?: ConfigResolver; }; ``` 1. Adopt `useComponentConfig` in the component and destructure from merged props: ```tsx import { useComponentConfig } from '../hooks/useComponentConfig'; export const MyComponent = memo((_props: MyComponentProps) => { const mergedProps = useComponentConfig('MyComponent', _props); const { style, ...props } = mergedProps; return ; }); ``` ### Rules to preserve behavior - Provider config supplies defaults only; local props must continue to win. - Use `_props` as the input variable and `mergedProps` as the configured output. - Type resolver entries with `*BaseProps` (not full `*Props`). - Keep scope to prop-level theming defaults; do not alter component behavior or control flow. - When practical during the same change, prefer arrow-function component declarations. ## Styling with StyleSheet Use `StyleSheet.create` for static styles and `useTheme()` for dynamic values: ```tsx import { StyleSheet, type StyleProp, type ViewStyle } from 'react-native'; const styles = StyleSheet.create({ container: { position: 'relative', width: '100%' }, }); // Dynamic styles via theme hook const theme = useTheme(); const dynamicStyle = { backgroundColor: theme.color.bgPrimary, padding: theme.space[2], }; ; ``` ### Inline styles - Mobile components should all expose a `style` and `styles` object props for overriding default styles. - As styling is a concern of that specific component, the `style` and `styles` props should never be on the `*BaseProps` type. - `styles` can be used for granular overrides on child elements within the component. - **Always** merge styles into a react-native style array with `useMemo` in the correct order (default styles => `style` prop => `styles[ELEMENT_NAME]` prop). **Example:** ```tsx type ComponentProps = ComponentBaseProps & { style?: StyleProp; styles?: { root?: StyleProp; label?: StyleProp; }; }; const theme = useTheme(); const containerStyles = useMemo( () => [ { backgroundColor: theme.color.bgPrimary }, // default styles style, // from props styles.root, // from props ], [theme.color.bgPrimary, animatedStyles, style] ); // Apply to component ``` ## Animation ### React Native Reanimated ```tsx import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; const opacity = useSharedValue(0); const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value, transform: [{ translateY: withTiming(opacity.value * -8) }], })); ; ``` We DO NOT use React-Spring anymore for animations on mobile. ## Gesture Handling Use `react-native-gesture-handler`: ```tsx import { Gesture, GestureDetector } from 'react-native-gesture-handler'; const panGesture = useMemo( () => Gesture.Pan() .onStart(() => { /* ... */ }) .onUpdate(({ translationX }) => { /* ... */ }) .onEnd(({ translationX, velocityX }) => { /* ... */ }) .withTestId(testID) .runOnJS(true), [dependencies], ); {/* ... */} ; ``` ## Layout Measurement Use `onLayout` callback instead of ResizeObserver: ```tsx const [size, onLayout] = useLayout(); // Or inline setHeight(e.nativeEvent.layout.height)} /> ``` ## Accessibility - Use appropriate accessibilityLabel, accessibilityHint, and accessibilityRole, accessibilityState props - Support screen readers (VoiceOver and TalkBack) - Ensure touch targets meet minimum size requirements (44x44 points) **Example:** Use React Native accessibility props: ```tsx ``` ### Screen Reader Content ```tsx // Hide visual content from screen readers {/* Animated/visual content */} // Provide accessible alternative {accessibleLabel} ``` ## Reference Components - **SlideButton**: gesture handling, spring animations, accessibility actions - **RollingNumber**: Reanimated, measurement patterns, screen reader content - **Select** (alpha/): controlled/uncontrolled, Drawer integration - **Stepper**: direction-based defaults, shared logic from cds-common - **Tour**: animations, complexity - **DatePicker**: complexity