| name | agent-storybook |
| description | Storybook expert for Angular 21+ component development, visual testing, and design system documentation |
| tags | storybook, angular, components, testing, design-system, frontend |
| version | 1.0.0 |
Storybook Skill
Expert in Storybook for Angular 21+ - component-driven development, visual testing, and living design system documentation.
When to Use This Skill
Use this skill when:
- Creating or updating component stories
- Setting up Storybook for a project
- Creating design system documentation stories
- Visual testing and accessibility testing
- Troubleshooting Storybook issues
- Configuring Storybook addons
- Writing component documentation
Core Principles
Component-Driven Development
- Build components in isolation before integrating into app
- Test all component states and variants
- Document component API (inputs, outputs, methods)
- Use Storybook as the single source of truth for UI components
Story Best Practices
- One story file per component (
.stories.ts) - Export multiple story variants showing different states
- Use descriptive story names (Primary, Disabled, Loading, Error, etc.)
- Include all edge cases and states
- Add accessibility testing to every story
Project Structure
Standard Storybook Setup
apps/frontend/
├── .storybook/
│ ├── main.ts # Main configuration
│ ├── preview.ts # Global decorators & parameters
│ └── manager.ts # UI customization (optional)
├── src/
│ └── app/
│ ├── components/
│ │ └── button/
│ │ ├── button.component.ts
│ │ └── button.stories.ts
│ └── pages/
│ └── home/
│ ├── home.component.ts
│ └── home.stories.ts
Story Naming Convention
Hierarchical Structure
Use / separator for organization:
Components:
Components/Button- Basic buttonComponents/Forms/Input- Nested form inputComponents/Cards/TaskCard- Card components
Pages:
Pages/Home- Page-level storyPages/Auth/Login- Nested page
Design System:
Design System/Colors- Color paletteDesign System/Typography- Font stylesDesign System/Spacing- Spacing tokens
Examples:
Examples/Forms/LoginForm- Complete form exampleExamples/Layouts/Dashboard- Layout example
Story Template (Angular 21+ Standalone)
Basic Component Story
import type { Meta, StoryObj } from '@storybook/angular';
import { ButtonComponent } from './button.component';
const meta: Meta<ButtonComponent> = {
title: 'Components/Button',
component: ButtonComponent,
tags: ['autodocs'], // Automatic documentation
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger'],
description: 'Button visual style',
},
size: {
control: 'select',
options: ['small', 'medium', 'large'],
},
disabled: {
control: 'boolean',
},
loading: {
control: 'boolean',
},
},
};
export default meta;
type Story = StoryObj<ButtonComponent>;
// Primary state
export const Primary: Story = {
args: {
label: 'Click me',
variant: 'primary',
size: 'medium',
},
};
// Secondary state
export const Secondary: Story = {
args: {
label: 'Click me',
variant: 'secondary',
},
};
// Disabled state
export const Disabled: Story = {
args: {
label: 'Click me',
disabled: true,
},
};
// Loading state
export const Loading: Story = {
args: {
label: 'Please wait...',
loading: true,
},
};
// All sizes comparison
export const AllSizes: Story = {
render: (args) => ({
props: args,
template: `
<div style="display: flex; gap: 1rem; align-items: center;">
<app-button size="small" [label]="label"></app-button>
<app-button size="medium" [label]="label"></app-button>
<app-button size="large" [label]="label"></app-button>
</div>
`,
}),
args: {
label: 'Click me',
},
};
Component with Services/Dependencies
import type { Meta, StoryObj } from '@storybook/angular';
import { applicationConfig } from '@storybook/angular';
import { provideHttpClient } from '@angular/common/http';
import { TaskCardComponent } from './task-card.component';
const meta: Meta<TaskCardComponent> = {
title: 'Components/Cards/TaskCard',
component: TaskCardComponent,
tags: ['autodocs'],
decorators: [
applicationConfig({
providers: [
provideHttpClient(),
// Add other providers here
],
}),
],
};
export default meta;
type Story = StoryObj<TaskCardComponent>;
export const Default: Story = {
args: {
task: {
id: '1',
title: 'Clean bathroom',
assignee: 'Alex',
points: 10,
completed: false,
},
},
};
export const Completed: Story = {
args: {
task: {
id: '2',
title: 'Take out trash',
assignee: 'Sarah',
points: 5,
completed: true,
},
},
};
Complex Layout Story
import type { Meta, StoryObj } from '@storybook/angular';
import { HomeComponent } from './home.component';
import { TaskCardComponent } from '../../components/task-card/task-card.component';
import { StatCardComponent } from '../../components/stat-card/stat-card.component';
const meta: Meta<HomeComponent> = {
title: 'Pages/Home',
component: HomeComponent,
tags: ['autodocs'],
parameters: {
layout: 'fullscreen', // Use full viewport
},
decorators: [
applicationConfig({
providers: [
/* ... */
],
}),
],
};
export default meta;
type Story = StoryObj<HomeComponent>;
export const Default: Story = {};
export const WithManyTasks: Story = {
args: {
tasks: [
{ id: '1', title: 'Task 1' /* ... */ },
{ id: '2', title: 'Task 2' /* ... */ },
{ id: '3', title: 'Task 3' /* ... */ },
],
},
};
export const EmptyState: Story = {
args: {
tasks: [],
},
};
Design System Documentation
Color Palette Story
import type { Meta, StoryObj } from '@storybook/angular';
const meta: Meta = {
title: 'Design System/Colors',
tags: ['autodocs'],
};
export default meta;
export const Primary: StoryObj = {
render: () => ({
template: `
<div style="display: grid; gap: 1rem;">
<div style="display: flex; align-items: center; gap: 1rem;">
<div style="width: 100px; height: 100px; background: var(--color-primary); border-radius: 8px;"></div>
<div>
<h3>Primary</h3>
<p>--color-primary: #6366F1</p>
</div>
</div>
<div style="display: flex; align-items: center; gap: 1rem;">
<div style="width: 100px; height: 100px; background: var(--color-secondary); border-radius: 8px;"></div>
<div>
<h3>Secondary</h3>
<p>--color-secondary: #EC4899</p>
</div>
</div>
<!-- Add more colors -->
</div>
`,
}),
};
export const AllColors: StoryObj = {
render: () => ({
template: `
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem;">
<div style="background: var(--color-primary); padding: 2rem; border-radius: 8px; color: white;">
<div style="font-weight: bold;">Primary</div>
<div style="font-size: 0.875rem;">#6366F1</div>
</div>
<div style="background: var(--color-secondary); padding: 2rem; border-radius: 8px; color: white;">
<div style="font-weight: bold;">Secondary</div>
<div style="font-size: 0.875rem;">#EC4899</div>
</div>
<!-- Add all design system colors -->
</div>
`,
}),
};
Typography Story
export const Typography: StoryObj = {
render: () => ({
template: `
<div style="font-family: var(--font-body);">
<h1 style="font-family: var(--font-heading); font-size: 48px; font-weight: 700;">
Heading 1 - Fredoka
</h1>
<h2 style="font-family: var(--font-heading); font-size: 36px; font-weight: 600;">
Heading 2 - Fredoka
</h2>
<p style="font-size: 16px;">
Body text - Outfit Regular
</p>
<p style="font-size: 16px; font-weight: 600;">
Body text - Outfit Semibold
</p>
</div>
`,
}),
};
Storybook Configuration
Main Configuration (main.ts)
import type { StorybookConfig } from '@storybook/angular';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-a11y',
'@storybook/addon-interactions',
'@storybook/addon-links',
],
framework: {
name: '@storybook/angular',
options: {},
},
docs: {
autodocs: 'tag', // Generate docs for components with 'autodocs' tag
},
};
export default config;
Preview Configuration (preview.ts)
import type { Preview } from '@storybook/angular';
import { setCompodocJson } from '@storybook/addon-docs/angular';
import docJson from '../documentation.json';
setCompodocJson(docJson);
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
backgrounds: {
default: 'light',
values: [
{ name: 'light', value: '#F8F9FF' },
{ name: 'dark', value: '#1E293B' },
{ name: 'white', value: '#FFFFFF' },
],
},
viewport: {
viewports: {
mobile: {
name: 'Mobile',
styles: { width: '375px', height: '667px' },
type: 'mobile',
},
tablet: {
name: 'Tablet',
styles: { width: '768px', height: '1024px' },
type: 'tablet',
},
desktop: {
name: 'Desktop',
styles: { width: '1440px', height: '900px' },
type: 'desktop',
},
},
},
},
};
export default preview;
Essential Addons
@storybook/addon-essentials
Includes: Controls, Actions, Viewport, Backgrounds, Toolbars, Measure, Outline
Usage:
- Controls - Interactive component props
- Actions - Log component events (@Output)
- Viewport - Test responsive behavior
- Backgrounds - Test on different backgrounds
@storybook/addon-a11y
Accessibility testing with axe-core
Usage:
export const MyComponent: Story = {
parameters: {
a11y: {
config: {
rules: [
{
id: 'color-contrast',
enabled: true,
},
],
},
},
},
};
Check for:
- Color contrast (WCAG AA: 4.5:1 for text, 3:1 for UI)
- ARIA labels
- Keyboard navigation
- Focus indicators
- Semantic HTML
@storybook/addon-interactions
Test user interactions
import { within, userEvent } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
export const ClickButton: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByRole('button');
await userEvent.click(button);
await expect(button).toHaveClass('clicked');
},
};
Component Development Workflow
Step 1: Create Component
# Generate component
cd apps/frontend
ng generate component components/button --standalone
Step 2: Create Story
# Create story file
touch src/app/components/button/button.stories.ts
Step 3: Develop in Storybook
# Run Storybook
npm run storybook
# Storybook opens at localhost:6006
# Hot reload automatically updates as you code
Step 4: Test All States
Create stories for:
- Default state
- All variants (primary, secondary, danger, etc.)
- All sizes (small, medium, large)
- Disabled state
- Loading state
- Error state
- Empty state (if applicable)
- With long text / edge cases
Step 5: Accessibility Check
- Use a11y addon (bottom panel)
- Check all violations
- Fix color contrast issues
- Add ARIA labels
- Test keyboard navigation
Step 6: Visual Review
- Test on all viewports (mobile, tablet, desktop)
- Test on all backgrounds (light, dark, white)
- Check hover states
- Check focus states
- Check animations
Step 7: Integration
Once component is complete in Storybook:
- Import into parent component
- Use in actual app
- E2E test in real context
Common Commands
Development
# Start Storybook dev server
npm run storybook
# Build static Storybook
npm run build-storybook
# Generate Compodoc documentation
npm run storybook:docs
# Run both Compodoc and Storybook
npm run storybook:docs && npm run storybook
Story Creation
# Create new story file
touch src/app/components/my-component/my-component.stories.ts
# Follow naming convention: {component-name}.stories.ts
Troubleshooting
Issue: Component not rendering
Solution:
- Check that component is standalone or imported in moduleMetadata
- Check that all dependencies are provided (services, HTTP client)
- Check console for errors
Issue: Styles not loading
Solution:
- Import global styles in
preview.ts - Check component styleUrls path
- Ensure CSS variables are defined
Issue: Compodoc failing
Solution:
# Regenerate documentation
npm run storybook:docs
# Check for TypeScript errors
npm run type-check
Issue: Hot reload not working
Solution:
- Restart Storybook
- Clear browser cache
- Check for conflicting ports
Best Practices Checklist
Every Component Story Should Have:
- Multiple story variants (Default, Disabled, Loading, etc.)
- All input combinations tested
- Accessibility testing enabled (a11y addon)
- Proper argTypes with descriptions
- Tags: ['autodocs'] for automatic documentation
- JSDoc comments on @Input() and @Output()
Every Design System Story Should Have:
- Visual representation
- Code examples
- Usage guidelines
- Do's and Don'ts (optional)
Story Organization:
- Hierarchical naming (Components/, Pages/, Design System/)
- Alphabetical ordering within categories
- Consistent naming convention
Integration with CI/CD
Build Storybook in CI
# GitHub Actions example
- name: Build Storybook
run: |
cd apps/frontend
npm run storybook:docs
npm run build-storybook
Deploy Storybook
# Build static version
npm run build-storybook
# Deploy to static hosting (Netlify, Vercel, GitHub Pages)
# Output directory: storybook-static/
Visual Regression Testing
Consider adding:
- Chromatic (visual testing platform)
- Percy (visual review)
- Storybook test runner
Success Criteria
After implementing this skill:
- ✅ All components have corresponding stories
- ✅ Design system fully documented
- ✅ Accessibility testing on every component
- ✅ Visual regression testing enabled
- ✅ Developers can create stories in < 5 minutes
- ✅ Storybook deployed for team collaboration
References
Official Documentation
Angular-Specific
Related Skills
Use together with:
- frontend skill - Angular 21+ component development
- frontend-design skill - UI design principles
- ux-design skill - User experience patterns