skills/format-storybook/SKILL.md
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 questions about file structure, controls, args, or how to document a component.
npx skillsauth add mikemai2awesome/agent-skills format-storybookInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Based on "A Storybook format that scales with you" by Cassondra Roberts.
This skill provides patterns and conventions for building maintainable Storybook implementations that scale with your component library.
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
Every story file needs a default export with metadata:
export default {
title: "Components/Button", // Sidebar hierarchy
component: "Button", // Component name
argTypes: { /* control definitions */ },
args: { /* default values */ },
parameters: { /* additional config */ },
tags: [ /* organizational tags */ ]
};
Group controls into logical categories for easy navigation:
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.
Always provide sensible defaults so the primary story renders in a useful state:
args: {
size: "md",
isDisabled: false,
label: "Click me"
}
Avoid meaningless "default" or "normal" options - use undefined instead.
Use .bind({}) to create stories that inherit from defaults:
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"
};
Use concise, descriptive names:
!dev - Hide stories from sidebar (documentation-only stories)!autodocs - Exclude from auto-generated docsThe template.js file contains your rendering logic.
Export a primary Template(args, context) function:
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`
<button
class=${classMap({
[rootClass]: true,
[`${rootClass}--${size}`]: size,
[`${rootClass}--disabled`]: isDisabled,
...customClasses.reduce((acc, c) => ({ ...acc, [c]: true }), {})
})}
style=${styleMap(customStyles)}
id=${ifDefined(id)}
data-testid=${ifDefined(testId)}
?disabled=${isDisabled}
>
${label}
</button>
`;
};
Accept these common arguments for flexibility:
rootClass - Base CSS classid and testId - Identification and testingcustomClasses - Array of additional classescustomStyles - Object of custom CSS propertiesImport and render nested component templates:
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);
classMap - Conditional classesstyleMap - Inline style objectsifDefined - Optional attributes (only render when defined)when - Conditional template regionsFor interactive examples, use context.updateArgs to toggle state:
onclick=${() => context.updateArgs({ isOpen: !args.isOpen })}
For tools like Chromatic, create test grids in component.test.js:
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.
Always provide ARIA attributes through arguments:
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:
aria-haspopup=${ifDefined(args.ariaHasPopup)}
aria-expanded=${ifDefined(args.ariaExpanded)}
Connect stories to design files:
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/..."
}
}
Include metadata for documentation:
import packageJson from "../package.json";
export default {
parameters: {
packageJson,
status: { type: "stable" } // or "experimental", "deprecated"
}
};
Use tags to communicate lifecycle:
tags: ["stable", "migrated"]
Configure package aliases in Storybook to avoid relative imports:
// .storybook/main.js
export default {
viteFinal: (config) => {
config.resolve.alias = {
...config.resolve.alias,
"@design-system": path.resolve(__dirname, "../packages")
};
return config;
}
};
Use in imports:
// ✅ Good
import { Template } from "@design-system/button/stories/template.js";
// ❌ Avoid
import { Template } from "../../../button/stories/template.js";
For projects without a framework, use lit for templates:
npm install lit
Benefits:
Map folder structure to sidebar hierarchy:
// .storybook/main.js
export default {
stories: [
{
directory: '../packages/components',
files: '*.stories.*',
titlePrefix: 'Components',
},
],
};
template.js focused on the component itselfimport { 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({});
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`
<button class=${classMap({
[rootClass]: true,
[`${rootClass}--${size}`]: size
})}>
${label}
</button>
`;
};
Read these when you need more detail than the guidelines above:
classMap, styleMap, ifDefined, when) or template rendering patternsdevelopment
Implement accessibility in iOS apps using Swift, UIKit, and SwiftUI. Use this skill whenever working on any iOS development task that involves: making UI elements accessible to VoiceOver or other assistive technologies, adding or reviewing accessibility labels/hints/traits/actions/values, supporting Dynamic Type or text scaling, respecting Reduce Motion or reduced transparency preferences, adapting to Dark Mode or increased contrast, building accessible forms and inputs, announcing dynamic content changes, managing focus programmatically, customizing accessibility focus order, supporting external keyboard navigation, or auditing iOS code for accessibility issues. Trigger even when the user only says "SwiftUI" or "UIKit" without mentioning "accessibility" explicitly — if they're building custom controls, modals, forms, lists, or animated views, this skill applies.
development
Write minimal, efficient CSS for small or minimalist projects by trusting the browser instead of fighting it. Only use this skill for personal sites, prototypes, simple landing pages, or projects intentionally kept lean — if the project has multiple developers, a component library, a design token system, or more than a handful of CSS files, a more comprehensive CSS approach is needed. If you're about to write a CSS reset, declare a base font-size on :root, set default colors on body, use px for spacing, or reach for physical margin/padding properties, this skill will stop you.
development
Write scalable, well-architected CSS using design tokens, cascade layers, and modern organization patterns. Use this skill as the default for any real project — if it has more than a handful of CSS files, multiple components, a team, a design system, or any kind of token or theming setup, this is the right skill.
development
Create web interfaces with an authentic early-2010s aesthetic. Use this skill when the user wants a 2010s-era, Web 2.0, or retro corporate web look — gradient headers, glossy buttons, skeuomorphic icons, horizontal band layouts, and drop shadows from circa 2010–2014.