agent-storybook

Expert in Storybook for Angular 21+ - component-driven development, visual testing, and living design system documentation.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "agent-storybook" with this command: npx skills add tidemann/st44-home/tidemann-st44-home-agent-storybook

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 button

  • Components/Forms/Input

  • Nested form input

  • Components/Cards/TaskCard

  • Card components

Pages:

  • Pages/Home

  • Page-level story

  • Pages/Auth/Login

  • Nested page

Design System:

  • Design System/Colors

  • Color palette

  • Design System/Typography

  • Font styles

  • Design System/Spacing

  • Spacing tokens

Examples:

  • Examples/Forms/LoginForm

  • Complete form example

  • Examples/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: &#x3C;div style="display: flex; gap: 1rem; align-items: center;"> &#x3C;app-button size="small" [label]="label">&#x3C;/app-button> &#x3C;app-button size="medium" [label]="label">&#x3C;/app-button> &#x3C;app-button size="large" [label]="label">&#x3C;/app-button> &#x3C;/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: [], }, };

Modal Component Story

CRITICAL: All modal components MUST have Storybook stories.

Modals require special testing because they have multiple interaction points (backdrop, ESC key, close button) that can fail silently.

import type { Meta, StoryObj } from '@storybook/angular'; import { signal } from '@angular/core'; import { AddChildModal } from './add-child-modal';

const meta: Meta<AddChildModal> = { title: 'Components/Modals/AddChildModal', component: AddChildModal, tags: ['autodocs'], argTypes: { open: { control: 'boolean' }, }, // Decorators for modal positioning decorators: [ (story) => ({ template: &#x3C;div style="min-height: 600px; position: relative;"> &#x3C;story /> &#x3C;/div> , }), ], };

export default meta; type Story = StoryObj<AddChildModal>;

// Default: Modal open export const Default: Story = { args: { open: true, }, };

// Closed state export const Closed: Story = { args: { open: false, }, parameters: { docs: { description: { story: 'Modal in closed state (not visible)', }, }, }, };

// With validation errors export const WithValidationErrors: Story = { args: { open: true, }, play: async ({ canvasElement }) => { // Simulate invalid form state for visual testing const canvas = within(canvasElement); const submitButton = await canvas.findByRole('button', { name: /add child/i }); await userEvent.click(submitButton); // Form validation errors should now be visible }, };

Modal Story Testing Checklist:

Test these interactions in Storybook:

  • Open/Close: Toggle open control

  • Close Button: Click X button

  • Backdrop Click: Click outside modal

  • ESC Key: Press Escape

  • Form Validation: Submit empty form

  • Loading State: Test submitting state

  • Error State: Test error messages

  • Accessibility: No violations in a11y addon

Common Modal Story Mistakes:

❌ Wrong: No Storybook story (bugs discovered in production) ✅ Correct: Story created BEFORE integrating into app

❌ Wrong: Only testing open state ✅ Correct: Testing open, closed, loading, error states

❌ Wrong: Not testing close interactions ✅ Correct: Manually test X button, backdrop, ESC in Storybook

See .claude/skills/modal-components/SKILL.md for Modal API usage.

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: &#x3C;div style="display: grid; gap: 1rem;"> &#x3C;div style="display: flex; align-items: center; gap: 1rem;"> &#x3C;div style="width: 100px; height: 100px; background: var(--color-primary); border-radius: 8px;">&#x3C;/div> &#x3C;div> &#x3C;h3>Primary&#x3C;/h3> &#x3C;p>--color-primary: #6366F1&#x3C;/p> &#x3C;/div> &#x3C;/div> &#x3C;div style="display: flex; align-items: center; gap: 1rem;"> &#x3C;div style="width: 100px; height: 100px; background: var(--color-secondary); border-radius: 8px;">&#x3C;/div> &#x3C;div> &#x3C;h3>Secondary&#x3C;/h3> &#x3C;p>--color-secondary: #EC4899&#x3C;/p> &#x3C;/div> &#x3C;/div> &#x3C;!-- Add more colors --> &#x3C;/div> , }), };

export const AllColors: StoryObj = { render: () => ({ template: &#x3C;div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem;"> &#x3C;div style="background: var(--color-primary); padding: 2rem; border-radius: 8px; color: white;"> &#x3C;div style="font-weight: bold;">Primary&#x3C;/div> &#x3C;div style="font-size: 0.875rem;">#6366F1&#x3C;/div> &#x3C;/div> &#x3C;div style="background: var(--color-secondary); padding: 2rem; border-radius: 8px; color: white;"> &#x3C;div style="font-weight: bold;">Secondary&#x3C;/div> &#x3C;div style="font-size: 0.875rem;">#EC4899&#x3C;/div> &#x3C;/div> &#x3C;!-- Add all design system colors --> &#x3C;/div> , }), };

Typography Story

export const Typography: StoryObj = { render: () => ({ template: &#x3C;div style="font-family: var(--font-body);"> &#x3C;h1 style="font-family: var(--font-heading); font-size: 48px; font-weight: 700;"> Heading 1 - Fredoka &#x3C;/h1> &#x3C;h2 style="font-family: var(--font-heading); font-size: 36px; font-weight: 600;"> Heading 2 - Fredoka &#x3C;/h2> &#x3C;p style="font-size: 16px;"> Body text - Outfit Regular &#x3C;/p> &#x3C;p style="font-size: 16px; font-weight: 600;"> Body text - Outfit Semibold &#x3C;/p> &#x3C;/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

  • Storybook for Angular

  • Writing Stories

  • Essential Addons

  • Accessibility Addon

Angular-Specific

  • Angular Standalone Components

  • Angular Signals

  • Angular OnPush Change Detection

Related Skills

Use together with:

  • frontend skill - Angular 21+ component development

  • frontend-design skill - UI design principles

  • ux-design skill - User experience patterns

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

ux-design

No summary provided by upstream source.

Repository SourceNeeds Review
General

frontend-design

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

openclaw-version-monitor

监控 OpenClaw GitHub 版本更新,获取最新版本发布说明,翻译成中文, 并推送到 Telegram 和 Feishu。用于:(1) 定时检查版本更新 (2) 推送版本更新通知 (3) 生成中文版发布说明

Archived SourceRecently Updated
Coding

ask-claude

Delegate a task to Claude Code CLI and immediately report the result back in chat. Supports persistent sessions with full context memory. Safe execution: no data exfiltration, no external calls, file operations confined to workspace. Use when the user asks to run Claude, delegate a coding task, continue a previous Claude session, or any task benefiting from Claude Code's tools (file editing, code analysis, bash, etc.).

Archived SourceRecently Updated