From 53e8ac89bc870cafdef7c15ece690a8f87da576d Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 3 Nov 2026 20:30:18 -0500 Subject: [PATCH 1/2] docs(mosaic): move the swingset docs house style into the mosaic skill --- .changeset/move-mosaic-docs-house-style.md | 2 + .claude/skills/mosaic/SKILL.md | 35 +-- .../references/documenting-components.md | 264 ++++++++++++++++++ packages/swingset/CLAUDE.md | 244 +--------------- 4 files changed, 272 insertions(+), 273 deletions(-) create mode 100644 .changeset/move-mosaic-docs-house-style.md create mode 100644 .claude/skills/mosaic/references/documenting-components.md diff --git a/.changeset/move-mosaic-docs-house-style.md b/.changeset/move-mosaic-docs-house-style.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/move-mosaic-docs-house-style.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.claude/skills/mosaic/SKILL.md b/.claude/skills/mosaic/SKILL.md index 88844ed63a1..fcae97157a8 100644 --- a/.claude/skills/mosaic/SKILL.md +++ b/.claude/skills/mosaic/SKILL.md @@ -107,33 +107,8 @@ Find usages with `git grep -n renderTriggerLabel`. ## Documenting a component in swingset -`packages/swingset/CLAUDE.md` is the house style — archetypes, required section -order, `meta` conventions. One rule on top of it, because it is the one agents -get wrong: - -**The docs describe the API as it is. They do not carry the reasoning that -produced it.** No "not a `size`, because…", no rejected alternatives, no history -of what the prop used to be. A reader is there to learn what the thing does, and -every sentence of rationale is a sentence they have to skim past to find it. -State the behaviour plainly and briefly, then stop. - -```mdx - - -### Variant - -Which surface the dialog holds, and so the geometry it is given. Not a `size`, -because these are different surfaces rather than one surface at two widths — a -second card width would be a size of the `card` variant, with nowhere to sit on -this axis. - - - -### Variant - -Which surface the dialog holds, and the geometry that comes with it. -``` - -Follow the root `AGENTS.md` rule for code comments. Put implementation rationale -and rejected alternatives in the PR description. Component docs describe the -resulting API. +Read [`references/documenting-components.md`](references/documenting-components.md) +for the house style: layers, `meta` conventions, each archetype's required +section order, and the finishing checklist. The rule agents most often get wrong +is under "Writing the prose": the docs describe the API as it is, never the +reasoning that produced it. diff --git a/.claude/skills/mosaic/references/documenting-components.md b/.claude/skills/mosaic/references/documenting-components.md new file mode 100644 index 00000000000..d39f505a065 --- /dev/null +++ b/.claude/skills/mosaic/references/documenting-components.md @@ -0,0 +1,264 @@ +# Documenting Mosaic components + +`packages/swingset/CLAUDE.md` explains _how the app works_ and `packages/swingset/README.md` covers the _mechanical wiring_ of adding a component (registry, `docModules`, redirect). This file is the **house style** — _what to write_ once the wiring is in place, so every component page reads consistently. + +A component's docs are two files in `packages/swingset/src/stories/`: + +- `.stories.tsx` — the live demos (named React exports + a `meta`). +- `.mdx` — the prose page that embeds those demos via the injected MDX tags (``, ``, ``, ``; see the **MDX** architecture note in `packages/swingset/CLAUDE.md` for the mechanism). + +Pick the archetype below by the component's **layer** (its `meta.group`), then follow that archetype's required MDX section order exactly. Same archetype → same headings in the same order, every time. That uniformity is the whole point. + +## Layers + +`meta.group` places an entry in one of these layers. Group order follows first appearance in the `registry` array. Groups in `ALPHABETICAL_GROUPS` (`packages/swingset/src/lib/registry.ts`) sort their entries by `title`; the rest render in registry order. Within a group, an optional `meta.navigation.category` sub-groups entries under a small collapsible subheading (e.g. `User Profile` splits into `Panels` and `Sections`), collapsed by default unless it contains the active page; category order also follows first appearance in the registry, and uncategorized entries render with no subheading (list them before the categorized ones). Use these exact group strings: + +| Group | What lives here | Archetype | +| -------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------- | +| `User Button` | Composed flow UI (e.g. `UserButton`) | C | +| `User Profile` | Composed flow UI (e.g. `UserProfileProfilePanel`) | C | +| `Components` | Styled Mosaic components — simple, with a flat variant surface (`Button`, `Input`), or compound (`Card`, `Field`, `Menu`, `Popover`) | A | +| `Primitives` | Headless primitives from `@clerk/mosaic`'s `src/primitives/` (`Accordion`) | B | +| `Styles` | Atomic styles that ship as StyleX atoms, not components (`Scroll Area`) | B (adapted) | +| `Hooks` | Headless hooks (`useForm`) | B (adapted) | +| `Localization` | The `localization` prop on `MosaicProvider`: catalogs, overrides, locale, and the message helpers | B (adapted) | + +`User Button` / `User Profile` → `Components` → `Primitives` runs high-level-composition → low-level-primitive. Composed layers are documented as compositions of lower layers (archetype C); leaf layers (Components, Primitives) get full prop/knob docs (archetypes A and B). + +`Styles` and `Hooks` are the non-component layers: there is no element to knob, so they follow +archetype B's shape (Example → Usage → Parts → Styling) with `Props` replaced by whatever the export +actually surfaces — an argument table for a style function, a return-value table for a hook. A +`Styles` entry documents the tokens its atoms read, since those tokens _are_ its API; a +`Hooks` entry needs only `meta`, with no story exports at all, which is the minimum a section entry +needs. + +Archetype A has two forms, chosen by whether the component exposes a single flat set of variant props: **simple** components (`Button`, `Input`) are knob-driven; **compound** components (`Card`, `Field`, `Menu`, `Popover`) have no flat variant props to knob, so they're documented like a primitive but themed. Both are detailed under Archetype A below. + +## `meta` conventions (all archetypes) + +```ts +export const meta: StoryMeta = { + group: 'Components', // exact group string from the table + title: 'Button', // drives slug + the page

