From drupal-core
Storybook patterns for Drupal Single Directory Components (SDC). Covers atomic design hierarchy, story organization, naming conventions, controls matching component props, structural testing via stories, page stories as regression references, and asset handling.
npx claudepluginhub ajv009/drupal-devkitThis skill uses the workspace's default tool permissions.
Stories prove your components work. They are living documentation, visual regression references, and the first place structural problems become visible. A story that renders correctly with realistic content gives confidence; a story that only works with placeholder text hides structural gaps.
Generates CSF3 Storybook stories for React/Vue components with variant coverage and state matrices. Use when creating stories or scanning for missing ones.
Prevents silent decimal mismatch bugs in EVM ERC-20 tokens via runtime decimals lookup, chain-aware caching, bridged-token handling, and normalization. For DeFi bots, dashboards using Python/Web3, TypeScript/ethers, Solidity.
Share bugs, ideas, or general feedback.
Stories prove your components work. They are living documentation, visual regression references, and the first place structural problems become visible. A story that renders correctly with realistic content gives confidence; a story that only works with placeholder text hides structural gaps.
Stories fall into a few categories:
Stories are organized by atomic design level:
| Level | Location | Examples |
|---|---|---|
| Atoms | stories/atoms/ | button, heading, text, image, logo, icon |
| Molecules | stories/molecules/ | card, blockquote, breadcrumb, search-form |
| Organisms | stories/organisms/ | hero, card-grid, two-column-text, stats-banner, header, footer, navigation |
<component-name>.stories.tsx using kebab-caseAtoms/Button, Molecules/Card, Organisms/HeroAuto documentation should be used, and all parameter types must be explicitly defined with appropriate controls. Always cross-reference the story parameters with the SDC *.component.yml props and slots. See references/sdc-component-yml.md for the full schema.
Non-interactive tests (e.g. expects) can be added to component stories, but interactive tests that simulate user input MUST NOT be included in stories.
Both props and slots arguments MUST be either scalar values, an image object type, or a React/Twig fragment of public components. You must never use direct HTML markup in story arguments.
Every prop defined in the component's *.component.yml should have a matching Storybook argType. Use the JSON Schema type to pick the right control:
| JSON Schema type | Storybook control |
|---|---|
type: string | text |
type: string + enum | select or radio |
type: boolean | boolean |
type: integer / type: number | number |
type: array | object (JSON editor) |
type: object | object (JSON editor) |
For slots defined in *.component.yml, represent them as render-function args or children in JSX. Document which slot each arg maps to.
stories/tests/<component-name>.stories.tsx using kebab-case, although if a component has a large number of tests, they can be grouped using <component-name>-<group>.stories.tsx.These are intended to provide automated tests for components and other UI elements.
Auto documentation MUST be disabled.
stories/templates/Templates/stories/pages/<page-name>.stories.tsx using kebab-case.Pages/These provide an example of how a page could be built using components.
Autodocs must be disabled and the fullscreen layout must be used. There must only be one story per page.
A layout wrapper component from the templates stories should be used to ensure consistent wrapping of the page with header, footer, and navigation.
Both props and slots arguments MUST be either scalar values, an image object type, or a React/Twig fragment of public components. You must never use direct HTML markup.
Placehold.co can be used to generate placeholder images for components:
{
src: "https://placehold.co/800x600",
alt: "Example image placeholder",
width: 800,
height: 600,
}
Page stories MUST only import and compose existing components.
themes/custom/mytheme/components/<name>)<div>, <span>, <section>) for layoutIf you need a wrapper <div>, find the existing component that provides that layout (e.g., a section component for width constraints, a grid component for columns). If none exists, create the SDC component first.
Control spacing between components using a spacing or layout component, NOT margins, padding, or wrapper divs.
// Correct — use a layout component
<Hero title="About Us" />
<Section width="normal" content={<Text text="<p>Our story...</p>" />} />
// Wrong — raw HTML and className for spacing
<Hero title="About Us" />
<div className="mt-16">
<Section width="normal" content={<Text text="<p>Our story...</p>" />} />
</div>
Page stories should export only ONE story (typically Default) with a name property matching the page title. This creates flat sidebar navigation instead of nested folders.
export const Default: Story = {
name: 'Home Page',
render: () => (
<PageWrapper>
<Hero title="Welcome" />
<Section width="normal" content={<Text text="<p>...</p>" />} />
</PageWrapper>
),
};
Every key page should have a corresponding page story that serves as a visual regression reference.
text: "Footer" instead of 3 menu columns with social links, it will not catch a structurally inadequate footer component.Stories are the first line of defense -- if a component crashes in Storybook, it will crash on the live site.
npm run build-storybook (Storybook static build) fails, fix before deploying.Assets should be stored in a stories/assets/ directory, in suitable subdirectories depending on the scope of the asset. An index.ts file must be created to export the assets with the correct image type. Real assets must never be directly imported in stories without their image type. The image type must include the correct dimensions and provide alt text.
Given a Drupal SDC card component at themes/custom/mytheme/components/card/:
name: Card
status: stable
props:
type: object
required:
- title
properties:
title:
type: string
title: Card title
description:
type: string
title: Card description
image_url:
type: string
title: Image URL
slots:
actions:
title: Card actions
description: Buttons or links at the bottom of the card.
import type { Meta, StoryObj } from '@storybook/react';
import Card from './Card';
const meta: Meta<typeof Card> = {
title: 'Molecules/Card',
component: Card,
tags: ['autodocs'],
argTypes: {
title: { control: 'text' },
description: { control: 'text' },
image_url: { control: 'text' },
},
};
export default meta;
type Story = StoryObj<typeof Card>;
// Bare minimum — no props, verifies component does not crash with empty data.
export const Empty: Story = {};
// Full realistic content — verifies all structural elements render.
export const FullContent: Story = {
args: {
title: 'Annual Community Report',
description: 'A detailed overview of our community impact this year.',
image_url: 'https://placehold.co/400x300',
},
};
See references/sdc-component-yml.md for the complete *.component.yml schema, including dependencies, libraryOverrides, and advanced prop patterns.