Component Demo Skill
This skill guides the creation and maintenance of components and their demo pages in the Protobox component showcase.
When to Use This Skill
Use this skill when:
- Creating new custom components in
src/components/ - Creating new custom hooks in
src/hooks/ - Updating existing components or hooks
- Planning feature additions that involve reusable components
Component Development Workflow
Phase 1: Planning - Ask About Demo Pages
IMPORTANT: When planning to create a new component or hook, ALWAYS ask the user:
"Would you like me to create a demo page for this [component/hook] in the component showcase?"
Options to present:
- Yes, create demo page - Create both the component and a showcase demo
- Yes, standalone demo - Create component and a standalone demo page at
/[name]-demo - Yes, playground - Create an interactive playground with props controls (Storybook-like)
- No demo needed - Just create the component/hook
Phase 2: Component Creation
Create the component in the appropriate location:
- Custom components:
src/components/[ComponentName].tsx - Custom hooks:
src/hooks/[hookName].tsx - UI components: Use
npx shadcn-ui@latest add [component]for shadcn components
Follow component best practices from src/components/README.md:
- Simple default usage with sensible defaults
- Accept
style,className, and native element props - Use composition for icons (via props, not hardcoded)
- Standard heights: 16px, 20px, 40px
- Support dark mode with Tailwind
dark:variants
Phase 3: Demo Page Creation (if requested)
A. For Component Showcase (/components/[name])
-
Create Content Component in
src/components/DemoShowcase/[Name]Content.tsx:- Extract core demo functionality without page wrappers
- Remove:
min-h-screen, full-page padding, headers, back links - Keep: interactive elements, controls, examples
- Use Card components to organize examples
- Include usage code examples
-
Register in Demo Registry (
src/components/DemoShowcase/demoRegistry.tsx):// Add lazy import const [Name]Content = lazy(() => import('./[Name]Content')) // Add to DEMO_REGISTRY array { id: '[name]', label: '[Display Name]', section: 'Hooks' | 'Components', component: [Name]Content, description: 'Brief description', path: '/components/[name]', } -
Add Route in
src/App.tsx:// Add lazy import at top const [Name]Content = lazy(() => import('./components/DemoShowcase/[Name]Content')) // Add route inside <Route path="/components"> nested routes <Route path="[name]" element={ <Suspense fallback={<div>Loading...</div>}> <[Name]Content /> </Suspense> } />
B. For Standalone Demo Page (/[name]-demo)
-
Create Page Component in
src/pages/[Name]Demo.tsx:- Include full-page wrapper with proper styling
- Add header with title and description
- Organize examples using Card components
- Include code examples showing usage
- Add back link to home
-
Add Route in
src/App.tsx:import [Name]Demo from './pages/[Name]Demo' <Route path="/[name]-demo" Component={[Name]Demo} />
C. For Component Playground (/components/[name]-playground)
Playgrounds provide an interactive, Storybook-like environment with a canvas and props controls.
-
Analyze Component Props:
- Read the target component's source file
- Parse the TypeScript Props interface
- Categorize props as controllable or complex
-
Guide Prop Selection - Present to User:
"I analyzed [ComponentName] and found these props:
Recommended for playground (controllable):
variant: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'size: 'default' | 'sm' | 'lg' | 'icon'disabled: boolean
Complex props (not controllable):
onClick: functionasChild: boolean (affects render behavior)ref: React ref
Which props would you like to expose in the playground?"
-
Create Playground Content in
src/components/DemoShowcase/[Name]PlaygroundContent.tsx:import PlaygroundCanvas, { PropSchema } from './PlaygroundCanvas' import { Button } from '@/components/ui/button' const propSchema: PropSchema = { variant: { type: 'select', options: ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'], defaultValue: 'default', }, size: { type: 'select', options: ['default', 'sm', 'lg', 'icon'], defaultValue: 'default', }, disabled: { type: 'boolean', defaultValue: false, }, children: { type: 'string', defaultValue: 'Button', }, } export default function ButtonPlaygroundContent() { return ( <PlaygroundCanvas componentName="Button" propSchema={propSchema} defaultProps={{ children: 'Click Me' }} > {(props) => <Button {...props}>{props.children as string}</Button>} </PlaygroundCanvas> ) } -
Register in Demo Registry (
src/components/DemoShowcase/demoRegistry.tsx):// Add lazy import const [Name]PlaygroundContent = lazy(() => import('./[Name]PlaygroundContent')) // Add to DEMO_REGISTRY array { id: '[name]-playground', label: '[Name] Playground', section: 'Components', component: [Name]PlaygroundContent, description: 'Interactive playground for [Name]', path: '/components/[name]-playground', type: 'playground', } -
Add Route in
src/App.tsx:const [Name]PlaygroundContent = lazy(() => import('./components/DemoShowcase/[Name]PlaygroundContent')) <Route path="[name]-playground" element={ <Suspense fallback={<div>Loading...</div>}> <[Name]PlaygroundContent /> </Suspense> } />
Prop Type Mapping Reference
When analyzing TypeScript props, map types to Leva controls:
| TypeScript Type | PropSchema Type | Notes |
|-----------------|-----------------|-------|
| string | 'string' | Text input |
| number | 'number' | Slider (add min/max/step) |
| boolean | 'boolean' | Checkbox toggle |
| 'a' \| 'b' \| 'c' | 'select' | Dropdown with options array |
| enum Foo { A, B } | 'select' | Extract enum values as options |
| React.ReactNode | 'string' | For simple text children |
| Color strings | 'color' | Color picker |
| () => void | Skip | Not controllable |
| Complex objects | Skip | Not controllable |
| React.Ref | Skip | Not controllable |
PropSchema Format
import { PropSchema } from '@/components/DemoShowcase/PlaygroundCanvas'
const schema: PropSchema = {
propName: {
type: 'string' | 'number' | 'boolean' | 'select' | 'color',
label?: string, // Custom label (defaults to propName)
defaultValue?: unknown, // Initial value
options?: string[], // For 'select' type
min?: number, // For 'number' type
max?: number, // For 'number' type
step?: number, // For 'number' type
}
}
Phase 4: Component Updates - Check for Existing Demos
CRITICAL: When updating an existing component or hook, ALWAYS check if demo pages exist:
-
Search for Demo Pages:
- Check
src/components/DemoShowcase/for[Name]Content.tsxor[Name]PlaygroundContent.tsx - Check
src/pages/for[Name]Demo.tsx - Check
demoRegistry.tsxfor entries (includingtype: 'playground')
- Check
-
If Demo Pages Exist:
- Update them to reflect new functionality
- Add examples demonstrating new features
- Update code examples
- Test that demos still work correctly
-
Prompt User:
"I found existing demo pages for this [component/hook]. I'll update them to include the new [feature/changes]."
Demo Content Best Practices
Structure
Organize demo content with clear sections:
<div className="space-y-8">
{/* Example 1: Basic Usage */}
<Card className="p-6 space-y-3">
<div className="flex items-center gap-2">
<Badge variant="secondary">Basic</Badge>
<span className="text-xs text-zinc-500 dark:text-zinc-400">
Configuration details
</span>
</div>
{/* Demo content */}
</Card>
{/* More examples... */}
{/* Code Example */}
<Card className="p-6 space-y-3 bg-zinc-50 dark:bg-zinc-900">
<Badge variant="outline">Usage Example</Badge>
<pre className="text-xs text-zinc-700 dark:text-zinc-300 overflow-x-auto">
<code>{`// Code example here`}</code>
</pre>
</Card>
</div>
Example Categories
Include diverse examples:
- Basic - Simple default usage
- Advanced - Complex configurations
- Interactive - User-controlled examples
- Variants - Different visual styles or behaviors
- Edge Cases - Boundary conditions
- Code Examples - Implementation snippets
Styling
- Use Card components for grouping examples
- Add Badge components for labeling examples
- Include configuration details in muted text
- Support dark mode throughout
- Add visual indicators for interactive elements
- Show state changes clearly
Component Showcase Navigation
The navigation is automatically generated from demoRegistry.tsx:
- Hooks section: Custom React hooks
- Components section: UI components, libraries, demos
Demos are displayed in the order they appear in DEMO_REGISTRY.
Testing Checklist
After creating or updating component demos:
- [ ] Navigate to the demo route
- [ ] Verify all examples render correctly
- [ ] Test interactive elements (buttons, inputs, controls)
- [ ] Check dark mode appearance
- [ ] Verify code examples are accurate
- [ ] Test on different screen sizes
- [ ] Check TypeScript compilation
- [ ] Verify navigation active state
Additional Checks for Playgrounds
- [ ] Verify Leva props panel appears (top-right corner)
- [ ] Test that prop changes update the component in real-time
- [ ] Verify navigation collapse toggle works
- [ ] Check canvas centering at different sizes
- [ ] Test all prop controls (selects, inputs, toggles)
Examples
Example 1: Creating a New Hook with Demo
User: "Create a useDebounce hook"
Claude:
1. Asks: "Would you like me to create a demo page for this hook in the component showcase?"
2. User selects: "Yes, create demo page"
3. Creates:
- src/hooks/useDebounce.tsx (the hook)
- src/components/DemoShowcase/DebounceContent.tsx (demo)
- Updates demoRegistry.tsx
- Updates App.tsx with route
Example 2: Updating Existing Component
User: "Add a 'size' prop to the Button component"
Claude:
1. Searches for existing demos
2. Finds: N/A (Button is shadcn component, no custom demo)
3. Updates Button usage examples in existing demos if needed
Example 3: Updating Hook with Demo
User: "Add a 'delay' option to useTypewriter"
Claude:
1. Updates src/hooks/useTypewriter.tsx
2. Searches and finds: src/components/DemoShowcase/TypewriterContent.tsx
3. Announces: "I found the existing TypewriterContent demo. I'll add an example showing the new delay option."
4. Adds new Card with delay example
5. Updates code example to include delay option
Example 4: Creating a Component Playground
User: "Create a playground for the Badge component"
Claude:
1. Asks: "Would you like me to create a demo page for this component?"
2. User selects: "Yes, playground"
3. Claude reads src/components/ui/badge.tsx
4. Analyzes BadgeProps interface and presents:
"I found these props:
**Controllable:**
- variant: 'default' | 'secondary' | 'destructive' | 'outline'
- children: ReactNode (as string)
**Not controllable:**
- className: styling (skip)
Which props would you like to expose?"
5. User selects: "variant and children"
6. Creates:
- src/components/DemoShowcase/BadgePlaygroundContent.tsx
- Updates demoRegistry.tsx with type: 'playground'
- Updates App.tsx with route
File Locations Reference
- Component showcase layout:
src/pages/ComponentShowcase.tsx - Demo registry:
src/components/DemoShowcase/demoRegistry.tsx - Navigation:
src/components/DemoShowcase/DemoNavigation.tsx - Demo content:
src/components/DemoShowcase/[Name]Content.tsx - Playground content:
src/components/DemoShowcase/[Name]PlaygroundContent.tsx - Playground canvas:
src/components/DemoShowcase/PlaygroundCanvas.tsx - Standalone demos:
src/pages/[Name]Demo.tsx - Routes:
src/App.tsx
Key Principles
- Always ask - Don't assume whether demos are wanted
- Always search - Check for existing demos before updating
- Keep demos updated - When components change, demos should reflect it
- Provide variety - Show multiple use cases, not just basic usage
- Make it interactive - Let users experiment with controls
- Include code - Show implementation examples
- Support dark mode - Test in both themes
- Organize clearly - Use consistent structure across demos
Scan to join WeChat group