--- name: ds-document-component description: Use when writing or generating Storybook documentation for a Baloise Design System component — creates stories.ts, doc-config.ts, and six MDX subpages (Overview, Usage, Variants, Styling, Accessibility, Testing) using reusable Storybook blocks (ComponentLead, ComponentPublicMethods, ComponentParts, CanvasTabs, ComponentPageObject) for dynamic data binding to components.json --- # Write Component Docs Generates a complete documentation set for a component in `apps/storybook/src/components///` (mirroring the component's category folder in `packages/core/src/components/`; see `apps/storybook/CONTEXT.md` for the taxonomy). The canonical reference for structure and style is the **tag** component (`apps/storybook/src/components/indicators/tag/`). Each component gets exactly **six MDX files** plus two TypeScript support files: | File | Purpose | | --------------------------- | --------------------------------------------------------------------------- | | `1-Overview.mdx` | Canvas (type-dependent) + Controls + ComponentLead + ComponentPublicMethods | | `2-Usage.mdx` | When to use, do's/don'ts, `UsageExamples` | | `3-Variants.mdx` | All story variants with Canvas (type-dependent) | | `4-Styling.mdx` | ComponentParts + ComponentCssVariables + ComponentDesignTokens | | `5-Accessibility.mdx` | WCAG guidelines via `A11yGuidelines` | | `6-Testing.mdx` | `ComponentPageObject` — PO API table + example test + install guide | | `.stories.ts` | Stencil story exports — both `🧩` (web component) and `🌍` (HTML/CSS) pairs | | `.doc-config.ts` | Shared section/color/tabs config | Do NOT create `api.md` (auto-generated by Stencil), `.parts.svg`, or any other files. --- ## Key Rules - **Category-prefixed title** — Storybook title is always `Components///...`, where `` matches the component's folder in `packages/core/src/components//` (see `apps/storybook/CONTEXT.md`). - **Color is always `purple`** for all components. - **Canvas component choice is type-dependent:** - **Web Components only (WC only)**: Use `` — shows CodePen embed without HTML tab - **Hybrid or CSS-only**: Use `` — shows both web component and HTML/CSS tabs - **Stories have dual variants**: every story comes in a `🧩 Name` (web component, `ds-*` tags) and a `🌍 Name` (HTML/CSS classes only) pair. - **`4-Styling.mdx`** uses `ComponentCssVariables` and `ComponentDesignTokens` — NOT `TokenOverview`. --- ## Title and storyId Conventions ### stories.ts ```ts title: 'Components///Variants' ``` ### MDX Meta titles | File | Meta title pattern | | --------------------- | -------------------------------------------------- | | `1-Overview.mdx` | `"Components///"` | | `2-Usage.mdx` | `"Components///Usage"` | | `3-Variants.mdx` | `"Components///Variants/Overview"` | | `4-Styling.mdx` | `"Components///Styling"` | | `5-Accessibility.mdx` | `"Components///Accessibility"` | | `6-Testing.mdx` | `"Components///Testing"` | > **Why `/` for Overview?** Storybook uses the last path segment as the sidebar/search label. Using the component name as the last segment makes the search show "Button" (not "Documentation") as the primary result, while keeping the page correctly nested under `Components//` in the sidebar. > > **Why a static string, not a computed title?** Storybook's CSF/MDX indexer requires `title` to > be a static string literal — a helper function call (e.g. deriving the category from > `import.meta.url`) fails to index with `CSF: unexpected dynamic title`. Write the category > segment out literally, matching the folder the file lives in. ### doc-config storyIds ```ts tabs: [ { label: 'Overview', storyId: 'components----' }, { label: 'Usage', storyId: 'components----usage' }, { label: 'Variants', storyId: 'components----variants-overview' }, { label: 'Styling', storyId: 'components----styling' }, { label: 'Accessibility', storyId: 'components----accessibility' }, { label: 'Testing', storyId: 'components----testing' }, ] ``` Also add an entry per MDX page to `apps/storybook/.storybook/story-paths.json` (used by the "Edit on GitHub" footer link), keyed by the same `storyId` and pointing at `components///.mdx`. --- ## Process ### Step 1 — Gather context Read these files to understand the component: - `packages/core/src/components///.tsx` — props, events, parts, render output - `packages/core/src/components///test/.visual.html` — story sections ### Step 1a — Determine component type Check the component type by examining the TSX file and visual.html: - **Web Components only (WC only)**: Component has no HTML/CSS class-based equivalent. The visual.html only shows web component usage (`` tags). - **Hybrid**: Component supports both web component (``) and HTML/CSS class-based variants (e.g., `
`). - **CSS-only**: Component has no web component implementation, only HTML/CSS classes. Ask the user if unclear: "Is this component WC only, hybrid, or CSS-only?" ### Step 2 — Present story sections Show ALL `data-testid` sections from `visual.html` as a numbered list: ``` Which sections should become story variants? 1. basic 2. with-icon 3. colors 4. sizes ... (enter numbers separated by commas, or "all") ``` Wait for user selection before generating anything. --- ## `.stories.ts` Structure stories to expose component props as controls. All props go into the `args` object and are rendered via `${props(args)}`: ```ts import type { JSX } from '@helvetia-design/core' import type { Meta } from '@storybook/html-vite' import { createCssMappings, cssClasses, props, StoryFactory, withComponentControls, withRender } from '../../../utils' type Args = JSX.Ds & { slot: string } const tag = 'ds-' // Only include css/cssClasses if the component has an HTML/CSS equivalent const css = createCssMappings(tag) const meta: Meta = { title: 'Components///Variants', args: { slot: 'Default content', }, argTypes: { ...withComponentControls({ tag: 'ds-' }), }, ...withRender(({ slot, ...args }) => ` ${props(args)}>${slot}>`), } export default meta const Story = StoryFactory(meta) export const Basic = Story({}) Basic.storyName = '🧩 Basic' export const BasicHtml = Story({}) BasicHtml.storyName = '🌍 Basic' export const WithVariant = Story({ args: { variant: 'success', }, }) WithVariant.storyName = '🧩 With Variant' export const WithVariantHtml = Story({ args: { variant: 'success', }, }) WithVariantHtml.storyName = '🌍 With Variant' // Additional stories follow the same pattern... ``` **Key patterns:** - **Meta includes default `withRender`** — covers both WC and HTML/CSS default rendering via `${props(args)}` - **Each story has an `args` object** — maps to component `@Prop()` values (e.g., `{ border: true, horizontal: true }`) - **Use `${props(args)}` in template** — serializes args to HTML attributes on the component tag - **No hardcoded attributes** — all variant differences go into `args`, not hardcoded in template strings - Story names: `'🧩 '` for web component, `'🌍 '` for HTML **For slots or complex content:** If a variant needs different slot content, override `withRender`: ```ts export const WithContent = Story({ args: { variant: 'primary', }, ...withRender( ({ variant, ...args }) => ` variant="${variant}" ${props(args)}> Custom slot content here `, ), }) ``` --- ## `.doc-config.ts` ```ts /** * Shared configuration for component documentation pages. */ export const _DOC_CONFIG = { section: 'Components / ', color: 'purple' as const, tabs: [ { label: 'Overview', storyId: 'components---' }, { label: 'Usage', storyId: 'components---usage' }, { label: 'Variants', storyId: 'components---variants-overview' }, { label: 'Styling', storyId: 'components---styling' }, { label: 'Accessibility', storyId: 'components---accessibility' }, { label: 'Testing', storyId: 'components---testing' }, ], } export const _TAB_TITLES = { overview: 'Overview', usage: 'Usage', variants: 'Variants', styling: 'Styling', accessibility: 'Accessibility', testing: 'Testing', } export const getTabs = (activeLabel: keyof typeof _TAB_TITLES) => { return _DOC_CONFIG.tabs.map(tab => ({ ...tab, active: tab.label === _TAB_TITLES[activeLabel], })) } ``` --- ## `1-Overview.mdx` ### For Web Components only: ```mdx import { Controls, Meta } from '@storybook/addon-docs/blocks' import { Banner, BannerTabs, CanvasWithCodePen, ComponentLead, ComponentPublicMethods, Footer, } from '../../../../.storybook/blocks' import * as Stories from './.stories' import { _DOC_CONFIG, getTabs } from './.doc-config' '} section={_DOC_CONFIG.section} color={_DOC_CONFIG.color} /> Stories} tabs={getTabs('overview')} /> Stories.Basic} sourceState="shown" /> Stories.Basic} />