| name | component-docs |
| description | Generate or update component documentation with usage examples, props tables, and Storybook stories. Use when documenting new components or improving existing component docs. |
| allowed-tools | Read, Edit, Write, Grep, Glob |
Component Documentation Skill
This skill helps you create and maintain component documentation in packages/ui/.
When to Use This Skill
- Writing documentation for new components
- Creating usage examples and code snippets
- Documenting component props and variants
- Setting up Storybook stories
- Generating API documentation
- Maintaining component README files
Documentation Structure
packages/ui/
├── src/
│ ├── components/
│ │ ├── button.tsx # Component implementation
│ │ ├── button.stories.tsx # Storybook stories (optional)
│ │ └── __tests__/
│ │ └── button.test.tsx # Component tests
├── docs/
│ ├── components/
│ │ ├── button.md # Component documentation
│ │ └── card.md
│ └── guides/
│ ├── getting-started.md
│ └── customization.md
└── README.md # Package overview
Component Documentation Template
Basic Component Documentation
# Button
A customizable button component built with Radix UI primitives.
## Installation
This component is part of the `@sgcarstrends/ui` package.
\`\`\`bash
pnpm add @sgcarstrends/ui
\`\`\`
## Usage
\`\`\`tsx
import { Button } from "@sgcarstrends/ui";
export function Example() {
return <Button>Click me</Button>;
}
\`\`\`
## Variants
### Default
\`\`\`tsx
<Button variant="default">Default Button</Button>
\`\`\`
### Destructive
\`\`\`tsx
<Button variant="destructive">Delete</Button>
\`\`\`
### Outline
\`\`\`tsx
<Button variant="outline">Outline Button</Button>
\`\`\`
## Sizes
\`\`\`tsx
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
<Button size="icon">
<Icon />
</Button>
\`\`\`
## Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| variant | `"default" \| "destructive" \| "outline" \| "secondary" \| "ghost" \| "link"` | `"default"` | The visual style of the button |
| size | `"default" \| "sm" \| "lg" \| "icon"` | `"default"` | The size of the button |
| asChild | `boolean` | `false` | Render as child element |
| disabled | `boolean` | `false` | Whether button is disabled |
## Examples
### With Icon
\`\`\`tsx
import { Button } from "@sgcarstrends/ui";
import { DownloadIcon } from "lucide-react";
export function DownloadButton() {
return (
<Button>
<DownloadIcon className="mr-2 h-4 w-4" />
Download
</Button>
);
}
\`\`\`
### Loading State
\`\`\`tsx
"use client";
import { useState } from "react";
import { Button } from "@sgcarstrends/ui";
export function LoadingButton() {
const [isLoading, setIsLoading] = useState(false);
return (
<Button disabled={isLoading} onClick={() => setIsLoading(true)}>
{isLoading ? "Loading..." : "Submit"}
</Button>
);
}
\`\`\`
### As Link
\`\`\`tsx
import { Button } from "@sgcarstrends/ui";
import Link from "next/link";
export function LinkButton() {
return (
<Button asChild>
<Link href="/about">Learn More</Link>
</Button>
);
}
\`\`\`
## Accessibility
- Uses semantic `<button>` element
- Supports keyboard navigation (Enter, Space)
- Proper focus states
- ARIA attributes supported via props
## Styling
The Button component uses Tailwind CSS and CSS variables for theming. Customize via:
\`\`\`tsx
<Button className="custom-classes">Custom Styled</Button>
\`\`\`
## Related Components
- [Link Button](#)
- [Icon Button](#)
- [Button Group](#)
JSDoc Comments
Add comprehensive JSDoc comments to components:
/**
* A customizable button component with multiple variants and sizes.
*
* @component
* @example
* ```tsx
* <Button variant="default" size="md">
* Click me
* </Button>
* ```
*/
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
/**
* Render the button as a child element (e.g., Link)
* @default false
*/
asChild?: boolean;
/**
* The visual style variant of the button
* @default "default"
*/
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
/**
* The size of the button
* @default "default"
*/
size?: "default" | "sm" | "lg" | "icon";
}
/**
* Button component
*
* @param {ButtonProps} props - Button props
* @returns {React.ReactElement} Button element
*
* @example
* Basic usage
* ```tsx
* <Button>Click me</Button>
* ```
*
* @example
* With variant
* ```tsx
* <Button variant="destructive">Delete</Button>
* ```
*
* @example
* As a link
* ```tsx
* <Button asChild>
* <Link href="/about">About</Link>
* </Button>
* ```
*/
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
}
);
Button.displayName = "Button";
Storybook Stories
Installing Storybook (if not present)
cd packages/ui
# Initialize Storybook
npx storybook@latest init
Basic Story
// packages/ui/src/components/button.stories.tsx
import type { Meta, StoryObj } from "@storybook/react";
import { Button } from "./button";
const meta = {
title: "Components/Button",
component: Button,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
argTypes: {
variant: {
control: "select",
options: ["default", "destructive", "outline", "secondary", "ghost", "link"],
},
size: {
control: "select",
options: ["default", "sm", "lg", "icon"],
},
},
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
children: "Button",
},
};
export const Destructive: Story = {
args: {
variant: "destructive",
children: "Delete",
},
};
export const Outline: Story = {
args: {
variant: "outline",
children: "Outline",
},
};
export const Small: Story = {
args: {
size: "sm",
children: "Small Button",
},
};
export const Large: Story = {
args: {
size: "lg",
children: "Large Button",
},
};
export const WithIcon: Story = {
args: {
children: (
<>
<span className="mr-2">📥</span>
Download
</>
),
},
};
export const Disabled: Story = {
args: {
children: "Disabled Button",
disabled: true,
},
};
Complex Story with Interactions
import type { Meta, StoryObj } from "@storybook/react";
import { within, userEvent, expect } from "@storybook/test";
import { Card, CardHeader, CardTitle, CardContent } from "./card";
const meta = {
title: "Components/Card",
component: Card,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
} satisfies Meta<typeof Card>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
render: () => (
<Card className="w-[350px]">
<CardHeader>
<CardTitle>Card Title</CardTitle>
</CardHeader>
<CardContent>
<p>Card content goes here.</p>
</CardContent>
</Card>
),
};
export const WithInteraction: Story = {
render: () => (
<Card className="w-[350px]">
<CardHeader>
<CardTitle>Interactive Card</CardTitle>
</CardHeader>
<CardContent>
<button data-testid="card-button">Click me</button>
</CardContent>
</Card>
),
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByTestId("card-button");
await userEvent.click(button);
await expect(button).toBeInTheDocument();
},
};
TypeDoc for API Documentation
Setup TypeDoc
cd packages/ui
pnpm add -D typedoc
Add to package.json:
{
"scripts": {
"docs:generate": "typedoc --out docs/api src/index.ts"
}
}
TypeDoc Configuration
// packages/ui/typedoc.json
{
"entryPoints": ["src/index.ts"],
"out": "docs/api",
"exclude": ["**/*.test.ts", "**/*.stories.tsx"],
"excludePrivate": true,
"excludeProtected": true,
"plugin": ["typedoc-plugin-markdown"],
"readme": "README.md",
"theme": "default"
}
Generate documentation:
pnpm docs:generate
README Documentation
Package README Template
# @sgcarstrends/ui
Shared UI component library for SG Cars Trends platform.
## Installation
\`\`\`bash
pnpm add @sgcarstrends/ui
\`\`\`
## Usage
\`\`\`tsx
import { Button, Card, Dialog } from "@sgcarstrends/ui";
export function Example() {
return (
<Card>
<Button>Click me</Button>
</Card>
);
}
\`\`\`
## Components
### Core Components
- [Button](docs/components/button.md) - Customizable button with variants
- [Card](docs/components/card.md) - Container for content
- [Dialog](docs/components/dialog.md) - Modal dialog
- [Badge](docs/components/badge.md) - Status badges
### Form Components
- [Input](docs/components/input.md) - Text input
- [Textarea](docs/components/textarea.md) - Multi-line text input
- [Select](docs/components/select.md) - Dropdown select
- [Checkbox](docs/components/checkbox.md) - Checkbox input
### Layout Components
- [Separator](docs/components/separator.md) - Visual divider
- [Tabs](docs/components/tabs.md) - Tabbed interface
## Styling
Components use Tailwind CSS and CSS variables for theming.
### Dark Mode
Dark mode is supported via CSS variables defined in \`globals.css\`.
### Customization
Customize component styles:
\`\`\`tsx
<Button className="custom-classes">Custom Button</Button>
\`\`\`
## Development
\`\`\`bash
# Install dependencies
pnpm install
# Run tests
pnpm test
# Run Storybook
pnpm storybook
# Build package
pnpm build
\`\`\`
## Contributing
See [CONTRIBUTING.md](../../CONTRIBUTING.md)
## License
MIT
Props Table Generator
Create a script to generate props tables from TypeScript:
// scripts/generate-props-table.ts
import * as ts from "typescript";
import * as fs from "fs";
function generatePropsTable(componentPath: string) {
const program = ts.createProgram([componentPath], {});
const sourceFile = program.getSourceFile(componentPath);
if (!sourceFile) return;
const checker = program.getTypeChecker();
ts.forEachChild(sourceFile, (node) => {
if (ts.isInterfaceDeclaration(node) && node.name.text.endsWith("Props")) {
console.log(`| Prop | Type | Default | Description |`);
console.log(`|------|------|---------|-------------|`);
node.members.forEach((member) => {
if (ts.isPropertySignature(member) && member.name) {
const name = member.name.getText(sourceFile);
const type = checker.typeToString(
checker.getTypeAtLocation(member)
);
const docs = ts.displayPartsToString(
member.symbol?.getDocumentationComment(checker)
);
console.log(`| ${name} | \`${type}\` | - | ${docs} |`);
}
});
}
});
}
// Usage
generatePropsTable("src/components/button.tsx");
Component Checklist
When documenting a component, ensure:
- Component file has JSDoc comments
- Props interface is fully documented
- Markdown documentation file exists
- Usage examples provided
- Props table included
- Variants documented
- Accessibility notes added
- Related components linked
- Storybook stories created (if using Storybook)
- Exported from package index
- Added to README component list
Documentation Patterns
Code Examples
Always provide working code examples:
## Example: Search Form
\`\`\`tsx
"use client";
import { useState } from "react";
import { Input, Button } from "@sgcarstrends/ui";
export function SearchForm() {
const [query, setQuery] = useState("");
return (
<form className="flex gap-2">
<Input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
<Button type="submit">Search</Button>
</form>
);
}
\`\`\`
Do's and Don'ts
## Best Practices
### ✅ Do
- Use semantic HTML elements
- Provide accessible labels
- Test keyboard navigation
- Support dark mode
### ❌ Don't
- Nest buttons inside buttons
- Forget hover states
- Override core styles directly
- Skip accessibility attributes
Automated Documentation
Using react-docgen
pnpm add -D react-docgen-typescript
// scripts/generate-docs.ts
import { parse } from "react-docgen-typescript";
const options = {
savePropValueAsString: true,
shouldExtractLiteralValuesFromEnum: true,
};
const docs = parse("src/components/button.tsx", options);
console.log(JSON.stringify(docs, null, 2));
Testing Documentation
Ensure examples work:
// __tests__/docs/examples.test.tsx
import { render } from "@testing-library/react";
// Test all documented examples
describe("Documentation Examples", () => {
it("renders basic button example", () => {
const { container } = render(<Button>Click me</Button>);
expect(container.querySelector("button")).toBeInTheDocument();
});
it("renders button with icon example", () => {
const { container } = render(
<Button>
<span className="mr-2">📥</span>
Download
</Button>
);
expect(container.textContent).toContain("Download");
});
});
References
- Related files:
packages/ui/docs/- Component documentationpackages/ui/README.md- Package overviewpackages/ui/src/components/*.stories.tsx- Storybook storiespackages/ui/CLAUDE.md- UI package documentation
Best Practices
- Clear Examples: Provide complete, working code examples
- Props Documentation: Document every prop with type and description
- Accessibility: Include accessibility notes
- Visual Examples: Use Storybook for interactive demos
- Keep Updated: Update docs when components change
- Search Friendly: Use clear headings and keywords
- Code Quality: Test documented examples
- Version History: Track changes in documentation