+ label: 'Delete Org', // optional friendlier sidebar text + status: 'stable', // maturity dot in sidebar + page badge; omit to show no status + source: 'packages/mosaic/src/components/button/button.tsx', // repo-root path → "View source" + styles: { + // Hand-written variant surface — archetype A · simple only + _variants: { variant: { primary: {}, outline: {} }, size: { sm: {}, md: {} } }, + _defaultVariants: { variant: 'primary', size: 'md' }, + }, +}; +``` + +- `title` is the component's export name; it produces the slug and is what readers match against code. Set `label` only when the sidebar should read differently (the slug and page heading still come from `title`). +- `status` marks the entry's maturity — `'stable'`, `'wip'`, or `'todo'` (planned, not yet implemented; e.g. `Table`) — rendered as a colored dot (green / amber / gray) to the left of the entry in the sidebar, and as a dot-plus-label badge to the right of the page's `# Title` h1. It is explicit: omitting it displays no status anywhere (not every entry carries one, e.g. `Scroll Area`). A todo entry is `meta` alone plus a one-paragraph MDX page; it skips its archetype's required sections (and `source`) until the component exists. +- `substatus` optionally says _why_ a wip entry is wip — one of the `WipSubstatus` values in `packages/swingset/src/lib/types.ts`. It renders in the page badge (`wip: needs wire-up`) and the sidebar dot's tooltip. Most wip entries won't need one; it is ignored on stable entries. +- `source` is always a path **relative to the monorepo root**, pointing at the file that exports the documented component. Always set it — it powers the "View source" link. +- `styles` declares the component's variant props and is **required for archetype A's simple (knob-driven) form** (it generates the knobs and the ``). Keep it in sync with the component's real prop union by hand — StyleX compiles its styles away, so nothing derives it for you. Omit it for compound A components, and for B and C. + +Always import the component explicitly — never `import *`. + +## Archetype A — styled component (`Components`) + +A styled Mosaic component. Which of the two forms below applies is decided by the component's shape, not by preference: if it exposes a single flat set of variant props (declared as `meta.styles`), use the **simple** form; if it's compound — a set of parts with no flat variant props (`Card`, `Field`, `Menu`, `Popover`) — use the **compound** form. + +**Every `Components`-layer story file exposes its source so each `` example renders a code footer.** Add the self-import once, right after the imports: + +```ts +export { default as __source } from './.stories?raw'; +``` + +That's all the wiring needed — `StoryEmbed` picks `__source` up automatically and renders a collapsible "View code" footer keyed to each example's story function (see the `` code-footer architecture note in `packages/swingset/CLAUDE.md`). No MDX change is required; keep authoring `` as before. This applies to both A forms (simple and compound) and to every example a Components page ships. + +### A · simple — flat variant props (`Button`, `Input`) + +Has a declared variant surface, so the page is **knob-driven**: an interactive canvas plus a generated prop table. Required MDX section order: + +```mdx +import * as ButtonStories from './button.stories'; + +# Button + + + +## Playground + + + +## Props + + + +## Usage + + + Click me + + +--- + +## Examples + +### Sizes + + + +### Disabled + + +``` + +- **Playground / Props / Usage are mandatory and always in this order.** The three share one playground state: editing a row in `` re-renders `` above it and regenerates the `` snippet below it. +- The story file exports a primary demo (rendered by ``) plus one named export per variation under **Examples**. Each story takes `props: Record` and casts through a local `knobsAsProps` helper — knobs are dynamically typed, the component isn't. +- Use ``'s `extra` for documenting non-variant props; the `xstyle` escape-hatch row is appended for you. +- Use `` to pin static, non-knob props in the generated snippet. +- `` renders `Prop | Type | Default | Value`: the **Default** column is filled from `meta.styles._defaultVariants`, and the **Value** column is the live knob seeded with that default. No per-row default annotation is needed; see _Document the default value_ under Archetype B. + +### A · compound — parts, no flat variant props (`Card`, `Field`, `Menu`, `Popover`) + +A compound styled component (`Popover.Root`/`Popover.Popup`/…) has no single flat prop interface to knob, so there's no `` or ``. Document it like a primitive (archetype B) but themed — the difference is the **Styling** section, which lists each part's `.cl-` class and `data-` attributes rather than saying "bring your own CSS". Required MDX section order: + +```mdx +import * as PopoverStories from './popover.component.stories'; + +# Popover + + + +## Example + + + +## Usage + + + +## Parts + + + +## Styling + + +``` + +The story is `meta` (no `styles`) plus a single `Default` export that renders the composed parts. The file pair is named `.component.stories.tsx` / `.component.mdx` so it doesn't collide with the headless `Primitives` entry of the same title (e.g. `Menu`, `Popover` exist in both layers); the `docModules` map disambiguates by group. + +## Archetype B — headless primitive (`Primitives`) + +No styles, so there's no knob canvas. The single demo renders the primitive **raw (unstyled)** to show only behavior, state, and ARIA wiring. The prop/styling tables are **hand-written** (there is no variant surface to derive them from). Required MDX section order: + +```mdx +# Accordion + + + +## Example + + + +## Usage + + + +## Parts + + + +## Props + + + +## Styling + + +``` + +The story is `meta` (no `styles`) plus a single `Default` export that renders the primitive unstyled. Don't add a `` or `` — primitives have neither knobs nor a variant surface to drive them. + +**Document the default value for every prop in a dedicated Default column.** Every props table — auto and hand-written — has a **Default** column; the `Type` stays a plain union/enum and the default is named in its own column (the convention every component-doc site and TypeDoc's `@default` tag follow), never inlined into the type. The auto `` renders `Prop | Type | Default | Value` and fills Default from `meta.styles._defaultVariants` (the **Value** column is the live knob seeded with that default); hand-written tables render `Prop | Type | Default | Description` and fill it by hand. Name the default member (`'base'`, `'multiple'`, `'bottom-start'`); use `—` when there is no default (a controlled-only or required prop) and append `(required)` for required props; when the default is behavioral rather than a literal, state it in words (`inherits Root`, `falls back to value`). + +## Archetype C — composed layer (`User Button`, `User Profile`) + +These compose lower layers, so the docs lead with the composition rather than knobs. Required MDX: + +```mdx +import * as SwitcherStories from './user-button.stories'; + +# UserButton + + + + +``` + +- The `composition` array names each lower-layer component this one builds on, with a link to that component's page and its `layer`. List every direct dependency so the layering is navigable. +- The story is `meta` plus a single `Default` export that renders the composed UI with no knobs. + +## Writing the prose + +- Keep the intro to one short, present-tense paragraph: what the thing is and what it's for. For primitives, say explicitly that it's headless and ships no styles. +- Prose should add what a demo can't — behavior, accessibility, when to reach for it — not restate prop names already in the table. +- Describe the API as it is; leave the reasoning that produced it out. No "not a `size`, because…", no rejected alternatives, no history of what a prop used to be — that belongs in the PR description. The docs get the conclusion, stated plainly and briefly. +- Lead every page with the heading hierarchy its archetype prescribes; don't invent new top-level sections or reorder them. Consistency across pages is the goal. + +Every sentence of rationale is one the reader has to skim past to find what the thing does: + +```mdx + + +### Variant + +Which surface the dialog holds, and so the geometry it is given. Not a `size`, +because these are different surfaces rather than one surface at two widths — a +second card width would be a size of the `card` variant, with nowhere to sit on +this axis. + + + +### Variant + +Which surface the dialog holds, and the geometry that comes with it. +``` + +## Before you finish + +- [ ] `meta.source` is set to a repo-root-relative path. +- [ ] Story renders. +- [ ] `Components`-layer story files export `__source` (the `?raw` self-import) so every `` example gets a "View code" footer. +- [ ] MDX sections match the archetype's required order exactly. +- [ ] Every props-table row states its default in the **Default** column (auto `` fills it from `meta.styles._defaultVariants`; `—` / `(required)` when none). +- [ ] Wiring done per `packages/swingset/README.md`: `registry.ts`, `DocsViewer.tsx`'s `docModules`, and the `app/(explorer)/page.tsx` redirect if this is now the first component. +- [ ] `pnpm format --filter @clerk/swingset` is clean. diff --git a/packages/swingset/CLAUDE.md b/packages/swingset/CLAUDE.md index 5475c0887dc..f126e0bcdfe 100644 --- a/packages/swingset/CLAUDE.md +++ b/packages/swingset/CLAUDE.md @@ -50,246 +50,4 @@ These require reading several files together; the `README.md` covers the step-by ## Documenting Mosaic components -The facts above explain _how the app works_ and `README.md` covers the _mechanical wiring_ of adding a component (registry, `docModules`, redirect). This section is the **house style** — _what to write_ once the wiring is in place, so every component page reads consistently. - -A component's docs are two files in `src/stories/`: - -- `.stories.tsx` — the live demos (named React exports + a `meta`). -- `.mdx` — the prose page that embeds those demos via the injected MDX tags (``, ``, ``, ``; see the **MDX** architecture note above for the mechanism). - -Pick the archetype below by the component's **layer** (its `meta.group`), then follow that archetype's required MDX section order exactly. Same archetype → same headings in the same order, every time. That uniformity is the whole point. - -### Layers - -`meta.group` places an entry in one of these layers. Group order follows first appearance in the `registry` array. Groups in `ALPHABETICAL_GROUPS` (`src/lib/registry.ts`) sort their entries by `title`; the rest render in registry order. Within a group, an optional `meta.navigation.category` sub-groups entries under a small collapsible subheading (e.g. `User Profile` splits into `Panels` and `Sections`), collapsed by default unless it contains the active page; category order also follows first appearance in the registry, and uncategorized entries render with no subheading (list them before the categorized ones). Use these exact group strings: - -| Group | What lives here | Archetype | -| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------- | -| `User Button` | Composed flow UI (e.g. `UserButton`) | C | -| `User Profile` | Composed flow UI (e.g. `UserProfileProfilePanel`) | C | -| `Components` | Styled Mosaic components — simple, with a flat variant surface (`Button`, `Input`), or compound (`Card`, `Field`, `Menu`, `Popover`) | A | -| `Primitives` | Headless primitives from `@clerk/mosaic`'s `src/primitives/` (`Accordion`) | B | -| `Styles` | Atomic styles that ship as StyleX atoms, not components (`Scroll Area`) | B (adapted) | -| `Hooks` | Headless hooks (`useForm`) | B (adapted) | -| `Localization` | The `localization` prop on `MosaicProvider`: catalogs, overrides, locale, and the message helpers | B (adapted) | - -`User Button` / `User Profile` → `Components` → `Primitives` runs high-level-composition → low-level-primitive. Composed layers are documented as compositions of lower layers (archetype C); leaf layers (Components, Primitives) get full prop/knob docs (archetypes A and B). - -`Styles` and `Hooks` are the non-component layers: there is no element to knob, so they follow -archetype B's shape (Example → Usage → Parts → Styling) with `Props` replaced by whatever the export -actually surfaces — an argument table for a style function, a return-value table for a hook. A -`Styles` entry documents the tokens its atoms read, since those tokens _are_ its API; a -`Hooks` entry needs only `meta`, with no story exports at all, which is the minimum a section entry -needs. - -Archetype A has two forms, chosen by whether the component exposes a single flat set of variant props: **simple** components (`Button`, `Input`) are knob-driven; **compound** components (`Card`, `Field`, `Menu`, `Popover`) have no flat variant props to knob, so they're documented like a primitive but themed. Both are detailed under Archetype A below. - -### `meta` conventions (all archetypes) - -```ts -export const meta: StoryMeta = { - group: 'Components', // exact group string from the table - title: 'Button', // drives slug + the page

