with one click
storybook-stories
// Create, update, or refactor Storybook stories following the project's standard patterns. Use this skill when adding stories for new components, updating existing stories, or fixing Storybook-related issues.
// Create, update, or refactor Storybook stories following the project's standard patterns. Use this skill when adding stories for new components, updating existing stories, or fixing Storybook-related issues.
| name | storybook-stories |
| description | Create, update, or refactor Storybook stories following the project's standard patterns. Use this skill when adding stories for new components, updating existing stories, or fixing Storybook-related issues. |
This skill enforces consistent Storybook story creation patterns across the application. It ensures that all components have proper documentation, interactive examples, and follow the established project structure.
<critical_component_usage> MANDATORY: Always Use Base UI Components from @agh/ui
CRITICAL REQUIREMENTS:
@agh/ui package (packages/ui)@.cursor/rules/react.mdc and @.cursor/rules/shadcn.mdcbg-background, text-foreground, border-border) instead of explicit colors@agh/uibg-white, text-black) - always use design tokensAvailable Base Components:
All components from packages/ui/src/components are available via @agh/ui:
packages/ui/src/index.ts for complete list of exportsDesign System Rules:
@.cursor/rules/react.mdc@.cursor/rules/shadcn.mdcbg-background, text-foreground, border-border, etc.
</critical_component_usage>File Location & Naming
stories/ folder within the same category folder as the component.src/components/base/accordion.tsx -> src/components/base/stories/accordion.stories.tsx.packages/ui/.storybook for packages/ui/src/components/*.stories.tsxweb/.storybook for web/src/components/ui/**/*.stories.tsx and web/src/systems/**/components/stories/*.stories.tsxComponent Imports
@agh/uiimport { Button, Card, Dialog } from "@agh/ui";packages/ui/src/index.ts to see available components before creating new onesMeta Configuration
components/custom/ComponentName or components/ui/ComponentName.component in the meta object.parameters.layout to "centered" by default.parameters.docs.description.component to describe the component.decorators if the component requires a specific container width or context.const meta: Meta<typeof Component> = { ... }web system stories may rely on the shared QueryClient + router + MSW decorators from web/.storybook/preview.ts; prefer those global decorators over per-story provider duplication.Story Definition
type Story = StoryObj<typeof meta>;.Default story as the primary example.args property, even if empty: args: {}systems/<name>/<ComponentName>.Render vs Args
render functions for compound components (like Accordion, Dialog, Select) that require children composition.args for simple components (like Button, Badge) where props define the variation.render, include args: {} propertyDesign System Compliance
bg-background, text-foreground, border-borderbg-white, text-black, border-gray-200@.cursor/rules/shadcn.mdcimport type { Meta, StoryObj } from "@storybook/react";
import { Button, Card, CardHeader, CardTitle, CardContent } from "@agh/ui";
import { MyCustomComponent } from "./my-custom-component";
const meta: Meta<typeof MyCustomComponent> = {
title: "components/custom/MyCustomComponent",
component: MyCustomComponent,
parameters: {
layout: "centered",
docs: {
description: {
component: "A custom component that composes base UI components from @agh/ui.",
},
},
},
// Optional decorator using design tokens
decorators: [
Story => (
<div className="w-[400px] p-4 bg-background border border-border rounded-lg">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<typeof meta>;
/**
* Default usage showing the standard behavior
* Uses base Button and Card components from @agh/ui
*/
export const Default: Story = {
args: {},
render: () => (
<Card>
<CardHeader>
<CardTitle>My Custom Component</CardTitle>
</CardHeader>
<CardContent>
<MyCustomComponent>
<Button variant="default">Action</Button>
</MyCustomComponent>
</CardContent>
</Card>
),
};
/**
* Variation with specific props
* All styling uses design tokens (bg-background, text-foreground, etc.)
*/
export const WithVariant: Story = {
args: {},
render: () => (
<div className="bg-card border border-border rounded-lg p-4">
<MyCustomComponent variant="secondary">
<Button variant="outline">Secondary Action</Button>
</MyCustomComponent>
</div>
),
};
import type { Meta, StoryObj } from "@storybook/react";
import { Button } from "@agh/ui";
const meta: Meta<typeof Button> = {
title: "components/ui/Button",
component: Button,
parameters: {
layout: "centered",
docs: {
description: {
component: "A button component with multiple variants and sizes.",
},
},
},
};
export default meta;
type Story = StoryObj<typeof meta>;
/**
* Default button with standard styling
*/
export const Default: Story = {
args: {
children: "Button",
variant: "default",
size: "default",
},
};
/**
* All variants using design tokens
*/
export const AllVariants: Story = {
args: {},
render: () => (
<div className="flex flex-wrap gap-4 bg-background p-4 rounded-lg">
<Button variant="default">Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="muted">Muted</Button>
</div>
),
};
Use tags: ["autodocs"] only for public packages/ui primitives or stories that intentionally document configuration-heavy public APIs. Do not add it by default to web/src/components/ui or web/src/systems/** stories; that policy comes from ADR-003 and keeps domain-story docs noise low.
Story Count Guidelines:
@agh/ui for existing components before creating new ones@agh/uibg-background, text-foreground, etc.) instead of explicit colorsrender function sets up the component in a way that allows interaction (e.g., not force-controlled unless necessary)meta: const meta: Meta<typeof Component>args: {} in all stories, even when using custom render functions@.cursor/rules/react.mdc@.cursor/rules/shadcn.mdcā Creating components from scratch when base components exist:
// ā BAD: Creating a button from scratch
export const Bad: Story = {
render: () => <button className="bg-blue-500 text-white px-4 py-2 rounded">Click me</button>,
};
// ā
GOOD: Using base Button from @agh/ui
import { Button } from "@agh/ui";
export const Good: Story = {
args: {},
render: () => <Button variant="default">Click me</Button>,
};
../../../.compozy/tasks/storybook-stories/_techspec.md../../../.compozy/tasks/storybook-stories/adrs/adr-001.md../../../.compozy/tasks/storybook-stories/adrs/adr-002.md../../../.compozy/tasks/storybook-stories/adrs/adr-003.md../../../.compozy/tasks/storybook-stories/adrs/adr-004.mdā Using explicit colors instead of design tokens:
// ā BAD: Using explicit colors
<div className="bg-white text-black border-gray-200">
// ā
GOOD: Using design tokens
<div className="bg-background text-foreground border-border">
ā Missing args property:
// ā BAD: Missing args property
export const Bad: Story = {
render: () => <Button>Click</Button>,
};
// ā
GOOD: Including args property
export const Good: Story = {
args: {},
render: () => <Button>Click</Button>,
};
ā Missing explicit type annotation:
// ā BAD: Type inference
const meta = {
title: "Components/Button",
component: Button,
} satisfies Meta<typeof Button>;
// ā
GOOD: Explicit type annotation
const meta: Meta<typeof Button> = {
title: "Components/Button",
component: Button,
};
ā Over-engineering stories with unnecessary examples:
// ā BAD: Too many stories with similar variations
export const Default: Story = { ... };
export const WithIcon: Story = { ... };
export const WithIconLeft: Story = { ... };
export const WithIconRight: Story = { ... };
export const WithLongText: Story = { ... };
export const WithShortText: Story = { ... };
export const Disabled: Story = { ... };
export const Loading: Story = { ... };
export const WithTooltip: Story = { ... };
export const InCard: Story = { ... };
export const InDialog: Story = { ... };
// ... 10+ stories for a simple button
// ā
GOOD: Concise, focused stories
export const Default: Story = {
args: {
children: "Button",
variant: "default",
},
};
export const Variants: Story = {
args: {},
render: () => (
<div className="flex gap-2">
<Button variant="default">Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
</div>
),
};
export const Disabled: Story = {
args: {
children: "Disabled",
disabled: true,
},
};
// Only 3 stories covering essential use cases
ā Over-complicated mock data or scenarios:
// ā BAD: Unnecessarily complex mock data
const mockUsers = [
{ id: "1", name: "John Doe", email: "john@example.com", role: "admin", avatar: "...", lastLogin: "...", permissions: [...], metadata: {...} },
{ id: "2", name: "Jane Smith", email: "jane@example.com", role: "user", avatar: "...", lastLogin: "...", permissions: [...], metadata: {...} },
// ... 10 more users with full data
];
// ā
GOOD: Minimal, realistic data
const mockUsers = [
{ id: "1", name: "John Doe", email: "john@example.com" },
{ id: "2", name: "Jane Smith", email: "jane@example.com" },
];