Skip to main content

stories-documentation

Full authoring procedure for the per-unit MDX docs page of gen2 components, internal components, patterns, and controllers: documentation structure, the Helpers section, section patterns (overview, anatomy, options, states, behaviors, accessibility), 1st-gen to gen2 comparison, verification against source so docs don't claim attributes, slots, or ARIA that don't exist, and general writing guidelines. Use when writing, migrating, or reviewing a gen2 `<unit>.mdx` docs page.

Source facts

Repository
adobe/spectrum-web-components
Last source activity
October 6, 2026 at 14:31
Detected SKILL.md language
English
Stars
1,576
Forks
274

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
stories-documentation
description
Full authoring procedure for the per-unit MDX docs page of gen2 components, internal components, patterns, and controllers: documentation structure, the Helpers section, section patterns (overview, anatomy, options, states, behaviors, accessibility), 1st-gen to gen2 comparison, verification against source so docs don't claim attributes, slots, or ARIA that don't exist, and general writing guidelines. Use when writing, migrating, or reviewing a gen2 `<unit>.mdx` docs page.
# Per-unit MDX authoring procedure This skill holds the full procedure. The always-apply constraints are in `.ai/rules/stories-documentation.md`, which loads automatically for matching files. ## Documentation structure Organize MDX into these sections (skip sections that don't apply): 1. **Overview** - rendered automatically by `<DocsHeader />` from the meta JSDoc, subtitle, and Overview story 2. **Anatomy** - A flat, unordered list of the component's parts, with slot names called out inline where needed (components and patterns) 3. **Usage** - How to instantiate and configure (controllers) 4. **Options** - Configuration, variants, sizes, styles 5. **States** - Different component states 6. **Behaviors** - Methods, events, automatic behaviors 7. **Accessibility** - Features and best practices 8. **Upcoming features** - Roadmap intent (prose only, no Canvas) 9. **API** - Rendered automatically by `<DocsFooter />`. Controllers hand-author a `## API` section first. 10. **Feedback** - Rendered automatically by `<DocsFooter />`. The remaining sections of this rule describe the **prose content** for each section. Author that prose inside the per-unit MDX, not as JSDoc above stories. ## Helpers section **Purpose**: Organize shared code, label mappings, and utilities used across multiple stories. **Location**: Between `export default meta` and first story (Playground/Autodocs). **When to use:** - Component has multiple variants, sizes, or options that need consistent labels - Stories require reusable label mappings or data structures - Complex rendering logic needs to be shared across stories **Pattern**: ```typescript // ──────────────────── // HELPERS // ──────────────────── // Label mappings for sizes const sizeLabels = { s: 'Small', m: 'Medium', l: 'Large', xl: 'Extra-large', } as const satisfies Record<ComponentSize, string>; // Label mappings for semantic variants const semanticLabels = { positive: 'Approved', negative: 'Rejected', informative: 'Active', neutral: 'Archived', notice: 'Pending', } as const satisfies Record<SemanticVariant, string>; // Combined label objects for convenience const allLabels = { ...semanticLabels, ...colorLabels }; ``` **Key principles:** - Use TypeScript `as const satisfies` for type-safe label mappings - Group related helpers together (size labels, variant labels, etc.) - Use clear, descriptive constant names (`sizeLabels`, not `labels`) - Provide realistic, meaningful labels (not "Label 1", "Label 2") **When NOT to use:** - Component has only 1-2 options (no need for label mappings) - Labels are self-explanatory (e.g., true/false) - Stories don't share any common code ## Section patterns > **Where the prose lives.** Each pattern below shows a "prose" block. Author that prose as Markdown inside the corresponding `## Section` / `### Story Title` in the `<unit>.mdx` file, immediately above the `<Canvas of={Stories.MyStory} />` reference. The accompanying story definition has no JSDoc above it. ### Overview **Purpose**: Quick introduction showing the component in its most common use case. **Documentation**: The Overview story content comes from two sources: 1. **JSDoc description above meta**: Provides the main description of what the component does and when to use it. This description: - Is displayed as the primary documentation for the Overview story - Can include markdown formatting and links to other components - Should reference other components using Storybook paths: `[Badge](../?path=/docs/components-badge--docs)` - Should provide fuller context than the subtitle 2. **`parameters.docs.subtitle`**: Provides a brief summary displayed as the subtitle. This subtitle: - Cannot include links (plain text only) - Should be concise and to-the-point - Should not repeat the JSDoc description verbatim - Complements the JSDoc description **Pattern**: ```typescript /** * A badge is a non-interactive visual label that displays a status, category, or attribute. * Badges can be used to highlight important information or to categorize items. For interactive * labels, see [Button](../?path=/docs/components-button--docs). */ const meta: Meta = { title: 'Badge', component: 'swc-badge', args, argTypes, render: (args) => template(args), parameters: { docs: { subtitle: `Visual label for status, category, or attribute` }, // ... other parameters }, tags: ['migrated'], }; export default meta; // ... HELPERS section if needed ... export const Overview: Story = { tags: ['overview'], args: { // Most common/representative configuration }, }; ``` **Note**: the Overview story renders into the docs page header via `<OverviewStory />` inside `<DocsHeader />`. The meta-level JSDoc (above `const meta`) and the `parameters.docs.subtitle` provide the surrounding prose; the Overview story itself just supplies the canvas. ### Anatomy **Purpose**: Give the reader a mental model of the component's parts in plain language. Anatomy is not a second copy of the API table: the generated `<ApiTable />` (rendered by `<DocsFooter />`) already lists every slot and property from source with type and description. Anatomy should not duplicate it. **Required content:** - A flat, unordered list of the component's visual parts - Slot names are called out **inline** only in places where a reader needs to know a piece of content comes from a slot (so they write it as markup) rather than a property (so they write it as an attribute); not a separate slot inventory - Visual examples showing structure **Consolidation rule**: Combine all slotted content combinations into a **single Anatomy story**. **MDX prose** (`## Anatomy` section uses `hideTitle` semantics: no per-story `###`, and no `###`/`####` subsections of any kind): ```mdx ## Anatomy A component-name consists of: - **Primary element**: main visual component - **Secondary element**, provided via the `icon` slot - **Optional indicator**: shown conditionally <Canvas of={Stories.Anatomy} /> ``` **Story definition** (no JSDoc above it): ```typescript export const Anatomy: Story = { render: (args) => html` ${template({ ...args /* minimal */ })} ${template({ ...args /* with icon */ })} ${template({ ...args /* with additional content */ })} `, tags: ['anatomy'], parameters: { flexLayout: 'row-wrap', }, }; ``` **Key principles:** - Use a flat, unordered list of parts. Never a numbered list: there is no numbered diagram for the numbers to key off of, and it reads oddly for single-part components. Never `###`/`####` subsections (e.g. no "Content", "Slots", or "Properties" headings). Doing so re-creates the API table by hand and drifts out of sync with it. - Name a slot inline in the parts list only when a reader needs to know content is passed as a slot rather than a property to compose it correctly - Don't re-list content-rendering properties that are already visible in the rendered example or covered by the API table - Show all meaningful combinations in one story ### Options **Purpose**: Document every attribute or property not covered in Anatomy, States, or Behaviors. **Order**: Sizes → Semantic variants → Non-semantic variants → Quiet/Subtle/Emphasized → Outline → Static color → Positioning → Other options **Consolidation rules**: - All sizes → single `Sizes` story - All semantic variants → single `SemanticVariants` story - All non-semantic variants → single `NonSemanticVariants` story **Layout requirement**: Use `flexLayout: 'row-wrap'` for stories displaying multiple variations. **Pattern for sizes**: ```mdx ### Sizes Component-names come in [X] sizes to fit various contexts: - **Small (`s`)**: Used for inline indicators or space-constrained areas - **Medium (`m`)**: Default size, used for typical use cases - **Large (`l`)**: Used for prominent displays or primary content areas - **Extra-large (`xl`)**: Maximum visibility (if applicable) All sizes shown below for comparison. <Canvas of={Stories.Sizes} /> ``` ```typescript export const Sizes: Story = { render: (args) => html` ${template({ ...args, size: 's', label: 'Small' })} ${template({ ...args, size: 'm', label: 'Medium' })} ${template({ ...args, size: 'l', label: 'Large' })} `, tags: ['options'], parameters: { flexLayout: 'row-wrap', }, }; ``` **Pattern for semantic variants**: ```mdx ### Semantic variants Semantic variants provide meaning through color: - **`accent`**: New, beta, prototype, draft - **`informative`**: Active, in use, live, published - **`neutral`**: Archived, deleted, paused, draft, not started, ended - **`positive`**: Approved, complete, success, new, purchased, licensed - **`notice`**: Needs approval, pending, scheduled - **`negative`**: Error, alert, rejected, failed All semantic variants shown below for comparison. <Canvas of={Stories.SemanticVariants} /> ``` ```typescript export const SemanticVariants: Story = { render: (args) => html` ${template({ ...args, variant: 'positive', label: 'Positive' })} ${template({ ...args, variant: 'informative', label: 'Informative' })} ${template({ ...args, variant: 'negative', label: 'Negative' })} ${template({ ...args, variant: 'neutral', label: 'Neutral' })} ${template({ ...args, variant: 'notice', label: 'Notice' })} `, tags: ['options'], parameters: { flexLayout: 'row-wrap', }, }; SemanticVariants.storyName = 'Semantic variants'; ``` **Pattern for static color**: sourced from Spectrum's static color usage guidance (e.g. [Button](https://spectrum.adobe.com/page/button/#Static-color)). The component's color pins to the chosen value regardless of the active theme, and the choice of value depends on the background it sits over. ```mdx Use `static-color` when the component-name needs to sit on top of a photo or colored background. It pins the component-name's color to the chosen value regardless of the active color theme: - **`white`**: use on dark color or image backgrounds - **`black`**: use on light color or image backgrounds ``` ```typescript export const StaticColors: Story = { render: (args) => html` ${['white', 'black'].map( (color) => html` ${template({ ...args, 'static-color': color })} ` )} `, args: { label: 'Loading', }, tags: ['options', '!test'], parameters: { flexLayout: false, staticColorsDemo: true, }, }; ``` ### States **Purpose**: Document all possible states the component can be in. **Order**: Default → Selected → Active → Disabled → Readonly → Error → Loading/Pending/Indeterminate → Other states **Consolidation rule**: Combine all states into a **single States story** when possible (or minimal stories when states are complex). **Pattern** (single States story; section heading and story name match — collapse to one heading, no `### States`; see "Single-story sections"): ```mdx ## States Components can exist in various states: - **Default**: Normal, interactive state - **Selected**: Item has been chosen or activated - **Disabled**: Functionality exists but is not available - **Error**: Validation failure or error condition All states shown below for comparison. <Canvas of={Stories.States} /> ``` ```typescript export const States: Story = { render: (args) => html` ${template({ ...args, label: 'Default' })} ${template({ ...args, selected: true, label: 'Selected' })} ${template({ ...args, disabled: true, label: 'Disabled' })} ${template({ ...args, invalid: true, label: 'Error' })} `, tags: ['states'], parameters: { flexLayout: 'row-wrap', }, }; ``` **Pattern for complex states** (when animation or interaction is critical — states no longer combine into one story, so each gets its own per-story `### Title`): ```mdx ### Indeterminate The indeterminate state shows an animated loading indicator when progress is unknown or cannot be determined. The animation automatically loops until the state changes. <Canvas of={Stories.Indeterminate} /> ``` ```typescript export const Indeterminate: Story = { tags: ['states'], args: { indeterminate: true, label: 'Loading...', }, }; ``` **Disabled state note** (fold into the `## States` prose above, or use verbatim if Disabled needs its own `### Disabled` story): ```mdx A component in a disabled state shows that [functionality] exists, but is not available in that circumstance. This can be used to maintain layout continuity and communicate that [functionality] may become available later. **ARIA support**: When disabled, the component automatically sets `aria-disabled="true"`.
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub