---
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 `docs/src/components/<component>/`. The canonical reference for structure and style is the **tag** component (`docs/src/components/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         |
| `<component>.stories.ts`    | Stencil story exports — both `🧩` (web component) and `🌍` (HTML/CSS) pairs |
| `<component>.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

- **No categories** — Storybook title is always `Components/<ComponentName>/...` with no intermediate category path.
- **Color is always `purple`** for all components.
- **Canvas component choice is type-dependent:**
  - **Web Components only (WC only)**: Use `<CanvasWithCodePen of={Stories.Basic} sourceState="shown" />` — shows CodePen embed without HTML tab
  - **Hybrid or CSS-only**: Use `<CanvasTabs htmlOf={Stories.BasicHtml} of={Stories.Basic} />` — 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/<ComponentName>/Variants'
```

### MDX Meta titles

| File                  | Meta title pattern                      |
| --------------------- | --------------------------------------- |
| `1-Overview.mdx`      | `"Components/<Name>/<Name>"`            |
| `2-Usage.mdx`         | `"Components/<Name>/Usage"`             |
| `3-Variants.mdx`      | `"Components/<Name>/Variants/Overview"` |
| `4-Styling.mdx`       | `"Components/<Name>/Styling"`           |
| `5-Accessibility.mdx` | `"Components/<Name>/Accessibility"`     |
| `6-Testing.mdx`       | `"Components/<Name>/Testing"`           |

> **Why `<Name>/<Name>` 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/<Name>` in the sidebar.

### doc-config storyIds

```ts
tabs: [
  { label: 'Overview', storyId: 'components-<component>--<component>' },
  { label: 'Usage', storyId: 'components-<component>--usage' },
  { label: 'Variants', storyId: 'components-<component>--variants-overview' },
  { label: 'Styling', storyId: 'components-<component>--styling' },
  { label: 'Accessibility', storyId: 'components-<component>--accessibility' },
  { label: 'Testing', storyId: 'components-<component>--testing' },
]
```

---

## Process

### Step 1 — Gather context

Read these files to understand the component:

- `packages/core/src/components/<component>/<component>.tsx` — props, events, parts, render output
- `packages/core/src/components/<component>/test/<component>.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 (`<ds-*>` tags).
- **Hybrid**: Component supports both web component (`<ds-*>`) and HTML/CSS class-based variants (e.g., `<div class="ds-*">`).
- **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.

---

## `<component>.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 '@baloise/ds-core'
import type { Meta } from '@storybook/html-vite'
import { createCssMappings, cssClasses, props, StoryFactory, withComponentControls, withRender } from '../../utils'

type Args = JSX.Ds<Component> & { slot: string }

const tag = 'ds-<component>'
// Only include css/cssClasses if the component has an HTML/CSS equivalent
const css = createCssMappings(tag)

const meta: Meta<Args> = {
  title: 'Components/<ComponentName>/Variants',
  args: {
    slot: 'Default content',
  },
  argTypes: {
    ...withComponentControls({ tag: 'ds-<component>' }),
  },
  ...withRender(({ slot, ...args }) => `<ds-<component> ${props(args)}>${slot}</ds-<component>>`),
}

export default meta

const Story = StoryFactory<Args>(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: `'🧩 <Name>'` for web component, `'🌍 <Name>'` 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 }) => `
      <ds-<component> variant="${variant}" ${props(args)}>
        Custom slot content here
      </ds-<component>
    `,
  ),
})
```

---

## `<component>.doc-config.ts`

```ts
/**
 * Shared configuration for <Component> component documentation pages.
 */

export const <COMPONENT>_DOC_CONFIG = {
  section: 'Components / <ComponentName>',
  color: 'purple' as const,
  tabs: [
    { label: 'Overview',      storyId: 'components-<component>--<component>' },
    { label: 'Usage',         storyId: 'components-<component>--usage' },
    { label: 'Variants',      storyId: 'components-<component>--variants-overview' },
    { label: 'Styling',       storyId: 'components-<component>--styling' },
    { label: 'Accessibility', storyId: 'components-<component>--accessibility' },
    { label: 'Testing',       storyId: 'components-<component>--testing' },
  ],
}

