# Format Storybook

> Structure and organize Storybook files for scalability using battle-tested patterns. Based on "A Storybook format that scales with you" by Cassondra Roberts. Always use this skill when creating or editing any Storybook story file, writing template files, organizing a component library, setting up visual regression tests with Chromatic, or when the user asks anything about Storybook — even casual…

- **Type:** Skill
- **Install:** `agentstack add skill-mikemai2awesome-agent-skills-format-storybook`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [mikemai2awesome](https://agentstack.voostack.com/s/mikemai2awesome)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [mikemai2awesome](https://github.com/mikemai2awesome)
- **Source:** https://github.com/mikemai2awesome/agent-skills/tree/main/skills/format-storybook

## Install

```sh
agentstack add skill-mikemai2awesome-agent-skills-format-storybook
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Format Storybook

> Based on "[A Storybook format that scales with you](https://allons-y.llc/posts/2025-10-31/)" by Cassondra Roberts.

This skill provides patterns and conventions for building maintainable Storybook implementations that scale with your component library.

## Core Principles

1. **Compose, don't duplicate** - Import child component templates instead of copying markup
2. **Consistency over perfection** - Pick patterns and stick with them across your library
3. **Progressive disclosure** - Provide sensible defaults with optional controls for edge cases
4. **Self-documenting structure** - Organize files so the architecture is immediately clear

## File Structure

Keep Storybook assets close to component source files:

```
component/
├── component.js
├── component.css
└── stories/
    ├── component.stories.js    # Story definitions, controls, config
    ├── template.js              # Reusable render functions
    ├── component.docs.mdx       # [Optional] Documentation
    └── component.test.js        # [Optional] Visual regression grids
```

## Story File Structure

### Default Export

Every story file needs a default export with metadata:

```javascript
export default {
  title: "Components/Button",           // Sidebar hierarchy
  component: "Button",                  // Component name
  argTypes: { /* control definitions */ },
  args: { /* default values */ },
  parameters: { /* additional config */ },
  tags: [ /* organizational tags */ ]
};
```

### Organizing Controls

Group controls into logical categories for easy navigation:

- **State** - Interactive states (focused, open, disabled)
- **Variant** - Design variations (size, appearance)
- **Content** - Text, images, nested components
- **Advanced** - Edge cases and specialized configs

```javascript
argTypes: {
  size: {
    control: "select",
    options: ["sm", "md", "lg"],
    description: "The size of the button",
    table: { category: "Variant" }
  },
  isDisabled: {
    control: "boolean",
    description: "Whether the button is disabled",
    table: { category: "State" }
  }
}
```

**Tip**: Create shared control files in `.storybook/controls/` to reuse across components.

### Default Values

Always provide sensible defaults so the primary story renders in a useful state:

```javascript
args: {
  size: "md",
  isDisabled: false,
  label: "Click me"
}
```

Avoid meaningless "default" or "normal" options - use `undefined` instead.

### Creating Individual Stories

Use `.bind({})` to create stories that inherit from defaults:

```javascript
export const Default = Template.bind({});
Default.args = {};

export const Disabled = Template.bind({});
Disabled.args = {
  isDisabled: true
};

export const WithIcon = Template.bind({});
WithIcon.args = {
  icon: "settings",
  label: "Settings"
};
```

### Story Naming Conventions

Use concise, descriptive names:

- ✅ **Default** - Primary interactive example
- ✅ **With Icon** - Optional features
- ✅ **Loading State** - Specific states
- ✅ **Error Variant** - Edge cases
- ❌ **Button Default** - Don't repeat component name

### Useful Tags

- `!dev` - Hide stories from sidebar (documentation-only stories)
- `!autodocs` - Exclude from auto-generated docs
- Combine both for visual regression testing grids

## Template Files

The `template.js` file contains your rendering logic.

### Template Structure

Export a primary `Template(args, context)` function:

```javascript
import { html } from "lit";
import { classMap } from "lit/directives/class-map.js";
import { ifDefined } from "lit/directives/if-defined.js";

export const Template = (args, context) => {
  const {
    rootClass = "button",
    id,
    testId,
    customClasses = [],
    customStyles = {},
    size = "md",
    isDisabled = false,
    label = "Button"
  } = args;

  return html`
     ({ ...acc, [c]: true }), {})
      })}
      style=${styleMap(customStyles)}
      id=${ifDefined(id)}
      data-testid=${ifDefined(testId)}
      ?disabled=${isDisabled}
    >
      ${label}
    
  `;
};
```

### Standard Arguments

Accept these common arguments for flexibility:

- `rootClass` - Base CSS class
- `id` and `testId` - Identification and testing
- `customClasses` - Array of additional classes
- `customStyles` - Object of custom CSS properties

### Composition Pattern

Import and render nested component templates:

```javascript
import { Template as Popover } from "@design-system/popover/stories/template.js";
import { Template as Button } from "@design-system/button/stories/template.js";

export const Template = (args, context) => Popover({
  ...args,
  isOpen: true,
  trigger: (passthroughs, ctx) => Button({
    label: "Open Menu",
    ...passthroughs
  }, ctx),
  content: [/* child content */]
}, context);
```

### Lit Directives for Dynamic Behavior

- `classMap` - Conditional classes
- `styleMap` - Inline style objects
- `ifDefined` - Optional attributes (only render when defined)
- `when` - Conditional template regions

For interactive examples, use `context.updateArgs` to toggle state:

```javascript
onclick=${() => context.updateArgs({ isOpen: !args.isOpen })}
```

## Visual Regression Testing

For tools like Chromatic, create test grids in `component.test.js`:

```javascript
import { Template } from "./template.js";

export const TestGrid = {
  render: Template,
  parameters: {
    chromatic: { disableSnapshot: false }
  }
};

export const Default = TestGrid.bind({});
Default.tags = ["!autodocs", "!dev"];
Default.args = {
  // Test-specific args
};
```

**Strategy**: Group many states and variants into a single snapshot grid to optimize test runs.

## Accessibility

Always provide ARIA attributes through arguments:

```javascript
argTypes: {
  ariaHasPopup: {
    control: "select",
    options: ["true", "false", "menu", "dialog"],
    table: { category: "Advanced" }
  },
  ariaExpanded: {
    control: "boolean",
    table: { category: "State" }
  }
}
```

Use `ifDefined` so they only render when needed:

```javascript
aria-haspopup=${ifDefined(args.ariaHasPopup)}
aria-expanded=${ifDefined(args.ariaExpanded)}
```

## Documentation and Metadata

### Design Links

Connect stories to design files:

```javascript
parameters: {
  design: {
    type: "figma",
    url: "https://www.figma.com/..."
  }
}
```

### Package Information

Include metadata for documentation:

```javascript
import packageJson from "../package.json";

export default {
  parameters: {
    packageJson,
    status: { type: "stable" } // or "experimental", "deprecated"
  }
};
```

### Component Status

Use tags to communicate lifecycle:

```javascript
tags: ["stable", "migrated"]
```

## Setup Recommendations

### Path Aliasing

Configure package aliases in Storybook to avoid relative imports:

```javascript
// .storybook/main.js
export default {
  viteFinal: (config) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      "@design-system": path.resolve(__dirname, "../packages")
    };
    return config;
  }
};
```

Use in imports:

```javascript
// ✅ Good
import { Template } from "@design-system/button/stories/template.js";

