coinbase/cds

dev.cds-mobile

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

View source
Original skill document

Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.

<!-- 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
from this repository

More skills

All skills
coinbase
Official

cds-code

Provides a workflow for building and reviewing high-quality React and React Native UI with the Coinbase Design System (CDS). Use this skill to create UI or make visual changes to existing UI with CDS components, tokens, style props, icons, and illustrations. Use it for explicit CDS-adherence audits and reviews, not non-visual logic, data, analytics, navigation, tests, generated code, or refactoring. Examples: "build this screen", "fix spacing on this component", "replace this UI with CDS components", "perform a CDS audit on our changes", "check our codebase for CDS adherence", "does this feature use CDS well?"

installs
1
GitHub stars
500
Updated
Sep 3
coinbase
Official

cds-docs

Retrieve Coinbase Design System (CDS) documentation: setup, installation, theming, tokens, and per-component APIs/examples. Use this skill whenever the task involves CDS components, design-system rules, theming, or choosing between web and mobile CDS packages, even if the user only says "use CDS" or names a component. Always start from the docs route index, then fetch only the pages you need to reason and implement correctly. Prefer the CDS MCP server (list-cds-routes, get-cds-doc); if MCP is unavailable, use curl against https://cds.coinbase.com/llms/....

installs
1
GitHub stars
500
Updated
Sep 3
coinbase
Official

components.styles

Guidelines writing styles API (styles, classNames, and static classNames) for a CDS component. Use this skill when adding customization options to a React component via styles or classNames props or when needing to update the docsite with component styles documentation.

installs
1
GitHub stars
500
Updated
Sep 3
coinbase
Official

eslint-plugin-custom-rule

USE THIS when asked to create a new eslint plugin rule for the eslint-plugin-cds package

installs
1
GitHub stars
500
Updated
Sep 3