export const <COMPONENT>_TAB_TITLES = {
  overview:      'Overview',
  usage:         'Usage',
  variants:      'Variants',
  styling:       'Styling',
  accessibility: 'Accessibility',
  testing:       'Testing',
}

export const get<Component>Tabs = (activeLabel: keyof typeof <COMPONENT>_TAB_TITLES) => {
  return <COMPONENT>_DOC_CONFIG.tabs.map(tab => ({
    ...tab,
    active: tab.label === <COMPONENT>_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 <Component>Stories from './<component>.stories'
import { <COMPONENT>_DOC_CONFIG, get<Component>Tabs } from './<component>.doc-config'

<Meta title="Components/<ComponentName>/<ComponentName>" />

<Banner label={'<ComponentName>'} section={<COMPONENT>_DOC_CONFIG.section} color={<COMPONENT>_DOC_CONFIG.color} />

<BannerTabs of={<Component>Stories} tabs={get<Component>Tabs('overview')} />

<ComponentLead component="<component>" />

<CanvasWithCodePen of={<Component>Stories.Basic} sourceState="shown" />

<Controls of={<Component>Stories.Basic} />

<ComponentPublicMethods component="<component>" />

<Footer />
```

### For Hybrid or CSS-only:

```mdx
import { Controls, Meta } from '@storybook/addon-docs/blocks'
import {
  Banner,
  BannerTabs,
  CanvasTabs,
  ComponentLead,
  ComponentPublicMethods,
  Footer,
} from '../../../.storybook/blocks'
import * as <Component>Stories from './<component>.stories'
import { <COMPONENT>_DOC_CONFIG, get<Component>Tabs } from './<component>.doc-config'

<Meta title="Components/<ComponentName>/<ComponentName>" />

<Banner label={'<ComponentName>'} section={<COMPONENT>_DOC_CONFIG.section} color={<COMPONENT>_DOC_CONFIG.color} />

<BannerTabs of={<Component>Stories} tabs={get<Component>Tabs('overview')} />

<ComponentLead component="<component>" />

<CanvasTabs htmlOf={<Component>Stories.BasicHtml} of={<Component>Stories.Basic} />

<Controls of={<Component>Stories.Basic} />

<ComponentPublicMethods component="<component>" />

<Footer />
```

---

## `2-Usage.mdx`

```mdx
import { Meta } from '@storybook/addon-docs/blocks'
import {
  Banner,
  BannerTabs,
  Code,
  Footer,
  UsageExamples,
} from '../../../.storybook/blocks'
import * as <Component>Stories from './<component>.stories'
import { <COMPONENT>_DOC_CONFIG, get<Component>Tabs } from './<component>.doc-config'

<Meta title="Components/<ComponentName>/Usage" />
<Banner label={'Usage'} section={<COMPONENT>_DOC_CONFIG.section} color={<COMPONENT>_DOC_CONFIG.color} />
<BannerTabs of={<Component>Stories} tabs={get<Component>Tabs('usage')} />

## When to Use

- [bullet list of valid use cases]

## When NOT to Use

- [bullet list of anti-patterns]

## Do's and Don'ts

### [Guideline Title]

<UsageExamples
  items={[
    {
      type: 'correct',
      title: 'Correct: [Short Label]',
      content: (
        <ds-<component>>Correct example</ds-<component>>
      ),
      description: '[Why this is correct]',
    },
    {
      type: 'incorrect',
      title: 'Incorrect: [Short Label]',
      content: (
        <ds-<component>>Incorrect example</ds-<component>>
      ),
      description: '[Why this is wrong]',
    },
  ]}
/>

<Footer />
```

---

## `3-Variants.mdx`

### For Web Components only:

```mdx
import { Meta } from '@storybook/addon-docs/blocks'
import {
  Banner,
  BannerTabs,
  CanvasWithCodePen,
  Footer,
  StoryHeading,
} from '../../../.storybook/blocks'
import * as <Component>Stories from './<component>.stories'
import { <COMPONENT>_DOC_CONFIG, get<Component>Tabs } from './<component>.doc-config'

<Meta title="Components/<ComponentName>/Variants/Overview" />

<StoryHeading of={<Component>Stories.Basic} hidden></StoryHeading>

<Banner label={'Variants'} section={<COMPONENT>_DOC_CONFIG.section} color={<COMPONENT>_DOC_CONFIG.color} />

<BannerTabs of={<Component>Stories} tabs={get<Component>Tabs('variants')} />

{/* STORIES */}
{/* ------------------------------------------------------ */}

<StoryHeading of={<Component>Stories.<StoryName>}></StoryHeading>

[One or two sentence description of this variant.]

<CanvasWithCodePen of={<Component>Stories.<StoryName>} sourceState="shown" />

{/* ------------------------------------------------------ */}

<StoryHeading of={<Component>Stories.<NextStoryName>}></StoryHeading>

[Description.]

<CanvasWithCodePen of={<Component>Stories.<NextStoryName>} sourceState="shown" />

<Footer />
```

### For Hybrid or CSS-only:

```mdx
import { Meta } from '@storybook/addon-docs/blocks'
import {
  Banner,
  BannerTabs,
  CanvasTabs,
  Footer,
  StoryHeading,
} from '../../../.storybook/blocks'
import * as <Component>Stories from './<component>.stories'
import { <COMPONENT>_DOC_CONFIG, get<Component>Tabs } from './<component>.doc-config'

<Meta title="Components/<ComponentName>/Variants/Overview" />

<StoryHeading of={<Component>Stories.Basic} hidden></StoryHeading>

<Banner label={'Variants'} section={<COMPONENT>_DOC_CONFIG.section} color={<COMPONENT>_DOC_CONFIG.color} />

<BannerTabs of={<Component>Stories} tabs={get<Component>Tabs('variants')} />

{/* STORIES */}
{/* ------------------------------------------------------ */}

<StoryHeading of={<Component>Stories.<StoryName>}></StoryHeading>

[One or two sentence description of this variant.]

<CanvasTabs htmlOf={<Component>Stories.<StoryName>Html} of={<Component>Stories.<StoryName>} />

{/* ------------------------------------------------------ */}

<StoryHeading of={<Component>Stories.<NextStoryName>}></StoryHeading>

[Description.]

<CanvasTabs htmlOf={<Component>Stories.<NextStoryName>Html} of={<Component>Stories.<NextStoryName>} />

<Footer />
```

**Notes:**

- The first `<StoryHeading hidden>` above the Banner is required by Storybook routing — it registers the Basic story as the "variants" landing page.
- For hybrid/CSS-only components: each variant block pairs `<StoryName>` with `<StoryName>Html`.
- For WC-only components: use `CanvasWithCodePen` without HTML tabs.

---

## `4-Styling.mdx`

```mdx
import { Meta } from '@storybook/addon-docs/blocks'
import {
  Banner,
  BannerTabs,
  ComponentCssVariables,
  ComponentDesignTokens,
  ComponentParts,
  Footer,
} from '../../../.storybook/blocks'
import * as <Component>Stories from './<component>.stories'
import { <COMPONENT>_DOC_CONFIG, get<Component>Tabs } from './<component>.doc-config'

<Meta title="Components/<ComponentName>/Styling" />

<Banner label={'Styling'} section={<COMPONENT>_DOC_CONFIG.section} color={<COMPONENT>_DOC_CONFIG.color} />

<BannerTabs of={<Component>Stories} tabs={get<Component>Tabs('styling')} />

<ComponentParts component="<component>" />

<ComponentCssVariables component="<component>" />

<ComponentDesignTokens component="<component>" />

<Footer />
```

**Notes:**

- Use `ComponentCssVariables` and `ComponentDesignTokens` — NOT the old `TokenOverview`.
- `ComponentParts` renders Shadow DOM parts from JSDoc `@part` tags automatically.

---

## `5-Accessibility.mdx`

```mdx
import { Canvas, Markdown, Meta } from '@storybook/addon-docs/blocks'
import {
  A11yGuidelines,
  Banner,
  BannerTabs,
  CanvasTabs,
  Footer,
  InfoQuote,
  Lead,
  StoryHeading,
  TokenOverview,
} from '../../../.storybook/blocks'
import * as <Component>Stories from './<component>.stories'
import { <COMPONENT>_DOC_CONFIG, get<Component>Tabs } from './<component>.doc-config'

<Meta title="Components/<ComponentName>/Accessibility" />

<Banner label={'Accessibility'} section={<COMPONENT>_DOC_CONFIG.section} color={<COMPONENT>_DOC_CONFIG.color} />

<BannerTabs of={<Component>Stories} tabs={get<Component>Tabs('accessibility')} />

## Guidelines

<A11yGuidelines
  items={[
    {
      type: 'do',
      title: '[Guideline Title — actionable requirement]',
      content: (
        <p style={{ margin: '0.5rem 0 0 0' }}>
          [Clear instruction for component consumers. Reference HTML attributes like <code>aria-label</code>,{' '}
          <code>aria-describedby</code>, or semantic elements where relevant.]
        </p>
      ),
    },
    {
      type: 'do',
      title: '[Second do example]',
      content: <p style={{ margin: '0.5rem 0 0 0' }}>[Another important practice for this component.]</p>,
    },
    {
      type: 'dont',
      title: '[Anti-pattern Title — what to avoid]',
      content: (
        <p style={{ margin: '0.5rem 0 0 0' }}>
          [What not to do and why it fails accessibility. Explain the consequence for users.]
        </p>
      ),
    },
    {
      type: 'dont',
      title: '[Second dont example]',
      content: <p style={{ margin: '0.5rem 0 0 0' }}>[Another common mistake that breaks accessibility.]</p>,
    },
  ]}
/>

## References

- [MDN Reference 1](https://example.com) - [Brief description of what users will learn]
- [MDN Reference 2](https://example.com) - [How it relates to this component]
- [WCAG 2.2 Guideline](https://www.w3.org/WAI/WCAG22/) - [Which success criterion applies]

<Footer />
```

**Accessibility content principle:**

- Document **consumer requirements only** — not internal implementation.
- Include **4–6 do/don't guidelines** (2–3 dos, 2–3 don'ts) that are **component-specific and actionable**.
- Add **MDN and WCAG references** at the end to direct users to authoritative resources.
- Guidelines should match the component's actual usage patterns. For example:
  - **Form fields** (Select, Input, Checkbox): Focus on labels, `aria-describedby` for error messages, required indication.
  - **Interactive buttons/links**: Focus on text labels, focus visible, keyboard activation.
  - **Content containers** (Badge, Card): Focus on semantic structure, color contrast, descriptive context.
  - **Navigation**: Focus on landmarks, skip links, ARIA roles.
- Avoid generic guidelines; tailor each item to the component's role and user interactions.

---

## Reusable Block Components

| Block                    | Import                       | Props                                            | Purpose                                                     |
| ------------------------ | ---------------------------- | ------------------------------------------------ | ----------------------------------------------------------- |
| `Banner`                 | `../../../.storybook/blocks` | `label`, `section`, `color`                      | Page header with title and accent color                     |
| `BannerTabs`             | `../../../.storybook/blocks` | `of`, `tabs`                                     | Navigation between Overview/Usage/etc                       |
| `CanvasTabs`             | `../../../.storybook/blocks` | `of`, `htmlOf?`                                  | Canvas with web component + HTML/CSS tabs (hybrid/CSS-only) |
| `CanvasWithCodePen`      | `../../../.storybook/blocks` | `of`, `sourceState?`                             | Canvas with CodePen embed for web components (WC only)      |
| `ComponentLead`          | `../../../.storybook/blocks` | `component`                                      | Auto-pulls description from components.json                 |
| `ComponentPublicMethods` | `../../../.storybook/blocks` | `component`, `subComponents?`, `title?`          | Auto-pulls methods from components.json                     |
| `ComponentParts`         | `../../../.storybook/blocks` | `component`                                      | Auto-pulls Shadow DOM parts from components.json            |
| `ComponentCssVariables`  | `../../../.storybook/blocks` | `component`                                      | CSS custom properties table                                 |
| `ComponentDesignTokens`  | `../../../.storybook/blocks` | `component`                                      | Design tokens table                                         |
| `A11yGuidelines`         | `../../../.storybook/blocks` | `items: { type, title, content }[]`              | Do/Don't accessibility checklist                            |
| `UsageExamples`          | `../../../.storybook/blocks` | `items: { type, title, content, description }[]` | Correct/Incorrect code pattern comparison                   |
| `StoryHeading`           | `../../../.storybook/blocks` | `of`, `hidden?`                                  | Section title before canvas component                       |
| `Footer`                 | `../../../.storybook/blocks` | none                                             | End-of-page footer                                          |
| `ComponentPageObject`    | `../../../.storybook/blocks` | `component`                                      | PO API table + example test + install guide                 |
| `Code`                   | `../../../.storybook/blocks` | `code`, `noPreview?`                             | Inline code snippet in UsageExamples                        |

---

## Shared Patterns

### Meta title must be a string literal

Storybook's static indexer cannot evaluate expressions:

```mdx
{/* ✓ correct */}

<Meta title="Components/Tag/Usage" />

{/* ✗ wrong — causes indexing error */}

<Meta title={`${TAG_DOC_CONFIG.section}/Usage`} />
```

### Import only what you use

Each MDX page imports only the blocks it actually renders.

### Section dividers in `3-Variants.mdx`

Use `{/* ------------------------------------------------------ */}` before every `StoryHeading`.

### Key story utilities

- `createCssMappings(tag)` — maps component props to CSS class names for HTML variants
- `cssClasses(mappings, args, baseClass)` — applies mapped CSS classes to an element
- `props(args)` — serialises Stencil props to HTML attribute string
- `withRender(fn)` — render function override
- `withComponentControls({ tag })` — argTypes from component JSDoc
- `StoryFactory<Args>(meta)` — returns the `Story()` helper

---

## `6-Testing.mdx`

```mdx
import { Meta } from '@storybook/addon-docs/blocks'
import {
  Banner,
  BannerTabs,
  ComponentPageObject,
  Footer,
} from '../../../.storybook/blocks'
import * as <Component>Stories from './<component>.stories'
import { <COMPONENT>_DOC_CONFIG, get<Component>Tabs } from './<component>.doc-config'

<Meta title="Components/<ComponentName>/Testing" />

<Banner label={'Testing'} section={<COMPONENT>_DOC_CONFIG.section} color={<COMPONENT>_DOC_CONFIG.color} />

<BannerTabs of={<Component>Stories} tabs={get<Component>Tabs('testing')} />

<ComponentPageObject component="<component>" />

<Footer />
```

**Notes:**

- **Keep this page minimal** — no extra content beyond the components above. Do not add additional sections, guidelines, or explanatory text.
- `ComponentPageObject` reads `pageObject` from `components.json` (populated during `npm run build`).
- If the component has no `.po.ts` in `packages/playwright`, the block shows "No page object available" + the install guide.
- If a page object exists, it renders Locators / Actions / Assertions tables, a semi-dynamic example test, and the install guide.
- The structure is always: Meta → Banner → BannerTabs → ComponentPageObject → Footer. Nothing more.

---

## Reference: Tag Component

The tag is the canonical example for the current pattern:

```
docs/src/components/tag/
  tag.stories.ts       ← title: 'Components/Tag/Variants', paired 🧩/🌍 stories
  tag.doc-config.ts    ← TAG_DOC_CONFIG (color: 'purple'), getTagTabs()
  1-Overview.mdx       ← CanvasTabs + Controls + ComponentLead + ComponentPublicMethods
  2-Usage.mdx          ← UsageExamples block
  3-Variants.mdx       ← Meta title ends in /Variants/Overview, StoryHeading + CanvasTabs pairs
  4-Styling.mdx        ← ComponentParts + ComponentCssVariables + ComponentDesignTokens
  5-Accessibility.mdx  ← A11yGuidelines + WCAG compliance list
  api.md               ← AUTO-GENERATED — never edit
```
