From 4d6bee6dbda0ca6b349eb4b3da67a8802ad8b21e Mon Sep 17 00:00:00 2001 From: Frankie Yan Date: Sat, 4 Jul 2026 13:33:44 -0700 Subject: [PATCH 01/10] chore(plop): add Variants and Accessibility stories to the component scaffold Co-Authored-By: Claude --- .../component/component.stories.tsx.hbs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.plop/templates/component/component.stories.tsx.hbs b/.plop/templates/component/component.stories.tsx.hbs index fd7b7bb5..374141ff 100644 --- a/.plop/templates/component/component.stories.tsx.hbs +++ b/.plop/templates/component/component.stories.tsx.hbs @@ -56,3 +56,24 @@ export const Playground: Story = { ) }, } + +export const Variants: Story = { + render() { + return ( +
+ <{{pascalCase name}} variant="primary">Primary{' '} + <{{pascalCase name}} variant="secondary">Secondary +
+ ) + }, +} + +export const Accessibility: Story = { + render() { + return ( +
+ <{{pascalCase name}} variant="primary">Accessible content +
+ ) + }, +} From 2a700b3fae2a7ff99f97f6674e54b5df106b76d0 Mon Sep 17 00:00:00 2001 From: Frankie Yan Date: Sat, 4 Jul 2026 13:33:46 -0700 Subject: [PATCH 02/10] chore(plop): generate an MDX docs page when scaffolding a component Co-Authored-By: Claude --- .plop/templates/component/component.mdx.hbs | 74 +++++++++++++++++++++ plopfile.mjs | 6 ++ 2 files changed, 80 insertions(+) create mode 100644 .plop/templates/component/component.mdx.hbs diff --git a/.plop/templates/component/component.mdx.hbs b/.plop/templates/component/component.mdx.hbs new file mode 100644 index 00000000..4d79153a --- /dev/null +++ b/.plop/templates/component/component.mdx.hbs @@ -0,0 +1,74 @@ +import { Canvas, Controls, Meta, Subtitle, Title } from '@storybook/addon-docs/blocks' + +import * as {{pascalCase name}}Stories from './{{dashCase name}}.stories' + + + + + +<Subtitle>One-line summary of what {{pascalCase name}} is for.</Subtitle> + +## Overview + +What {{pascalCase name}} is and the problem it solves, in a sentence or two. For design +guidance (anatomy, when to use, do and don't), see the +[{{pascalCase name}} handbook page](https://github.com/Doist/component-libraries/blob/main/handbook/components/{{dashCase name}}.md). + +{/* Verify this handbook link: the slug isn't always the component's dash-name (e.g. + buttons.md, menus.md, switches.md) and some pages don't exist yet. */} + +## When to use + +{/* Two or more concrete scenarios. Trim to what the handbook doesn't already cover. */} + +- Use {{pascalCase name}} when ... +- Prefer another component when ... + +## Basic usage + +The simplest real example. Pass `variant` and `children`. + +<Canvas of={ {{pascalCase name}}Stories.Playground } /> + +## Variants + +{{pascalCase name}} supports a `primary` and a `secondary` variant. + +<Canvas of={ {{pascalCase name}}Stories.Variants } /> + +## States + +{/* Visual states (idle, hover, disabled, loading) live here. Add a `States` story and + reference it with <Canvas of={ {{pascalCase name}}Stories.States } />, or delete this + section if the component has no distinct states. */} + +## API + +<Controls of={ {{pascalCase name}}Stories.Playground } /> + +{/* Composable components: add `ArgTypes` to the import above and document each exported + part with its own block, e.g. + +### `<{{pascalCase name}}.Item>` + +<ArgTypes of={ {{pascalCase name}}Item } /> + +Repeat per exported sub-component. */} + +## Custom properties + +The following CSS custom properties customize the {{pascalCase name}} appearance. The values +shown are the defaults. + +```css +--reactist-{{dashCase name}}-tint +--reactist-{{dashCase name}}-fill +``` + +## Accessibility + +- **Keyboard**: describe focus order and keys the component handles. +- **Screen reader**: the name, role, and state it exposes (and via which prop). +- **Focus**: focus management / trapping behavior, if any. + +<Canvas of={ {{pascalCase name}}Stories.Accessibility } /> diff --git a/plopfile.mjs b/plopfile.mjs index 14bfb621..626a2ed9 100644 --- a/plopfile.mjs +++ b/plopfile.mjs @@ -56,6 +56,12 @@ function componentGenerator(plop) { templateFile: templateFile('component/component.stories.tsx'), }) + actions.push({ + type: 'add', + path: 'src/{{dashCase name}}/{{dashCase name}}.mdx', + templateFile: templateFile('component/component.mdx'), + }) + return actions }, }) From cb555f27255f5d8f04bfb58dbbb6cc5e81809c6c Mon Sep 17 00:00:00 2001 From: Frankie Yan <frankie@doist.com> Date: Sat, 4 Jul 2026 13:33:47 -0700 Subject: [PATCH 03/10] docs(plop): document the component MDX doc convention Co-Authored-By: Claude <noreply@anthropic.com> --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 26522078..dd6b1ae0 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ The generated source files include the component implementation with sample prop You also need to export your new component by adding a reference to it in the [top-level index file](src/index.ts). +The generator also creates a `<component-name>.mdx` documentation page next to the stories. Edit it to document the component: fill in the Overview and link the matching [design handbook page](https://github.com/Doist/component-libraries/tree/main/handbook/components), keep the sections that apply, and delete the ones that don't. For composable components (multiple exported parts), repeat the API block per part. Its live examples render from the generated `Playground`, `Variants`, and `Accessibility` stories. + ## Storybook For the first development mode run: From 46af306331c72935b56c4161fa33f540253af0a4 Mon Sep 17 00:00:00 2001 From: Frankie Yan <frankie@doist.com> Date: Sat, 4 Jul 2026 15:36:09 -0700 Subject: [PATCH 04/10] chore(plop): make the component scaffold pass lint and type-check clean Co-Authored-By: Claude <noreply@anthropic.com> --- .plop/templates/component/component.stories.tsx.hbs | 5 +++-- .plop/templates/component/component.test.tsx.hbs | 2 ++ .plop/templates/component/component.tsx.hbs | 8 +++----- .plop/templates/component/index.ts.hbs | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.plop/templates/component/component.stories.tsx.hbs b/.plop/templates/component/component.stories.tsx.hbs index 374141ff..f3941d21 100644 --- a/.plop/templates/component/component.stories.tsx.hbs +++ b/.plop/templates/component/component.stories.tsx.hbs @@ -1,8 +1,9 @@ import * as React from 'react' -import type { Meta, StoryObj } from '@storybook/react' import { {{pascalCase name}} } from './{{dashCase name}}' +import type { Meta, StoryObj } from '@storybook/react' + const meta = { title: 'Design system/{{pascalCase name}}', component: {{pascalCase name}}, @@ -48,7 +49,7 @@ export const Playground: Story = { control: { type: 'text' }, }, }, - render(args) { + render(args: React.ComponentProps<typeof {{pascalCase name}}>) { return ( <section> <{{pascalCase name}} {...args} /> diff --git a/.plop/templates/component/component.test.tsx.hbs b/.plop/templates/component/component.test.tsx.hbs index dbe0405a..e91960a1 100644 --- a/.plop/templates/component/component.test.tsx.hbs +++ b/.plop/templates/component/component.test.tsx.hbs @@ -1,5 +1,7 @@ import * as React from 'react' + import { render, screen } from '@testing-library/react' + import { {{pascalCase name}} } from './{{dashCase name}}' describe('{{pascalCase name}}', () => { diff --git a/.plop/templates/component/component.tsx.hbs b/.plop/templates/component/component.tsx.hbs index f1da8df3..a42def26 100644 --- a/.plop/templates/component/component.tsx.hbs +++ b/.plop/templates/component/component.tsx.hbs @@ -1,5 +1,7 @@ import * as React from 'react' + import { Box } from '../box' + import styles from './{{dashCase name}}.module.css' type {{pascalCase name}}Props = { @@ -14,11 +16,7 @@ type {{pascalCase name}}Props = { } function {{pascalCase name}}({ children, variant }: {{pascalCase name}}Props) { - return ( - <Box className={[styles.container, styles[variant]]}> - {children} - </Box> - ) + return <Box className={[styles.container, styles[variant]]}>{children}</Box> } export { {{pascalCase name}} } diff --git a/.plop/templates/component/index.ts.hbs b/.plop/templates/component/index.ts.hbs index 45e186b9..cd311cbf 100644 --- a/.plop/templates/component/index.ts.hbs +++ b/.plop/templates/component/index.ts.hbs @@ -1,2 +1,2 @@ -export { {{pascalCase name}} } from './{{dashCase name}}' export type { {{pascalCase name}}Props } from './{{dashCase name}}' +export { {{pascalCase name}} } from './{{dashCase name}}' From 2086ae5da901fc25b7065410c63fe1468f16b36c Mon Sep 17 00:00:00 2001 From: Frankie Yan <frankie@doist.com> Date: Fri, 17 Jul 2026 02:57:07 -0700 Subject: [PATCH 05/10] chore: stop running Prettier on MDX files Co-Authored-By: Claude <noreply@anthropic.com> --- .prettierignore | 2 ++ lint-staged.config.js | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.prettierignore b/.prettierignore index 61fa2401..189c5fca 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,3 +3,5 @@ dist/** coverage/** docs/** lib/** +# Prettier's MDX parser predates MDX 2/3 and corrupts {/* … */} comments (see PR #1091) +*.mdx diff --git a/lint-staged.config.js b/lint-staged.config.js index 06ae75b2..4594fa56 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -1,5 +1,5 @@ module.exports = { - './**/*.{js,jsx,ts,tsx,md,mdx,json,css,scss,less}': ['prettier --write'], + './**/*.{js,jsx,ts,tsx,md,json,css,scss,less}': ['prettier --write'], './**/*.{js,jsx,ts,tsx}': ['npx eslint --format codeframe --fix'], 'src/**/*.{js,jsx,ts,tsx}': ['npx @doist/react-compiler-tracker --stage-record-file'], } diff --git a/package.json b/package.json index e9c72c12..bd145dba 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "type-check:react18": "tsc --noEmit -p ./tsconfig.react18.json", "lint": "eslint --format codeframe --cache --ext js,jsx,ts,tsx ./", "storybook": "storybook dev -p 6006", - "prettify": "prettier --write \"./**/*.{js,jsx,ts,tsx,json,css,scss,less,md,mdx}\"", + "prettify": "prettier --write \"./**/*.{js,jsx,ts,tsx,json,css,scss,less,md}\"", "plop": "plop" }, "peerDependencies": { From 99be6cb642be16d23011804e684193a34f44a9e7 Mon Sep 17 00:00:00 2001 From: Frankie Yan <frankie@doist.com> Date: Fri, 17 Jul 2026 02:57:51 -0700 Subject: [PATCH 06/10] chore(plop): prompt for the Storybook category when scaffolding Co-Authored-By: Claude <noreply@anthropic.com> --- .../templates/component/component.stories.tsx.hbs | 2 +- plopfile.mjs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.plop/templates/component/component.stories.tsx.hbs b/.plop/templates/component/component.stories.tsx.hbs index f3941d21..c0e91362 100644 --- a/.plop/templates/component/component.stories.tsx.hbs +++ b/.plop/templates/component/component.stories.tsx.hbs @@ -5,7 +5,7 @@ import { {{pascalCase name}} } from './{{dashCase name}}' import type { Meta, StoryObj } from '@storybook/react' const meta = { - title: 'Design system/{{pascalCase name}}', + title: '{{category}}/{{pascalCase name}}', component: {{pascalCase name}}, parameters: { /** diff --git a/plopfile.mjs b/plopfile.mjs index 626a2ed9..933f806e 100644 --- a/plopfile.mjs +++ b/plopfile.mjs @@ -23,6 +23,21 @@ function componentGenerator(plop) { message: 'Component name (e.g. "dropdown select", "sortable-table", "PromotionBanner")', }, + { + type: 'list', + name: 'category', + message: 'Storybook category (the section the component belongs to)', + choices: [ + '📐 Layout', + '🔤 Typography', + '🔘 Buttons & links', + '📝 Form', + '📑 Menus & tabs', + '📊 Data display', + '💬 Feedback', + '🪟 Overlays', + ], + }, ], actions() { From 47df169f2b29a01d66e2d545c4f36e9d08c17533 Mon Sep 17 00:00:00 2001 From: Frankie Yan <frankie@doist.com> Date: Fri, 17 Jul 2026 02:59:09 -0700 Subject: [PATCH 07/10] chore(plop): scaffold a Default story and drop the premature accessible badge Co-Authored-By: Claude <noreply@anthropic.com> --- .../component/component.stories.tsx.hbs | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/.plop/templates/component/component.stories.tsx.hbs b/.plop/templates/component/component.stories.tsx.hbs index c0e91362..3e93cba9 100644 --- a/.plop/templates/component/component.stories.tsx.hbs +++ b/.plop/templates/component/component.stories.tsx.hbs @@ -2,7 +2,7 @@ import * as React from 'react' import { {{pascalCase name}} } from './{{dashCase name}}' -import type { Meta, StoryObj } from '@storybook/react' +import type { Meta, StoryObj } from '@storybook/react-vite' const meta = { title: '{{category}}/{{pascalCase name}}', @@ -13,6 +13,8 @@ const meta = { * Used to highlight the web accessibility standards the component meets or * whether the component is deprecated. * Possible values: `accessible`, `partiallyAccessible`, `notAccessible`, `deprecated` + * Start at `notAccessible`; raise it only once the component has been through an + * accessibility review, so a fresh component never claims a standard it hasn't earned. * @see .storybook/badges/README.md */ badges: ['notAccessible'], @@ -35,24 +37,11 @@ export default meta type Story = StoryObj<typeof meta> -export const Playground: Story = { - args: { - variant: 'primary', - children: 'Hello world', - }, - argTypes: { - variant: { - control: { type: 'inline-radio' }, - options: ['primary', 'secondary'], - }, - children: { - control: { type: 'text' }, - }, - }, - render(args: React.ComponentProps<typeof {{pascalCase name}}>) { +export const Default: Story = { + render() { return ( <section> - <{{pascalCase name}} {...args} /> + <{{pascalCase name}} variant="primary">Hello world</{{pascalCase name}}> </section> ) }, @@ -70,6 +59,8 @@ export const Variants: Story = { } export const Accessibility: Story = { + // Demonstrate what the docs page's Accessibility section describes (the + // accessible name, keyboard behavior, decorative usage), not just default content. render() { return ( <section> @@ -78,3 +69,26 @@ export const Accessibility: Story = { ) }, } + +export const Playground: Story = { + args: { + variant: 'primary', + children: 'Hello world', + }, + argTypes: { + variant: { + control: { type: 'inline-radio' }, + options: ['primary', 'secondary'], + }, + children: { + control: { type: 'text' }, + }, + }, + render(args: React.ComponentProps<typeof {{pascalCase name}}>) { + return ( + <section> + <{{pascalCase name}} {...args} /> + </section> + ) + }, +} From 7acd610305de59ff7f00e82f436799a0f8c3f056 Mon Sep 17 00:00:00 2001 From: Frankie Yan <frankie@doist.com> Date: Fri, 17 Jul 2026 03:29:29 -0700 Subject: [PATCH 08/10] chore(plop): align the scaffolded docs page with the house dialect Co-Authored-By: Claude <noreply@anthropic.com> --- .plop/templates/component/component.mdx.hbs | 79 +++++++++++++++------ 1 file changed, 59 insertions(+), 20 deletions(-) diff --git a/.plop/templates/component/component.mdx.hbs b/.plop/templates/component/component.mdx.hbs index 4d79153a..afea35cb 100644 --- a/.plop/templates/component/component.mdx.hbs +++ b/.plop/templates/component/component.mdx.hbs @@ -1,8 +1,14 @@ -import { Canvas, Controls, Meta, Subtitle, Title } from '@storybook/addon-docs/blocks' +{{!-- Template-authoring note, stripped from the generated file: JSX braces that touch +Handlebars braces form a triple-stache and break generation. Keep a space inside +`of={ … }` and let `~` trim it from the output, e.g. `of={ {{~pascalCase name~}} Stories}`. +Avoid inline `style={{ … }}` in this file for the same reason. +Markdown tables don't render in this repo's MDX (no remark-gfm); wrap them in a +`<Markdown>{` … `}</Markdown>` block and escape inner backticks as `\``, like avatar.mdx. --}} +import { Canvas, Controls, Markdown, Meta, Subtitle, Title } from '@storybook/addon-docs/blocks' import * as {{pascalCase name}}Stories from './{{dashCase name}}.stories' -<Meta of={ {{pascalCase name}}Stories } /> +<Meta of={ {{~pascalCase name~}} Stories} /> <Title /> @@ -17,6 +23,18 @@ guidance (anatomy, when to use, do and don't), see the {/* Verify this handbook link: the slug isn't always the component's dash-name (e.g. buttons.md, menus.md, switches.md) and some pages don't exist yet. */} +{/* Compound components (multiple exported parts): describe the parts in an Anatomy table. + +## Anatomy + +<Markdown>{` +| Part | Renders | Owns | +| --- | --- | --- | +| \`<{{pascalCase name}}>\` | the root element | context and coordination between parts | +| \`<{{pascalCase name}}.Item>\` | one item | its own selection state | +`}</Markdown> +*/} + ## When to use {/* Two or more concrete scenarios. Trim to what the handbook doesn't already cover. */} @@ -28,42 +46,63 @@ guidance (anatomy, when to use, do and don't), see the The simplest real example. Pass `variant` and `children`. -<Canvas of={ {{pascalCase name}}Stories.Playground } /> +<Canvas of={ {{~pascalCase name~}} Stories.Default} /> ## Variants {{pascalCase name}} supports a `primary` and a `secondary` variant. -<Canvas of={ {{pascalCase name}}Stories.Variants } /> +<Canvas of={ {{~pascalCase name~}} Stories.Variants} /> + +{/* Visual states (idle, hover, disabled, loading) get their own section. Add a States + story, then uncomment: ## States -{/* Visual states (idle, hover, disabled, loading) live here. Add a `States` story and - reference it with <Canvas of={ {{pascalCase name}}Stories.States } />, or delete this - section if the component has no distinct states. */} +<Canvas of={ {{~pascalCase name~}} Stories.States} /> +*/} + +## Props -## API +Try the props in the playground below; the table lists each prop with its type and default. -<Controls of={ {{pascalCase name}}Stories.Playground } /> +<Canvas of={ {{~pascalCase name~}} Stories.Playground} /> -{/* Composable components: add `ArgTypes` to the import above and document each exported - part with its own block, e.g. +<Controls of={ {{~pascalCase name~}} Stories.Playground} /> + +{/* Compound components: document each exported part with its own block. Import the parts + from the component module (e.g. `import { {{pascalCase name}}Item } from './{{dashCase name}}'`), + add `ArgTypes` and `Description` to the blocks import above, then repeat per part: ### `<{{pascalCase name}}.Item>` -<ArgTypes of={ {{pascalCase name}}Item } /> +<Description of={ {{~pascalCase name~}} Item} /> -Repeat per exported sub-component. */} +<ArgTypes of={ {{~pascalCase name~}} Item} /> +*/} ## Custom properties -The following CSS custom properties customize the {{pascalCase name}} appearance. The values -shown are the defaults. +These CSS custom properties control the {{pascalCase name}} colors. The variant classes set +them; the values below are what the `primary` variant sets. + +<Markdown>{` +| Property | Default (primary variant) | Purpose | +| --- | --- | --- | +| \`--reactist-{{dashCase name}}-tint\` | \`var(--reactist-content-primary)\` | Content color | +| \`--reactist-{{dashCase name}}-fill\` | \`var(--reactist-framework-fill-selected)\` | Background color | +`}</Markdown> + +{/* Theming variables set on `:root` with literal defaults belong in this table too. For + long lists of color variables, a ColorPalette (see avatar.mdx) works better. */} + +{/* If integration responsibilities deliberately stay with the consumer (data fetching, + persistence, entity conventions), make the boundary explicit: + +## What the consumer owns -```css ---reactist-{{dashCase name}}-tint ---reactist-{{dashCase name}}-fill -``` +- **Some responsibility**: what the component expects the consumer to handle, and why. +*/} ## Accessibility @@ -71,4 +110,4 @@ shown are the defaults. - **Screen reader**: the name, role, and state it exposes (and via which prop). - **Focus**: focus management / trapping behavior, if any. -<Canvas of={ {{pascalCase name}}Stories.Accessibility } /> +<Canvas of={ {{~pascalCase name~}} Stories.Accessibility} /> From 3ccd0cf9ca2fb04b75d5565ca53b1017269a3b1c Mon Sep 17 00:00:00 2001 From: Frankie Yan <frankie@doist.com> Date: Fri, 17 Jul 2026 03:31:58 -0700 Subject: [PATCH 09/10] chore(plop): add a docs generator for existing components Co-Authored-By: Claude <noreply@anthropic.com> --- plopfile.mjs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/plopfile.mjs b/plopfile.mjs index 933f806e..78a10f15 100644 --- a/plopfile.mjs +++ b/plopfile.mjs @@ -82,7 +82,32 @@ function componentGenerator(plop) { }) } +/** @param {NodePlopAPI} plop */ +function docsGenerator(plop) { + plop.setGenerator('docs', { + description: 'MDX docs page for an existing component that lacks one', + + prompts: [ + { + type: 'input', + name: 'name', + message: + 'Existing component name, matching its directory (e.g. "toast", "text field")', + }, + ], + + actions: [ + { + type: 'add', + path: 'src/{{dashCase name}}/{{dashCase name}}.mdx', + templateFile: templateFile('component/component.mdx'), + }, + ], + }) +} + /** @param {NodePlopAPI} plop */ export default function (plop) { componentGenerator(plop) + docsGenerator(plop) } From 6f02229f4da3848f76a8526a3f1b8d7fa4051e26 Mon Sep 17 00:00:00 2001 From: Frankie Yan <frankie@doist.com> Date: Fri, 17 Jul 2026 03:33:19 -0700 Subject: [PATCH 10/10] docs: define the component docs-page structure convention Co-Authored-By: Claude <noreply@anthropic.com> --- AGENTS.md | 5 +++-- README.md | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e2bae8ff..aaa98c8a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,7 +35,8 @@ src/ component-name.tsx # Implementation component-name.module.css # CSS Modules styles component-name.test.tsx # Tests - component-name.stories.mdx # Storybook docs (or .tsx) + component-name.stories.tsx # Storybook stories (CSF) + component-name.mdx # Storybook MDX docs page styles/ design-tokens.css # Global CSS custom properties (--reactist-*) utils/ @@ -51,7 +52,7 @@ src/ ### File structure -Every component has `index.ts`, `component.tsx`, `component.module.css`, `component.test.tsx`, and a story file. Use `npm run plop component` to scaffold new components. +Every component has `index.ts`, `component.tsx`, `component.module.css`, `component.test.tsx`, a `component.stories.tsx` story file, and a `component.mdx` docs page. Use `npm run plop component` to scaffold new components. The `component.mdx` docs page follows the section structure documented in README.md under "Documenting a component"; keep the heading names and order, uncomment the optional sections you fill in, wrap any markdown table in a `<Markdown>` block (no remark-gfm), and note that Prettier is never run on `.mdx` files. ### Implementation patterns diff --git a/README.md b/README.md index dd6b1ae0..214ac06c 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,24 @@ The generated source files include the component implementation with sample prop You also need to export your new component by adding a reference to it in the [top-level index file](src/index.ts). -The generator also creates a `<component-name>.mdx` documentation page next to the stories. Edit it to document the component: fill in the Overview and link the matching [design handbook page](https://github.com/Doist/component-libraries/tree/main/handbook/components), keep the sections that apply, and delete the ones that don't. For composable components (multiple exported parts), repeat the API block per part. Its live examples render from the generated `Playground`, `Variants`, and `Accessibility` stories. +### Documenting a component + +The generator also creates a `<component-name>.mdx` documentation page next to the stories; its live examples render from the generated `Default`, `Variants`, `Accessibility`, and `Playground` stories. For an existing component that lacks a docs page, `npm run plop docs` scaffolds just the `.mdx` (repoint its canvases at the component's real stories as your first step). + +Every docs page follows the same structure, in this order: + +1. `<Title />` and `<Subtitle>` (rendered from the story meta, so the page title matches the sidebar) +2. **Overview**: what the component is and the problem it solves, linking the matching [design handbook page](https://github.com/Doist/component-libraries/tree/main/handbook/components) for anatomy and usage guidance +3. **Anatomy** (compound components only): a table mapping each exported part to what it renders and owns +4. **When to use**: concrete scenarios, including when to prefer another component +5. **Basic usage**: the simplest real example +6. **Variants** and **States**: visual variations; rename or replace these with domain sections when the component's modes need it (e.g. a Docked/Overlay split) +7. **Props**: an interactive playground canvas with the props table directly below it; compound components add a `### <Component.Part>` block with `Description` and `ArgTypes` per exported part, importing the parts at the top of the file +8. **Custom properties**: the component's CSS custom properties as a Property/Default/Purpose table (a `ColorPalette` is fine for long color lists) +9. **What the consumer owns** (optional): integration responsibilities the component deliberately leaves to the consumer +10. **Accessibility**: keyboard, screen reader, and focus behavior, with a demo canvas + +Keep the heading names and order. Optional sections are scaffolded as `{/* … */}` comments: uncomment the ones you fill in, delete the ones that don't apply. Constraints consumers must not violate (e.g. "only one modal overlay at a time") get a short callout inside the section they belong to. Markdown tables (Anatomy, Custom properties) must be wrapped in a `<Markdown>{` … `}</Markdown>` block with their inner backticks escaped, since this repo's MDX has no remark-gfm and renders bare tables as literal text (see `avatar.mdx`). Prettier does not format `.mdx` (its MDX parser predates MDX 2/3 and corrupts comments), so match the surrounding formatting by hand. ## Storybook