Skip to content
Back to skills

Dev.Cds Mobile

ASecurity

USE THIS when asked to work on a new or existing (MOBILE) CDS React component in packages/mobile

  • 507 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added June 6, 2026
designgoreactspring

Security analysis

A100/100

Scanned June 6, 2026

npx -y skills add coinbase/cds --skill dev.cds-mobile --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dev.Cds Mobile?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dev.Cds Mobile
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/coinbase-dev-cds-mobile/badge)](https://www.skillsdirectory.com/skills/coinbase-dev-cds-mobile)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dev.cds-mobile
description: USE THIS when asked to work on a new or existing (MOBILE) CDS React component in packages/mobile
---

<!-- TODO: nested AGENTS.md files should work but they seem a little flaky at the moment. Intelligent mdc files are working better -->

# 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<MyComponentBaseProps>;
};
```

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 <Pressable style={style} {...props} />;
});
```

### 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],
};

<View style={[styles.container, dynamicStyle, style]} />;
```

### 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<ViewStyle>;
  styles?: {
    root?: StyleProp<ViewStyle>;
    label?: StyleProp<TextStyle>;
  };
};

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
<Box style={containerStyles}>
```

## 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) }],
}));

<Animated.View style={animatedStyle} />;
```

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],
);

<GestureDetector gesture={panGesture}>
  <Animated.View>{/* ... */}</Animated.View>
</GestureDetector>;
```

## Layout Measurement

Use `onLayout` callback instead of ResizeObserver:

```tsx
const [size, onLayout] = useLayout();
<View onLayout={onLayout} />

// Or inline
<View onLayout={(e) => 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
<View
  accessible
  accessibilityRole="adjustable"
  accessibilityLabel="Product carousel"
  accessibilityLiveRegion="polite"
>
  <Pressable
    accessibilityState={{ selected: isActive, disabled }}
    accessibilityActions={[{ name: 'activate' }]}
    onAccessibilityAction={handleAccessibilityAction}
  />
</View>
```

### Screen Reader Content

```tsx
// Hide visual content from screen readers
<View accessibilityElementsHidden importantForAccessibility="no-hide-descendants">
  {/* Animated/visual content */}
</View>

// Provide accessible alternative
<Text
  importantForAccessibility="yes"
  accessibilityLiveRegion="polite"
  style={{ color: 'transparent', position: 'absolute' }}
>
  {accessibleLabel}
</Text>
```

## 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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.