
dev.cds-web
★ 486by coinbase · part of coinbase/cds
dev.cds-web — an installable skill for AI agents, published by coinbase/cds.
This is the playbook your agent receives when the skill activates — you don't need to read it to use the skill, but it's here to audit before installing.
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
- Register the component in
packages/web/src/core/componentConfig.tsusing its*BaseProps:
import type { MyComponentBaseProps } from '../category/MyComponent';
export type ComponentConfig = {
MyComponent?: ConfigResolver<MyComponentBaseProps>;
};- Adopt
useComponentConfigin the component and destructure from merged props:
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
_propsas the input variable andmergedPropsas 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.
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:
<Box padding={{ base: 2, phone: 1, desktop: 3 }} />Granular classNames
- Components can expose a
classNamesobject prop for granular overrides on child elements within the component. - Since the keys of the
classNamesobject are specific to the component they should never be on the*BasePropstype
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
styleandstylesobject props for overriding inline styles. - As styling is a concern of that specific component, the
styleandstylesprops should never be on the*BasePropstype. - Styles should be merged into a single object with a
useMemohook and applied in the correct order (default styles =>styleprop =>styles[ELEMENT_NAME]prop).
Example:
type ComponentProps = ComponentBaseProps & {
style?: React.CSSProperties;
styles?: { root?: React.CSSProperties; label?: React.CSSProperties };
};Animation
Use Framer Motion for complex animations:
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:
<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 elementstyle?: React.CSSProperties- inline styles always applied to root element- Polymorphic
asprop 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
npx skills add https://github.com/coinbase/cds --skill dev.cds-webRun this in your project — your agent picks the skill up automatically.
No common issues documented yet. If you hit a problem, the repository's GitHub Issues page is the best place to look.