Create a full-quality documentation page for a design system component on the website. Use when asked to document a component on the website, add a component docs page, or create the website page for a component.
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
SSkills DirectorySkills Directory
Is that skill safe to install?
Are you the author of Component Doc Page?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/robritacca-dotcom-component-doc-page)
---
name: component-doc-page
description: Create a full-quality documentation page for a design system component on the website. Use when asked to document a component on the website, add a component docs page, or create the website page for a component.
icon: article
displayDescription: "Creates a full-quality documentation page for a design system component on the website. Reads the component's props to generate a variant showcase grid (the Button page is the benchmark), writes all three page files, and adds the component's preview entry (the one hand-maintained surface). The index card, sidebar, sitemap and breadcrumbs derive from the component registry, so there is no navigation to wire."
invoke: ["document [X] on the website","add a docs page for [X]","create the website page for [X]"]
---
# component-doc-page
Create a full-quality documentation page for a design system component on the website.
## When invoked
Use this skill when asked to document a component on the website, add a component page, or create docs for a component — phrases like "document [X] on the website", "add a docs page for [X]", "create the website page for [X]".
This is a more thorough, component-specific version of `new-page`. The Button page is the quality benchmark for static variant grids; for components whose value is interaction or streaming state (the `ai` category), the chat-message page (`website/src/app/components/chat-message/page.tsx`) is the exemplar — a `"use client"` page with small stateful demos instead of a grid.
## Instructions
1. **Gather requirements** if not already provided:
- Component name (PascalCase)
- Storybook path (optional — format: `/?path=/docs/components-<name lowercased>--docs`, so `ActionCard` is `components-actioncard--docs`)
2. **Read the source component** `src/components/ComponentName/ComponentName.tsx`:
- Extract all props from the TypeScript interface (the entry in `website/src/data/component-api.generated.ts` has them pre-parsed with types, defaults and descriptions — the source is still worth reading for behaviour)
- Identify all variant enumerations (e.g. `variant`, `size`, `status` props with union types)
- Understand the component's states (default, hover, active, disabled, loading, etc.)
- Note the BEM class names used for each variant/state
3. **Read the gold-standard reference:**
- `website/src/app/components/button/page.tsx` — study the variant showcase grid structure (rows = states, columns = variants), the `pageHeader` block, `introSection`, and how `PageLinks` is used
- `website/src/app/components/button/page.module.css` — CSS module structure
4. **Create `website/src/app/components/<component-slug>/page.tsx`:**
- Mirror the Button page's layout shell exactly — same components, same nesting, same class names — and render `<ComponentsSidebar />` with no props: it resolves the active entry from the pathname and its groups from the registry. Don't improvise structure. Mirror the *structure*, not deprecated APIs: if an existing page still uses a prop marked `@deprecated` in the component source (the `priority` → `variant` rename is the precedent), write the current prop name.
- Invariants the exemplar can't teach:
- `subDisplay` is a *tagline* for the component (e.g. Button's "The main action element") — not the word "Components"; the breadcrumb already shows the section
- `introBody` is a clear 1–2 sentence description of the component's purpose, inferred from its props and JSDoc if available
- All copy on the page (tagline, intro, section labels) follows `content-design.md` — neutral, sentence case, no em dashes
- Import the component through the package, never a relative path into `src/`: `import { X } from "rift-ds/components/X/X"` (recharts-backed charts come from `rift-ds/charts`) — the website is an npm-workspace consumer of the published package's exports
- Include `PageLinks` with the Storybook path. Its Figma link needs no prop: it reads the page's frame from `website/src/data/figma-nodes.json`, which the `new-figma-component` skill fills
- End the page with `<ComponentInstallStrip slug="<component-slug>" />` after the last section — the per-page install commands, rendered at the page floor across the content column's full width. `scripts/validate-website-surfaces.mjs` requires the mount with the page's own slug and fails the build without it
- **Variant showcase grid**: render the component in every meaningful combination of its variants and states. For components with discrete variants × states (like Button), render a proper grid. For simpler components, render one example per meaningful state/variant.
- If the component has a `tone` prop beside `variant` (emphasis × colour, per CLAUDE.md's Component Anatomy), the grid's columns are the emphasis × tone cells the component actually draws, built from one cell list the way the Button page does. Skip combinations the component renders identically rather than showing a duplicate, and never demo a deprecated single-axis value.
- If a demo renders h2 headings of its own (a heading-bearing component like SectionTitle, or Prose sample content), wrap that demo container in `data-anchor-ignore` — the site-wide floating anchor rail reads every page's h2s, and demo headings are the demo's, not sections of the page. The discovery rules live in the doc block of `website/src/components/FloatingAnchorNav/SiteAnchorRail.tsx`.
5. **Create `website/src/app/components/<component-slug>/page.module.css`:**
- Standard layout classes: `dsLayout`, `dsContent`, `pageHeader`, `pageTitle`, `subDisplay`, `introSection`, `introBody`
- Any additional classes needed for the variant showcase grid
- Page assembly follows design.md's **Composition** section (rhythm ladder, spacing ownership, one level of chrome, dividers last) — the exemplar shows the pattern, the spec owns the rules
- **Prose is never width-capped.** No `max-width` on `introBody`, section body text, or any paragraph class — page text fills the content column, exactly as the Button page does. A `max-width` is only legitimate on a *demo container* (a box that holds a rendered component, e.g. a drawer or form-control mount) where the component itself needs a bounded stage. If you find yourself capping a paragraph "for readability", don't — the column width is the layout's decision, not the page's. `scripts/validate-page-prose-width.mjs` fails the build on a page rule that sets paragraph type and a `max-width`; text that belongs to a rendered demo is sanctioned in its table.
- CSS custom properties only
- Mobile type and section rhythm collapse at the **token layer**, system-wide (`design.md`'s responsive spec owns the breakpoint) — do not add per-page `@media` overrides for tokenized values; when the showcase grid genuinely needs a breakpoint, use a width from `src/tokens/breakpoints.ts` (a content-driven one outside it needs a `ds-allow(breakpoint)` directive above the query; `scripts/validate-breakpoints.mjs` enforces it)
6. **Create `website/src/app/components/<component-slug>/layout.tsx`** — it is exactly this, with no description of its own:
```tsx
import { componentPageMetadata } from "@/config/navigation";
export const metadata = componentPageMetadata("<component-slug>");
export default function Layout({ children }: { children: React.ReactNode }) {
return <>{children}</>;
}
```
Both the title and the description resolve from the component's entry in `src/components/registry.json` (or, for the chat widget, `src/chat/registry.json`; `website/src/data/component-pages.ts` merges the two) — that is the only place the one-line description lives, so do **not** pass one here. `scripts/validate-page-titles.mjs` requires this exact call and fails the build otherwise; a slug with no registry entry fails the website build with a message naming the fix.
7. **Do not touch `website/src/config/navigation.ts`.** `componentsSidebarLinks` is **derived** from the registry — there is no list to add to. Registering the component in `src/components/registry.json` (with its `label`, `slug` and `description`; the chat widget's entry lives in `src/chat/registry.json` and is merged in through `website/src/data/component-pages.ts`) is what puts it in the sidebar, and the sitemap, mega-nav and breadcrumbs follow from there. If the component is not yet registered, do that first; the `new-component` skill covers the entry's shape.
8. **Add a preview entry to `website/src/components/ComponentPreviews/ComponentPreviews.tsx`** — the one surface still hand-maintained, because each entry holds a bespoke preview:
- Add a `"component-slug": () => (...)` entry to the `previews` map — use the real component (imported from `rift-ds`) where it reads well at miniature size, as most entries do, or a small inline-styled mockup where it doesn't (see the accordion entry). An icon in a mockup sets `--icon-size` inline (`style={{ ["--icon-size" as string]: "14px" }}`), never `fontSize`: the icon class sizes its box from that property, so a bare font size leaves a small glyph in a default-size box and the tile sits off-centre. The index's category section renders the entry through `ComponentCardGrid`, so there is no card to place by hand and no order to maintain.
9. **Sitemap is automatic** — `website/src/app/sitemap.ts` derives its routes from the shared sidebar configs, which for components derive from the registry. Do not edit `sitemap.ts` by hand. `scripts/validate-website-surfaces.mjs` build-enforces the showcase page, its `ComponentInstallStrip` mount (step 4), the step-8 preview entry (in both directions — an entry whose key is not a registered slug fails too), and the `design.md` spec section; the sidebar entry and its ordering are no longer checked because they can no longer drift. If the component has no `design.md` spec section yet, add one before building — see the `new-component` skill's spec step for the expected shape.