Agent skill
storybook-development
Create component documentation and testing with Storybook using CSF3, interaction testing, and visual regression. Use when documenting components, creating UI catalog, or visual testing.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/design/storybook-development-nekorush14-dotfiles
SKILL.md
Storybook Development Specialist
Specialized in creating component documentation and visual testing with Storybook.
When to Use This Skill
- Documenting React components
- Creating component UI catalog
- Visual regression testing
- Component-driven development
- Testing component variants and states
- Sharing component library with team
- Testing interaction and accessibility
Core Principles
- Component Isolation: Test components in isolation
- Visual Documentation: Show all component states
- Interactive: Allow props manipulation with controls
- Accessible: Document accessibility features
- Testable: Include interaction tests
- Discoverable: Organize stories logically
Storybook Setup
# Initialize Storybook with Vite
npx storybook@latest init --builder vite
# Install additional addons
npm install -D @storybook/addon-a11y
npm install -D @storybook/test
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite'
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-a11y',
],
framework: {
name: '@storybook/react-vite',
options: {},
},
docs: {
autodocs: 'tag',
},
}
export default config
CSF3 Stories
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from './Button'
// Meta configuration
const meta = {
title: 'Components/Button',
component: Button,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger'],
},
size: {
control: 'radio',
options: ['sm', 'md', 'lg'],
},
},
} satisfies Meta<typeof Button>
export default meta
type Story = StoryObj<typeof meta>
// Stories
export const Primary: Story = {
args: {
variant: 'primary',
children: 'Button',
},
}
export const Secondary: Story = {
args: {
variant: 'secondary',
children: 'Button',
},
}
export const Large: Story = {
args: {
size: 'lg',
children: 'Large Button',
},
}
export const Disabled: Story = {
args: {
disabled: true,
children: 'Disabled Button',
},
}
Args and Controls
// Card.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { Card } from './Card'
const meta = {
title: 'Components/Card',
component: Card,
args: {
// Default args for all stories
title: 'Card Title',
description: 'Card description goes here',
},
argTypes: {
title: {
control: 'text',
description: 'The card title',
},
description: {
control: 'text',
description: 'The card description',
},
variant: {
control: 'select',
options: ['default', 'highlighted', 'muted'],
description: 'Visual variant of the card',
},
onClick: { action: 'clicked' },
},
} satisfies Meta<typeof Card>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {}
export const Highlighted: Story = {
args: {
variant: 'highlighted',
title: 'Highlighted Card',
},
}
export const WithLongContent: Story = {
args: {
title: 'Long Content Card',
description:
'This is a very long description that demonstrates how the card handles lengthy content. ' +
'It should wrap properly and maintain good readability.',
},
}
Decorators
// Global decorator (.storybook/preview.tsx)
import React from 'react'
import type { Preview } from '@storybook/react'
import { ThemeProvider } from '../src/contexts/ThemeProvider'
const preview: Preview = {
decorators: [
(Story) => (
<ThemeProvider>
<div style={{ padding: '3rem' }}>
<Story />
</div>
</ThemeProvider>
),
],
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
}
export default preview
// Story-specific decorator
export const DarkMode: Story = {
decorators: [
(Story) => (
<div className="dark" style={{ background: '#1a1a1a', padding: '2rem' }}>
<Story />
</div>
),
],
args: {
children: 'Dark Mode Button',
},
}
Interaction Testing
import { expect, userEvent, within } from '@storybook/test'
import type { Meta, StoryObj } from '@storybook/react'
import { LoginForm } from './LoginForm'
const meta = {
title: 'Forms/LoginForm',
component: LoginForm,
} satisfies Meta<typeof LoginForm>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {}
export const FilledForm: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
// WHY: Simulate user filling out the form
await userEvent.type(canvas.getByLabelText('Email'), '[email protected]')
await userEvent.type(canvas.getByLabelText('Password'), 'password123')
await expect(canvas.getByLabelText('Email')).toHaveValue('[email protected]')
},
}
export const SubmitForm: Story = {
args: {
onSubmit: async (data) => {
console.log('Form submitted:', data)
},
},
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement)
await userEvent.type(canvas.getByLabelText('Email'), '[email protected]')
await userEvent.type(canvas.getByLabelText('Password'), 'password123')
await userEvent.click(canvas.getByRole('button', { name: /submit/i }))
// Verify submission was called
await expect(args.onSubmit).toHaveBeenCalled()
},
}
export const ValidationErrors: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
// Submit without filling
await userEvent.click(canvas.getByRole('button', { name: /submit/i }))
// Check for validation errors
await expect(canvas.getByText(/email is required/i)).toBeInTheDocument()
await expect(canvas.getByText(/password is required/i)).toBeInTheDocument()
},
}
Parameters
export const Centered: Story = {
parameters: {
layout: 'centered', // Center in canvas
},
}
export const FullWidth: Story = {
parameters: {
layout: 'fullscreen', // Full viewport
},
}
export const CustomBackground: Story = {
parameters: {
backgrounds: {
default: 'dark',
values: [
{ name: 'light', value: '#ffffff' },
{ name: 'dark', value: '#1a1a1a' },
],
},
},
}
export const WithViewport: Story = {
parameters: {
viewport: {
defaultViewport: 'mobile1',
},
},
}
Documenting with MDX
<!-- Button.stories.mdx -->
import { Canvas, Meta, Story } from '@storybook/blocks'
import * as ButtonStories from './Button.stories'
<Meta of={ButtonStories} />
# Button
The Button component is a versatile UI element used for user interactions.
## Usage
```tsx
import { Button } from '@/components/Button'
<Button variant="primary">Click me</Button>
Variants
Sizes
Props
Visual Regression Testing
// Button.stories.tsx
export const AllVariants: Story = {
render: () => (
<div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Danger</Button>
<Button variant="primary" disabled>
Disabled
</Button>
</div>
),
parameters: {
// Configure for visual regression
chromatic: {
viewports: [320, 1200],
},
},
}
// Disable snapshots for interactive stories
export const Interactive: Story = {
parameters: {
chromatic: { disableSnapshot: true },
},
}
Accessibility Testing
// .storybook/preview.tsx
const preview: Preview = {
parameters: {
a11y: {
config: {
rules: [
{
id: 'color-contrast',
enabled: true,
},
],
},
},
},
}
// Story with specific a11y parameters
export const HighContrast: Story = {
parameters: {
a11y: {
config: {
rules: [
{
id: 'color-contrast',
enabled: true,
options: { level: 'AAA' },
},
],
},
},
},
}
Component Variants Pattern
// Showing all component states
export const AllStates: Story = {
render: () => (
<div style={{ display: 'grid', gap: '2rem' }}>
<div>
<h3>Default</h3>
<Input placeholder="Enter text" />
</div>
<div>
<h3>With Value</h3>
<Input value="Sample text" />
</div>
<div>
<h3>Disabled</h3>
<Input disabled placeholder="Disabled input" />
</div>
<div>
<h3>Error</h3>
<Input error="This field is required" placeholder="Error state" />
</div>
<div>
<h3>Loading</h3>
<Input loading placeholder="Loading..." />
</div>
</div>
),
}
Mocking Data
import { http, HttpResponse } from 'msw'
export const WithMockedData: Story = {
parameters: {
msw: {
handlers: [
http.get('/api/users', () => {
return HttpResponse.json([
{ id: '1', name: 'John Doe' },
{ id: '2', name: 'Jane Smith' },
])
}),
],
},
},
}
Addon Integration
// .storybook/main.ts
const config: StorybookConfig = {
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials', // Essential addons
'@storybook/addon-interactions', // Interaction testing
'@storybook/addon-a11y', // Accessibility testing
'@storybook/addon-coverage', // Code coverage
],
}
Tools to Use
Read: Read component codeWrite: Create story filesEdit: Update storiesBash: Run Storybook and build
Bash Commands
# Run Storybook
npm run storybook
# Build Storybook
npm run build-storybook
# Run interaction tests
npm run test-storybook
# Run with coverage
npm run test-storybook -- --coverage
# Build and deploy
npm run build-storybook && npx http-server storybook-static
Workflow
- Create Component: Build React component
- Write Story: Create
.stories.tsxfile - Add Variants: Document all component states
- Add Controls: Configure argTypes for interactivity
- Add Interactions: Test user interactions
- Test Accessibility: Use a11y addon
- Visual Regression: Integrate Chromatic
- Document: Add MDX documentation
- Share: Build and deploy Storybook
Related Skills
react-component-development: For component implementationvitest-react-testing: For unit testsreact-styling: For component stylingplaywright-testing: For E2E tests
Coding Standards
See React Coding Standards
Key Reminders
- Use CSF3 format for stories (modern, type-safe)
- Document all component variants and states
- Use Controls for interactive props
- Implement interaction tests with
playfunction - Use decorators for global wrappers (themes, providers)
- Configure parameters for layout, backgrounds, viewports
- Enable autodocs with
tags: ['autodocs'] - Test accessibility with a11y addon
- Use MSW for mocking API requests
- Organize stories with clear hierarchy (title)
- Keep stories focused on single component
- Show realistic usage examples
- Test dark mode and responsive variants
- Integrate visual regression testing (Chromatic)
- Write comments explaining WHY for complex story setup
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?