- label: 'Delete Org', // optional friendlier sidebar text - status: 'stable', // maturity dot in sidebar + page badge; omit to show no status - source: 'packages/mosaic/src/components/button/button.tsx', // repo-root path → "View source" - styles: { - // Hand-written variant surface — archetype A · simple only - _variants: { variant: { primary: {}, outline: {} }, size: { sm: {}, md: {} } }, - _defaultVariants: { variant: 'primary', size: 'md' }, - }, -}; -``` - -- `title` is the component's export name; it produces the slug and is what readers match against code. Set `label` only when the sidebar should read differently (the slug and page heading still come from `title`). -- `status` marks the entry's maturity — `'stable'`, `'wip'`, or `'todo'` (planned, not yet implemented; e.g. `Table`) — rendered as a colored dot (green / amber / gray) to the left of the entry in the sidebar, and as a dot-plus-label badge to the right of the page's `# Title` h1. It is explicit: omitting it displays no status anywhere (not every entry carries one, e.g. `Scroll Area`). A todo entry is `meta` alone plus a one-paragraph MDX page; it skips its archetype's required sections (and `source`) until the component exists. -- `substatus` optionally says _why_ a wip entry is wip — one of the `WipSubstatus` values in `src/lib/types.ts`. It renders in the page badge (`wip: needs wire-up`) and the sidebar dot's tooltip. Most wip entries won't need one; it is ignored on stable entries. -- `source` is always a path **relative to the monorepo root**, pointing at the file that exports the documented component. Always set it — it powers the "View source" link. -- `styles` declares the component's variant props and is **required for archetype A's simple (knob-driven) form** (it generates the knobs and the ``). Keep it in sync with the component's real prop union by hand — StyleX compiles its styles away, so nothing derives it for you. Omit it for compound A components, and for B and C. - -Always import the component explicitly — never `import *`. - -### Archetype A — styled component (`Components`) - -A styled Mosaic component. Which of the two forms below applies is decided by the component's shape, not by preference: if it exposes a single flat set of variant props (declared as `meta.styles`), use the **simple** form; if it's compound — a set of parts with no flat variant props (`Card`, `Field`, `Menu`, `Popover`) — use the **compound** form. - -**Every `Components`-layer story file exposes its source so each `` example renders a code footer.** Add the self-import once, right after the imports: - -```ts -export { default as __source } from './.stories?raw'; -``` - -That's all the wiring needed — `StoryEmbed` picks `__source` up automatically and renders a collapsible "View code" footer keyed to each example's story function (see the `` code-footer architecture note above). No MDX change is required; keep authoring `` as before. This applies to both A forms (simple and compound) and to every example a Components page ships. - -#### A · simple — flat variant props (`Button`, `Input`) - -Has a declared variant surface, so the page is **knob-driven**: an interactive canvas plus a generated prop table. Required MDX section order: - -```mdx -import * as ButtonStories from './button.stories'; - -# Button - - - -## Playground - - - -## Props - - - -## Usage - - - Click me - - ---- - -## Examples - -### Sizes - - - -### Disabled - - -``` - -- **Playground / Props / Usage are mandatory and always in this order.** The three share one playground state: editing a row in `` re-renders `` above it and regenerates the `` snippet below it. -- The story file exports a primary demo (rendered by ``) plus one named export per variation under **Examples**. Each story takes `props: Record` and casts through a local `knobsAsProps` helper — knobs are dynamically typed, the component isn't. -- Use ``'s `extra` for documenting non-variant props; the `xstyle` escape-hatch row is appended for you. -- Use `` to pin static, non-knob props in the generated snippet. -- `` renders `Prop | Type | Default | Value`: the **Default** column is filled from `meta.styles._defaultVariants`, and the **Value** column is the live knob seeded with that default. No per-row default annotation is needed; see _Document the default value_ under Archetype B. - -#### A · compound — parts, no flat variant props (`Card`, `Field`, `Menu`, `Popover`) - -A compound styled component (`Popover.Root`/`Popover.Popup`/…) has no single flat prop interface to knob, so there's no `` or ``. Document it like a primitive (archetype B) but themed — the difference is the **Styling** section, which lists each part's `.cl-` class and `data-` attributes rather than saying "bring your own CSS". Required MDX section order: - -```mdx -import * as PopoverStories from './popover.component.stories'; - -# Popover - - - -## Example - - - -## Usage - - - -## Parts - - - -## Styling - - -``` - -The story is `meta` (no `styles`) plus a single `Default` export that renders the composed parts. The file pair is named `.component.stories.tsx` / `.component.mdx` so it doesn't collide with the headless `Primitives` entry of the same title (e.g. `Menu`, `Popover` exist in both layers); the `docModules` map disambiguates by group. - -### Archetype B — headless primitive (`Primitives`) - -No styles, so there's no knob canvas. The single demo renders the primitive **raw (unstyled)** to show only behavior, state, and ARIA wiring. The prop/styling tables are **hand-written** (there is no variant surface to derive them from). Required MDX section order: - -```mdx -# Accordion - - - -## Example - - - -## Usage - - - -## Parts - - - -## Props - - - -## Styling - - -``` - -The story is `meta` (no `styles`) plus a single `Default` export that renders the primitive unstyled. Don't add a `` or `` — primitives have neither knobs nor a variant surface to drive them. - -**Document the default value for every prop in a dedicated Default column.** Every props table — auto and hand-written — has a **Default** column; the `Type` stays a plain union/enum and the default is named in its own column (the convention every component-doc site and TypeDoc's `@default` tag follow), never inlined into the type. The auto `` renders `Prop | Type | Default | Value` and fills Default from `meta.styles._defaultVariants` (the **Value** column is the live knob seeded with that default); hand-written tables render `Prop | Type | Default | Description` and fill it by hand. Name the default member (`'base'`, `'multiple'`, `'bottom-start'`); use `—` when there is no default (a controlled-only or required prop) and append `(required)` for required props; when the default is behavioral rather than a literal, state it in words (`inherits Root`, `falls back to value`). - -### Archetype C — composed layer (`User Button`, `User Profile`) - -These compose lower layers, so the docs lead with the composition rather than knobs. Required MDX: - -```mdx -import * as SwitcherStories from './user-button.stories'; - -# UserButton - - - - -``` - -- The `composition` array names each lower-layer component this one builds on, with a link to that component's page and its `layer`. List every direct dependency so the layering is navigable. -- The story is `meta` plus a single `Default` export that renders the composed UI with no knobs. - -### Writing the prose - -- Keep the intro to one short, present-tense paragraph: what the thing is and what it's for. For primitives, say explicitly that it's headless and ships no styles. -- Prose should add what a demo can't — behavior, accessibility, when to reach for it — not restate prop names already in the table. -- Describe the API as it is; leave the reasoning that produced it out. No "not a `size`, because…", no rejected alternatives, no history of what a prop used to be — that belongs in the PR description. The docs get the conclusion, stated plainly and briefly. -- Lead every page with the heading hierarchy its archetype prescribes; don't invent new top-level sections or reorder them. Consistency across pages is the goal. - -### Before you finish - -- [ ] `meta.source` is set to a repo-root-relative path. -- [ ] Story renders. -- [ ] `Components`-layer story files export `__source` (the `?raw` self-import) so every `` example gets a "View code" footer. -- [ ] MDX sections match the archetype's required order exactly. -- [ ] Every props-table row states its default in the **Default** column (auto `` fills it from `meta.styles._defaultVariants`; `—` / `(required)` when none). -- [ ] Wiring done per `README.md`: `registry.ts`, `DocsViewer.tsx`'s `docModules`, and the `app/(explorer)/page.tsx` redirect if this is now the first component. -- [ ] `pnpm format --filter @clerk/swingset` is clean. +Before adding a component page or restructuring a story or MDX page, read the house style in `.claude/skills/mosaic/references/documenting-components.md`: layers, `meta` conventions, each archetype's required section order, and the finishing checklist. From acab7cf9c5f236cb46cd6a36f8ca49f91bc4c24a Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 3 Nov 2026 20:40:00 -0500 Subject: [PATCH 2/2] docs(mosaic): list the Organization Profile, Misc and Blocks groups in the docs house style --- .../references/documenting-components.md | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/.claude/skills/mosaic/references/documenting-components.md b/.claude/skills/mosaic/references/documenting-components.md index d39f505a065..ed2bdf13946 100644 --- a/.claude/skills/mosaic/references/documenting-components.md +++ b/.claude/skills/mosaic/references/documenting-components.md @@ -13,17 +13,20 @@ Pick the archetype below by the component's **layer** (its `meta.group`), then f `meta.group` places an entry in one of these layers. Group order follows first appearance in the `registry` array. Groups in `ALPHABETICAL_GROUPS` (`packages/swingset/src/lib/registry.ts`) sort their entries by `title`; the rest render in registry order. Within a group, an optional `meta.navigation.category` sub-groups entries under a small collapsible subheading (e.g. `User Profile` splits into `Panels` and `Sections`), collapsed by default unless it contains the active page; category order also follows first appearance in the registry, and uncategorized entries render with no subheading (list them before the categorized ones). Use these exact group strings: -| Group | What lives here | Archetype | -| -------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------- | -| `User Button` | Composed flow UI (e.g. `UserButton`) | C | -| `User Profile` | Composed flow UI (e.g. `UserProfileProfilePanel`) | C | -| `Components` | Styled Mosaic components — simple, with a flat variant surface (`Button`, `Input`), or compound (`Card`, `Field`, `Menu`, `Popover`) | A | -| `Primitives` | Headless primitives from `@clerk/mosaic`'s `src/primitives/` (`Accordion`) | B | -| `Styles` | Atomic styles that ship as StyleX atoms, not components (`Scroll Area`) | B (adapted) | -| `Hooks` | Headless hooks (`useForm`) | B (adapted) | -| `Localization` | The `localization` prop on `MosaicProvider`: catalogs, overrides, locale, and the message helpers | B (adapted) | - -`User Button` / `User Profile` → `Components` → `Primitives` runs high-level-composition → low-level-primitive. Composed layers are documented as compositions of lower layers (archetype C); leaf layers (Components, Primitives) get full prop/knob docs (archetypes A and B). +| Group | What lives here | Archetype | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------- | +| `User Button` | Composed flow UI (e.g. `UserButton`) | C | +| `User Profile` | Composed flow UI (e.g. `UserProfileProfilePanel`) | C | +| `Organization Profile` | Composed flow UI (e.g. `OrganizationProfileGeneralPanel`) | C | +| `Misc` | Standalone composed components (e.g. `OrganizationSwitcher`, `Waitlist`) | C | +| `Blocks` | Flows assembled from components, wired by the caller's machine (e.g. `Confirmation`, `Destructive`) | C | +| `Components` | Styled Mosaic components — simple, with a flat variant surface (`Button`, `Input`), or compound (`Card`, `Field`, `Menu`, `Popover`) | A | +| `Primitives` | Headless primitives from `@clerk/mosaic`'s `src/primitives/` (`Accordion`) | B | +| `Styles` | Atomic styles that ship as StyleX atoms, not components (`Scroll Area`) | B (adapted) | +| `Hooks` | Headless hooks (`useForm`) | B (adapted) | +| `Localization` | The `localization` prop on `MosaicProvider`: catalogs, overrides, locale, and the message helpers | B (adapted) | + +`User Button` / `User Profile` / `Organization Profile` / `Misc` / `Blocks` → `Components` → `Primitives` runs high-level-composition → low-level-primitive. Composed layers are documented as compositions of lower layers (archetype C); leaf layers (Components, Primitives) get full prop/knob docs (archetypes A and B). `Styles` and `Hooks` are the non-component layers: there is no element to knob, so they follow archetype B's shape (Example → Usage → Parts → Styling) with `Props` replaced by whatever the export @@ -201,7 +204,7 @@ The story is `meta` (no `styles`) plus a single `Default` export that renders th **Document the default value for every prop in a dedicated Default column.** Every props table — auto and hand-written — has a **Default** column; the `Type` stays a plain union/enum and the default is named in its own column (the convention every component-doc site and TypeDoc's `@default` tag follow), never inlined into the type. The auto `` renders `Prop | Type | Default | Value` and fills Default from `meta.styles._defaultVariants` (the **Value** column is the live knob seeded with that default); hand-written tables render `Prop | Type | Default | Description` and fill it by hand. Name the default member (`'base'`, `'multiple'`, `'bottom-start'`); use `—` when there is no default (a controlled-only or required prop) and append `(required)` for required props; when the default is behavioral rather than a literal, state it in words (`inherits Root`, `falls back to value`). -## Archetype C — composed layer (`User Button`, `User Profile`) +## Archetype C — composed layer (`User Button`, `User Profile`, `Organization Profile`, `Misc`, `Blocks`) These compose lower layers, so the docs lead with the composition rather than knobs. Required MDX: