用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/ivanboring/canvas-storybook-ai --skill stories命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
How to build a site's pages and UI to the chosen build approach — faithfully rebuilding an existing source site (like-for-like, pixel-perfect or close-match) or building to a design system. Use when recreating a page or site from an existing source, migrating a design, or building UI to a design system / component library.
Requirements and patterns for creating or modifying React components with Tailwind CSS and CVA
Step-by-step guide for creating new components from scratch including folder structure and naming conventions
正在显示 SKILL.md
| name | stories |
| description | Creating and modifying Storybook stories for components and pages |
Stories fall into a few categories:
src/components/<component_name>/<component_name>.stories.tsx (snake_case, the same base
name as the component)These are intended to provide examples of how to use a component.
Auto documentation should be used, and all parameter types must be explicitly
defined with appropriate controls. Always cross-reference the story parameters
with the <component_name>.component.yml props and slots.
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.
Every component story's meta MUST include a
parameters.docs.description.component string. It renders at the top of the
autodocs page and is the first thing an author reads about the component.
card_container wraps heading
and grid_container).getSiteData(), etc.),
note the data source.table.category groupings (see
"Group argTypes by Canvas vs Internal" below).const meta = {
title: 'Components/Stats',
component: Stats,
parameters: {
docs: {
description: {
component:
'Block of metrics with an optional heading. Items are supplied via the `items` slot using `stat_item` children.',
},
},
},
argTypes: {
/* ... */
},
} satisfies Meta<typeof Stats>;
Every prop the React component accepts MUST appear in argTypes with a
table.category — otherwise it renders uncategorised in the auto-docs args
table. Group each into one of three buckets:
Canvas Props (tables.CANVAS) — props declared under props: in
<component_name>.component.yml, editable by content authors in Canvas.Canvas Slots (tables.SLOT) — slot props declared under slots: in
<component_name>.component.yml; authors drop child components into these.Internal (JSX only) (tables.INTERNAL) — props the component accepts
but that are NOT in <component_name>.component.yml: className, children,
JSX-composition helpers (e.g. a rightColumn node), and test/mock props.Import the shared tables presets from @/stories/argtypes and attach the
right one to each argType — never re-declare the presets inline. Declare the
argTypes grouped in that order (Canvas Props, then Canvas Slots, then Internal),
which is also how the categories sort in the args table.
import { tables } from '@/stories/argtypes';
const meta = {
title: 'Components/Foo',
component: Foo,
argTypes: {
// Canvas Props — mirror component.yml `props:`
variant: {
control: 'select',
options: ['Solid', 'Outline'],
description: 'Visual style variant.',
table: tables.CANVAS,
},
text: { control: 'text', description: 'Label text.', table: tables.CANVAS },
// Canvas Slots — mirror component.yml `slots:`
content: {
control: false,
description: 'Slot for the components rendered inside Foo.',
table: tables.SLOT,
},
// Internal — accepted by the component, not surfaced to Canvas editors
className: {
control: 'text',
description: 'Extra CSS classes appended to the outer element.',
table: tables.INTERNAL,
},
children: {
control: false,
description: 'React nodes rendered inside the component.',
: tables.,
},
},
} < >;
Guidance on control:
<component_name>.component.yml enum):
control: 'select' with an options: […] mirroring the enum values exactly.control: 'text'.control: 'boolean'.control: { type: 'number', min, max } or
control: { type: 'range', min, max, step }.children): control: false.control: 'object'.Autodocs generates a "Hide … items" toggle per category. Storybook does not currently expose a "collapsed by default" API for categories; consider that a known limitation.
Both props and slots arguments MUST be either scalar values, a media object
type, or a React fragment of public components. You MUST NOT use direct markup
or custom styling — all visual presentation must be encapsulated within the
components themselves. Stories MUST only use props and slots defined in the
component's <component_name>.component.yml, unless the prop exists to
facilitate testing or mocking (e.g. injecting test data).
src/components/<component_name>/<component_name>.tests.tsx (snake_case), although if a
component has a large number of tests, they can be grouped using
<component_name>-<group>.tests.tsx. The .tests.tsx suffix (not
.stories.tsx) keeps these test-only stories out of the component-story glob;
they are indexed via testsIndexer in .storybook/main.ts.These are intended to provide automated tests for components and other UI elements.
Auto documentation MUST be disabled.
If a component makes use of the canvas parsing to directly inspect or manipulate components in props, a test should be added to ensure that the component correctly parses the canvas island.
Any component whose layout or controls change by breakpoint MUST be tested at both a desktop and a mobile viewport — never only one. Either re-run the assertions at each width inside a single play function, or provide separate desktop and mobile test stories.
Set the viewport from within the play function:
const setViewport = async (width: number, height: number) => {
const { page } = await import('vitest/browser');
await page.viewport(width, height);
await new Promise((r) => setTimeout(r, 100)); // let the layout settle
};
Assert the breakpoint-specific differences explicitly, including that the control for the other breakpoint is hidden — e.g. a desktop sidebar nav is visible and the mobile dropdown is not, and vice versa. Hidden-when-it-should-show (and the reverse) is a common regression that only surfaces when both widths are tested.
A component that renders differently before hydration (server / no-JavaScript) than once interactive — i.e. progressive enhancement — MUST have its pre-hydration render tested, not just its hydrated one, because that is what search engines and no-JS visitors receive. Storybook always runs JavaScript, so reproduce the no-JS render by preventing the component from upgrading to its interactive state.
When the upgrade is gated on a one-shot requestAnimationFrame (a common
useHydrated-style flag), stub it in the story's beforeEach so the callback
never fires, and restore it in the returned teardown:
beforeEach: () => {
const original = window.requestAnimationFrame;
window.requestAnimationFrame = () => 0; // returns a handle, never calls back
return () => {
window.requestAnimationFrame = original;
};
},
The component then stays in its static/stacked render. Assert the no-JS
contract: it never gains its interactive marker (e.g. an is-interactive
class), all content is reachable (every panel present and visible, not just the
active one), and any navigation degrades to working native anchors (each link's
href targets a real element id). Test this at both desktop and mobile
viewports. A matching no-play story is also useful for visually inspecting the
pre-hydration render.
src/pages<page-name>.stories.tsx using kebab-case.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.
The PageLayout component from the layouts story should be used to ensure consistent wrapping of the page.
Both props and slots arguments MUST be either scalar values, a media object
type, or a React fragment of public components. You MUST NOT use direct markup
or custom styling — all visual presentation must be encapsulated within the
components themselves. Stories MUST only use props and slots defined in the
component's <component_name>.component.yml, unless the prop exists to
facilitate testing or mocking. Pages should be directly composed of components
so that the "show code" view reflects real component usage.
Placeholder.io can be used to generate placeholder images for components:
{
src: "https://placehold.co/800x600",
alt: "Example image placeholder",
width: 800,
height: 600,
}
Assets should be stored in the src/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.