// ❌ Avoid
import { Template } from "../../../button/stories/template.js";
```

### Rendering Library (Vanilla Projects)

For projects without a framework, use `lit` for templates:

```bash
npm install lit
```

Benefits:
- Lightweight and performant
- Conditional classes and styles
- Optional attributes
- Dynamic rendering utilities

### Auto-Generated Titles

Map folder structure to sidebar hierarchy:

```javascript
// .storybook/main.js
export default {
  stories: [
    {
      directory: '../packages/components',
      files: '*.stories.*',
      titlePrefix: 'Components',
    },
  ],
};
```

## Dos and Don'ts

### Do

- ✅ Import child component templates instead of duplicating markup
- ✅ Keep `template.js` focused on the component itself
- ✅ Categorize controls and set sensible defaults
- ✅ Hide VRT-only stories from sidebar using tags
- ✅ Reuse shared controls from a common location
- ✅ Include proper ARIA attributes
- ✅ Link to design files in parameters

### Don't

- ❌ Hardcode IDs - use a helper function to generate random IDs
- ❌ Duplicate markup from nested components
- ❌ Skip accessibility attributes
- ❌ Create stories for every possible variant (let users explore via controls)
- ❌ Use relative imports when aliases are available
- ❌ Add meaningless "default" or "normal" variant options

## Quick Reference

### Minimal Story File

```javascript
import { Template } from "./template.js";

export default {
  title: "Components/Button",
  component: "Button",
  argTypes: {
    size: {
      control: "select",
      options: ["sm", "md", "lg"],
      table: { category: "Variant" }
    }
  },
  args: {
    size: "md",
    label: "Click me"
  }
};

export const Default = Template.bind({});
```

### Minimal Template File

```javascript
import { html } from "lit";
import { classMap } from "lit/directives/class-map.js";

export const Template = (args) => {
  const { rootClass = "button", size = "md", label = "Button" } = args;
  
  return html`
    
      ${label}
    
  `;
};
```

## References

Read these when you need more detail than the guidelines above:

- [storybook-docs.md](references/storybook-docs.md) - Read when you need to look up specific Storybook APIs, addon configuration, or testing integrations (Chromatic, test runner)
- [lit-templates.md](references/lit-templates.md) - Read when you need details on Lit directives (`classMap`, `styleMap`, `ifDefined`, `when`) or template rendering patterns
- [article.md](references/article.md) - Read when you want the philosophy and reasoning behind these patterns, or need comprehensive end-to-end examples

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [mikemai2awesome](https://github.com/mikemai2awesome)
- **Source:** [mikemai2awesome/agent-skills](https://github.com/mikemai2awesome/agent-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-mikemai2awesome-agent-skills-format-storybook
- Seller: https://agentstack.voostack.com/s/mikemai2awesome
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
