toddmoy avatar

component-demo

Create demonstration pages for components and hooks.

提供方 toddmoy|开源

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:

  1. Yes, create demo page - Create both the component and a showcase demo
  2. Yes, standalone demo - Create component and a standalone demo page at /[name]-demo
  3. Yes, playground - Create an interactive playground with props controls (Storybook-like)
  4. 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
  • Do NOT use dark mode — this project is light-mode only

Phase 3: Demo Page Creation (if requested)

A. For Component Showcase (/components/[name])

  1. 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
  2. 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]',
    }
    
  3. 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)

  1. 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
  2. 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.

  1. Analyze Component Props:

    • Read the target component's source file
    • Parse the TypeScript Props interface
    • Categorize props as controllable or complex
  2. 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: function
    • asChild: boolean (affects render behavior)
    • ref: React ref

    Which props would you like to expose in the playground?"

  3. 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>
      )
    }
    
  4. 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',
    }
    
  5. 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 PropSchema controls:

TypeScript TypePropSchema TypeNotes
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
() => voidSkipNot controllable
Complex objectsSkipNot controllable
React.RefSkipNot 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:

  1. Search for Demo Pages:

    • Check src/components/DemoShowcase/ for [Name]Content.tsx or [Name]PlaygroundContent.tsx
    • Check src/pages/ for [Name]Demo.tsx
    • Check demoRegistry.tsx for entries (including type: 'playground')
  2. If Demo Pages Exist:

    • Update them to reflect new functionality
    • Add examples demonstrating new features
    • Update code examples
    • Test that demos still work correctly
  3. 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">
        Configuration details
      </span>
    </div>
    {/* Demo content */}
  </Card>

  {/* More examples... */}

  {/* Code Example */}
  <Card className="p-6 space-y-3 bg-zinc-50">
    <Badge variant="outline">Usage Example</Badge>
    <pre className="text-xs text-zinc-700 overflow-x-auto">
      <code>{`// Code example here`}</code>
    </pre>
  </Card>
</div>

Example Categories

Include diverse examples:

  1. Basic - Simple default usage
  2. Advanced - Complex configurations
  3. Interactive - User-controlled examples
  4. Variants - Different visual styles or behaviors
  5. Edge Cases - Boundary conditions
  6. Code Examples - Implementation snippets

Styling

  • Use Card components for grouping examples
  • Add Badge components for labeling examples
  • Include configuration details in muted text
  • Light-mode only — do not use dark: variants
  • 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)
  • Verify light-mode-only styling (no dark: variants)
  • Verify code examples are accurate
  • Test on different screen sizes
  • Check TypeScript compilation
  • Verify navigation active state

Additional Checks for Playgrounds

  • Verify the props panel appears
  • 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

  1. Always ask - Don't assume whether demos are wanted
  2. Always search - Check for existing demos before updating
  3. Keep demos updated - When components change, demos should reflect it
  4. Provide variety - Show multiple use cases, not just basic usage
  5. Make it interactive - Let users experiment with controls
  6. Include code - Show implementation examples
  7. Light-mode only - Do not use dark: variants
  8. Organize clearly - Use consistent structure across demos