dev.cds-web
$
npx mdskill add coinbase/cds/dev.cds-web<!-- TODO: nested AGENTS.md files should work but they seem a little flaky at the moment. Intelligent mdc files are working better -->
SKILL.md
.github/skills/dev.cds-webView on GitHub ↗
---
name: dev.cds-web
description: USE THIS when asked to work on a new or existing (WEB) CDS React component in packages/web
---
<!-- TODO: nested AGENTS.md files should work but they seem a little flaky at the moment. Intelligent mdc files are working better -->
# CDS Web Package Guidelines
## Component Config Adoption (Web)
Use this guidance when adding `ComponentConfigProvider` defaults for the specific component you are editing.
### Required implementation pattern
1. Register the component in `packages/web/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 { className, style, ...props } = mergedProps;
return <Box className={className} 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 polymorphic/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.
## CSS with Linaria
Use Linaria for zero-runtime CSS. **Always use CDS theme CSS variables** for colors, spacing, typography, and other design tokens.
Reference packages/web/src/core/theme.ts:53-119 for the CSS variable naming pattern.
```tsx
import { css, cx } from '@linaria/core';
const containerCss = css`
/* Spacing tokens */
padding: var(--space-2);
gap: var(--space-1);
/* Color tokens */
background: var(--color-bgPrimary);
color: var(--color-fgPrimary);
border: 1px solid var(--color-line);
/* Border radius tokens */
border-radius: var(--borderRadius-400);
/* Typography tokens */
font-size: var(--fontSize-body);
&:hover {
background: var(--color-bgPrimaryHover);
}
`;
// Merge classNames with cx utility **in CORRECT ORDER**
<div
className={cx(
containerCss, // Base styles first
isCompact && conditionClassToApply, // Conditional computed styles
className, // User-provided className prop
classNames.root, // granular overrides last
)}
/>;
```
**IMPORTANT:** Using CSS variables ensures components respond correctly to theme changes (light/dark mode, brand themes).
### CSS Variable Naming
Design tokens from `packages/common/src/core/theme.ts` map to CSS variables:
- Colors: `--color-{tokenName}` (e.g., `--color-bgPrimary`, `--color-fgMuted`)
- Spacing: `--space-{scale}` (e.g., `--space-2` = 16px, `--space-3` = 24px)
- Typography: `--fontSize-{font}`, `--fontWeight-{font}`, `--lineHeight-{font}`, `--fontFamily-{font}`
- Border: `--borderRadius-{size}`, `--borderWidth-{size}`
- Sizing: `--iconSize-{size}`, `--avatarSize-{size}`, `--controlSize-{name}`
- Shadows: `--shadow-{level}`
### Responsive Breakpoints
Reference `packages/web/src/styles/media.ts` for breakpoint values:
- **phone**: 0-767px
- **tablet**: 768-1279px
- **desktop**: 1280px+
Use the `Box` component's responsive prop API for responsive values:
```tsx
<Box padding={{ base: 2, phone: 1, desktop: 3 }} />
```
### Granular classNames
- Components can expose a `classNames` object prop for granular overrides on child elements within the component.
- Since the keys of the `classNames` object are specific to the component they should never be on the `*BaseProps` type
### data-attributes
- Use data attributes for state-based styling: `data-active`, `data-disabled`, `data-variant`, `data-filled`
## Inline styles
- Web components should all expose a `style` and `styles` object props for overriding inline styles.
- As styling is a concern of that specific component, the `style` and `styles` props should never be on the `*BaseProps` type.
- Styles should be merged into a single object with a `useMemo` hook and applied in the correct order (default styles => `style` prop => `styles[ELEMENT_NAME]` prop).
**Example:**
```tsx
type ComponentProps = ComponentBaseProps & {
style?: React.CSSProperties;
styles?: { root?: React.CSSProperties; label?: React.CSSProperties };
};
```
## Animation
Use Framer Motion for complex animations:
```tsx
import { m as motion, AnimatePresence } from 'framer-motion';
<AnimatePresence>
{visible && (
<motion.div
initial={{ opacity: 0, y: -8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -8 }}
/>
)}
</AnimatePresence>;
```
For simple transitions, prefer CSS transitions in Linaria.
## Accessibility
Use ARIA attributes:
```tsx
<div role="group" aria-roledescription="carousel" aria-live="polite">
<button aria-pressed={isActive} tabIndex={isVisible ? 0 : -1} />
</div>
```
- Implement keyboard navigation (Arrow keys, Home, End) for interactive components.
- Provide descriptive labels for all interactive elements
- Associate form inputs with helper text using aria-describedby
- Use semantic HTML elements whenever possible
- Follow WCAG 2.1 AA standards for color contrast
- Support screen readers by providing descriptive labels and instructions
## Web-Specific Props
- `className?: string` - CSS class always applied to root element
- `style?: React.CSSProperties` - inline styles always applied to root element
- Polymorphic `as` prop for element type (where applicable e.g. see Box)
## Reference Components
- **Carousel**: compound components, imperative handle, context pattern
- **Select** (alpha/): generics, controlled/uncontrolled
- **RollingNumber**: animation config, measurement patterns
More from coinbase/cds
- cds-accessibility|
- cds-code|
- cds-design-to-code|
- cds-docs|
- cds-migrator-transform|
- components.best-practicesUse this skill whenever working on CDS React components in any package.
- components.stylesGuidelines 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.
- components.write-docsGuidelines for creating or updating documentation for a CDS component on the docsite (apps/docs/). Use this skill after creating or making updates to a CDS React component to write high quality documentaiton in the CDS docsite.
- deprecate-cds-api|
- dev.cds-mobileUSE THIS when asked to work on a new or existing (MOBILE) CDS React component in packages/mobile