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.
npx skills add https://github.com/coinbase/cds --skill components.styles
Goal: Add styles API (styles, classNames, and static classNames) to a CDS component and/or update the component documentation with styles documentation.
If no component name is provided, ask the user which component they want to add styles to.
Find the component source file:
packages/web/src/[source-category]/[ComponentName].tsx # for web
packages/mobile/src/[source-category]/[ComponentName].tsx # for mobile
> ⚠️ IMPORTANT: Adding styles/classNames props is a commitment to the component's internal structure.
>
> Before adding styles API, carefully review the component's JSX structure:
>
> - Flag if the component could be simplified (e.g., unnecessary wrappers, redundant containers)
> - Do NOT add styles to elements that may be refactored - this creates breaking changes
> - Ask the user if you notice the component structure could be improved before committing to it
>
> Once published, changing or removing selectors is a breaking change for consumers.
Review the component's JSX to identify elements that should be targetable via styles/classNames:
start, content, end, header, footer)> IMPORTANT: Before adding a new selector name not in this list, get explicit confirmation from the user.
> When a new selector is approved, add it to this list.
| Selector | Description |
| --------------------- | ----------------------------------------------------- |
| accessory | Accessory element (e.g., chevron, icon at end) |
| activeIndicator | Active indicator element (e.g., in tabs) |
| bottomContent | Bottom section content |
| carousel | Main carousel track element |
| carouselContainer | Outer carousel container |
| childrenContainer | Container wrapping children |
| content | Main content area |
| contentContainer | Container wrapping content |
| description | Description text element |
| day | Date cell in a calendar grid |
| end | End slot content (e.g., actions, icons) |
| fill | Fill/progress indicator within a track |
| header | Header section |
| helperText | Helper/assistive text below content |
| icon | Icon element |
| intermediary | Middle/intermediary element between sections |
| label | Label text element |
| labels | Container for multiple labels |
| logo | Logo element |
| mainContent | Primary content area |
| media | Media element (image, avatar, icon) |
| modal | Visible modal card element |
| navigation | Navigation controls (e.g., prev/next buttons) |
| overlay | Full-viewport overlay/backdrop element |
| pagination | Pagination indicators |
| pressable | Pressable/interactive wrapper |
| progress | Progress indicator element |
| progressBar | ProgressBar sub-component within a composed component |
| root | Root/outermost container element |
| safeArea | Safe area region wrapping content |
| start | Start slot content (e.g., back button) |
| step | Individual step element (in steppers) |
| substepContainer | Container for nested sub-steps |
| subtitle | Subtitle text element |
| tab | Tab element (in tabs) |
| tabs | Tabs container element |
| thumb | Draggable thumb element (in sliders) |
| title | Title text element |
| titleStack | Stack containing title/subtitle/description |
| titleStackContainer | Container wrapping titleStack |
| topContent | Top section content |
| track | Track/rail element (in progress bars, sliders) |
| trigger | Trigger element that opens a dropdown/popover |
Selector JSDoc comments describe what the element is, not what the prop does:
/** Description *//** Header element, only rendered on phone viewport */Examples:
/** Root element */
/** Title text element */
/** Navigation controls element */
/** Header element, only rendered on phone viewport in horizontal direction */
For web components, add three things:
Add a static classNames object with JSDoc comments. Place this before the component's type definitions:
/**
* Static class names for [ComponentName] component parts.
* Use these selectors to target specific elements with CSS.
*/
export const [componentName]ClassNames = {
/** Root element */
root: 'cds-[ComponentName]',
/** [Concise element description] */
[selectorName]: 'cds-[ComponentName]-[selectorName]',
// ... more selectors as needed
} as const;
Naming conventions:
cds- prefix for all class namescds-NavigationBarcds-NavigationBar-contentWrapper, cds-Foo-titleStackExample:
export const fooClassNames = {
root: 'cds-Foo',
contentWrapper: 'cds-Foo-contentWrapper',
titleStack: 'cds-Foo-titleStack',
helperText: 'cds-Foo-helperText',
} as const;
Import and use the StylesAndClassNames utility type:
import type { StylesAndClassNames } from '../types';
export type [ComponentName]BaseProps = BoxBaseProps & {
// ... other props (without styles/classNames)
};
export type [ComponentName]Props = [ComponentName]BaseProps & StylesAndClassNames<typeof [componentName]ClassNames> & Omit<BoxProps<[ComponentName]DefaultElement>, 'children'>;
This automatically generates the styles and classNames props based on your static classNames object.
Apply the static classNames, dynamic classNames, and styles in the component:
import { cx } from '../cx';
// In the component:
<VStack
className={cx([componentName]ClassNames.root, className, classNames?.root)}
style={{ ...style, ...styles?.root }}
// ... other props
>
<HStack
className={cx([componentName]ClassNames.contentWrapper, classNames?.contentWrapper)}
style={styles?.contentWrapper}
>
{children}
</HStack>
</VStack>
Add tests to verify that static class names are applied correctly to the component. This ensures the class names remain stable for consumers who depend on them for CSS targeting.
Test pattern:
import { [componentName]ClassNames } from '../[ComponentName]';
describe('[ComponentName] static classNames', () => {
it('applies static class names to component elements', () => {
render(
<[ComponentName]WithTheme
start={<div>Start</div>} // Include props that render conditional elements
>
<div>Children</div>
</[ComponentName]WithTheme>,
);
// Test root element
const root = screen.getByRole('[role]'); // or use testID/other selector
expect(root).toHaveClass([componentName]ClassNames.root);
// Test sub-elements using querySelector with the static class name
expect(root.querySelector(`.${[componentName]ClassNames.start}`)).toBeInTheDocument();
expect(root.querySelector(`.${[componentName]ClassNames.content}`)).toBeInTheDocument();
});
});
Key testing principles:
toHaveClass() for elements accessible via roles/queriesquerySelector() with the static class name for internal elementsExample from NavigationBar:
import { navigationBarClassNames } from '../NavigationBar';
describe('NavigationBar static classNames', () => {
it('applies static class names to component elements', () => {
render(
<NavigationBarWithTheme start={<div>Start</div>}>
<div>Children</div>
</NavigationBarWithTheme>,
);
const nav = screen.getByRole('navigation');
expect(nav).toHaveClass(navigationBarClassNames.root);
expect(nav.querySelector(`.${navigationBarClassNames.start}`)).toBeInTheDocument();
expect(nav.querySelector(`.${navigationBarClassNames.content}`)).toBeInTheDocument();
});
});
For mobile components, the pattern is simpler (no static classNames):
export type [ComponentName]Props = {
// ... other props
/** Custom styles for individual elements of the [ComponentName] component */
styles?: {
/** Root container element */
root?: StyleProp<ViewStyle>;
/** [Concise element description] */
[selectorName]?: StyleProp<ViewStyle | TextStyle>;
// ... more selectors as needed
};
};
<View style={[defaultStyles.root, styles?.root]}>
<View style={[defaultStyles.content, styles?.content]}>{children}</View>
</View>
If any selectors have special rendering conditions, append the note after the element description with a comma:
styles?: {
/** Header element, only rendered on phone viewport in horizontal direction */
header?: React.CSSProperties;
};
Common cases to document:
The StylesAndClassNames utility type (from packages/web/src/types.ts) automatically generates:
// Given:
const fooClassNames = {
root: 'cds-Foo',
contentWrapper: 'cds-Foo-contentWrapper',
} as const;
// StylesAndClassNames<typeof fooClassNames> generates:
{
styles?: {
root?: React.CSSProperties;
contentWrapper?: React.CSSProperties;
};
classNames?: {
root?: string;
contentWrapper?: string;
};
}
See packages/web/src/navigation/NavigationBar.tsx for a complete example of the styles API pattern:
StylesAndClassNames type on regular Props (not BaseProps)cx()See packages/web/src/navigation/__tests__/NavigationBar.test.tsx for static classNames test example:
NavigationBar static classNames describe block: Tests all static class names are appliedAfter adding the styles API to the component, update the documentation:
yarn nx run docs:docgen
components.write-docs SKILL for general knowledge on how to write component documentation:_webStyles.mdx with ComponentStylesTable and StylesExplorer_mobileStyles.mdx with ComponentStylesTable (if mobile)index.mdx to import and render the styles tablesBefore completing, verify:
cds-ComponentName-selectorName convention (camelCase)StylesAndClassNames utility type on regular Props (not BaseProps) (web) or manual styles type (mobile)cx() in component JSX (web only)yarn nx run docs:docgen to regenerate styles dataUse Expo DOM components to run web code in a webview on native and as-is on web. Migrate web code to native incrementally.
Frontend development guidelines for React/TypeScript applications. Modern patterns including Suspense, lazy loading, useSuspenseQuery, file organization with features directory, MUI v7 styling, TanStack Router, performance optimization, and TypeScript best practices. Use when creating components, pages, features, fetching data, styling, routing, or working with frontend code.
Next.js 16 Cache Components - PPR, use cache directive, cacheLife, cacheTag, updateTag
| Build Shopify apps, extensions, themes using GraphQL Admin API, Shopify CLI, Polaris UI, and Liquid. "shopify theme", "liquid template", "polaris", "shopify graphql", "shopify webhook", "shopify billing", "app subscription", "metafields", "shopify functions"
Build Gradio web UIs and demos in Python. Use when creating or editing Gradio apps, components, event listeners, layouts, or chatbots.
MANDATORY prerequisite — load this skill BEFORE every `generate_diagram` tool call. NEVER call `generate_diagram` directly without loading this skill first. Trigger whenever the user asks to create, generate, draw, render, sketch, or build a diagram — flowchart, architecture diagram, sequence diagram, ERD or entity-relationship diagram, state diagram or state machine, gantt chart, or timeline. Also trigger when the user mentions Mermaid syntax or wants a system architecture, decision tree, dependency graph, API call flow, auth handshake, schema, or pipeline visualized in FigJam. Routes to type-specific guidance, sets universal Mermaid constraints, and tells you when to use a different diagram type or skip the tool entirely (mindmaps, pie charts, class diagrams, etc.).
Analyzes web performance using Chrome DevTools MCP. Measures Core Web Vitals (LCP, INP, CLS) and supplementary metrics (FCP, TBT, Speed Index), identifies render-blocking resources, network dependency chains, layout shifts, caching issues, and accessibility gaps. Use when asked to audit, profile, debug, or optimize page load performance, Lighthouse scores, or site speed. Biases towards retrieval from current documentation over pre-trained knowledge.
Algorithmic philosophies are computational aesthetic movements that are then expressed through code. Output .md files (philosophy), .html files (interactive viewer), and .js files (generative algorithms).
Take coinbase/components.styles from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.