Scaffold interactive React demo components for AI design patterns with design system integration, starter tests, and documentation
This skill speeds up pattern completion by generating interactive demo components that showcase each AI design pattern in action, complete with tests and documentation.
Claude will automatically invoke this skill when:
For each pattern, this skill generates:
Pattern Demo Output
├── Interactive Component
│ └── src/components/examples/[PatternName]Example.tsx
├── Component Tests
│ └── src/components/examples/__tests__/[PatternName]Example.test.tsx
├── Documentation
│ └── Comments in component explaining pattern
└── Integration
└── Auto-linked in pattern page at /patterns/[pattern-slug]
These have demos you can reference:
Select from the 12 patterns requiring demos:
Review a similar completed demo to understand the structure:
# Look at Contextual Assistance demo (reference)
cat src/components/examples/ContextualAssistanceExample.tsx
# Check its test
cat src/components/examples/__tests__/ContextualAssistanceExample.test.tsx
Common Demo Patterns:
Use this template as starting point:
// src/components/examples/[PatternName]Example.tsx
'use client'
import React, { useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { Button } from '@/components/ui/Button'
import { Card } from '@/components/ui/Card'
/**
* [PatternName] Example Component
*
* This component demonstrates the [PatternName] AI design pattern.
*
* Pattern Description:
* [2-3 sentence explanation of what this pattern does]
*
* Key Features:
* - Feature 1
* - Feature 2
* - Feature 3
*/
export const [PatternName]Example: React.FC = () => {
const [state, setState] = useState('initial')
const [isLoading, setIsLoading] = useState(false)
const handleInteraction = async () => {
setIsLoading(true)
// Simulate AI processing
await new Promise(resolve => setTimeout(resolve, 1000))
setState('updated')
setIsLoading(false)
}
return (
<div
data-testid="[pattern-slug]-demo"
className="space-y-6 p-6"
>
{/* Pattern Title & Description */}
<div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">
Interactive Demo
</h3>
<p className="mt-2 text-sm text-gray-600 dark:text-gray-400">
[Description of what user can interact with]
</p>
</div>
{/* Interactive Content */}
<Card className="p-6">
<AnimatePresence mode="wait">
{state === 'initial' && (
<motion.div
key="initial"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="space-y-4"
>
{/* Initial state content */}
<p className="text-gray-700 dark:text-gray-300">
[Initial state description]
</p>
<Button onClick={handleInteraction} loading={isLoading}>
[Action to trigger pattern]
</Button>
</motion.div>
)}
{state === 'updated' && (
<motion.div
key="updated"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="space-y-4"
>
{/* Updated state showing pattern in action */}
<p className="text-gray-700 dark:text-gray-300">
[Updated state - pattern effect visible]
</p>
<Button variant="outline" onClick={() => setState('initial')}>
Reset
</Button>
</motion.div>
)}
</AnimatePresence>
</Card>
{/* Pattern Benefits */}
<div className="space-y-3">
<h4 className="text-sm font-semibold text-gray-900 dark:text-white">
Pattern Benefits:
</h4>
<ul className="space-y-2">
<li className="flex gap-3">
<span className="text-green-600 dark:text-green-400">✓</span>
<span className="text-sm text-gray-700 dark:text-gray-300">
Benefit 1
</span>
</li>
<li className="flex gap-3">
<span className="text-green-600 dark:text-green-400">✓</span>
<span className="text-sm text-gray-700 dark:text-gray-300">
Benefit 2
</span>
</li>
</ul>
</div>
</div>
)
}
export default [PatternName]Example
Your demo should:
✅ Show Pattern in Action
✅ Include Interactive Controls
✅ Demonstrate Benefits
✅ Handle Edge Cases
✅ Use Design System
@/components/ui/dark: prefixMost demos use animations to show the pattern:
// State transitions with AnimatePresence
<AnimatePresence mode="wait">
{isExpanded && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.3 }}
>
Content
</motion.div>
)}
</AnimatePresence>
// Hover effects
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={handleClick}
>
Click me
</motion.button>
// Progress indicators
<motion.div
initial={{ width: 0 }}
animate={{ width: '100%' }}
transition={{ duration: 2 }}
className="h-1 bg-blue-500"
/>
// src/components/examples/__tests__/[PatternName]Example.test.tsx
import React from 'react'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { [PatternName]Example } from '../[PatternName]Example'
describe('[PatternName]Example', () => {
it('renders without crashing', () => {
render(<[PatternName]Example />)
expect(screen.getByTestId('[pattern-slug]-demo')).toBeInTheDocument()
})
it('displays initial state', () => {
render(<[PatternName]Example />)
expect(screen.getByText(/[initial state text]/)).toBeInTheDocument()
})
it('responds to user interaction', async () => {
const user = userEvent.setup()
render(<[PatternName]Example />)
const button = screen.getByRole('button', { name: /[action text]/ })
await user.click(button)
await waitFor(() => {
expect(screen.getByText(/[updated state text]/)).toBeInTheDocument()
})
})
it('shows loading state during interaction', async () => {
const user = userEvent.setup()
render(<[PatternName]Example />)
const button = screen.getByRole('button', { name: /[action text]/ })
await user.click(button)
// Loading state visible briefly
expect(screen.getByRole('button')).toHaveAttribute('disabled')
})
it('matches snapshot', () => {
const { container } = render(<[PatternName]Example />)
expect(container).toMatchSnapshot()
})
it('resets to initial state', async () => {
const user = userEvent.setup()
render(<[PatternName]Example />)
// Trigger update
await user.click(screen.getByRole('button', { name: /[action text]/ }))
// Click reset
await user.click(screen.getByRole('button', { name: /Reset/ }))
expect(screen.getByText(/[initial state text]/)).toBeInTheDocument()
})
it('is accessible', () => {
render(<[PatternName]Example />)
const button = screen.getByRole('button')
expect(button).toHaveAccessibleName()
})
})
# Run tests for your demo
npm test -- --testPathPattern="[PatternName]Example"
# Check coverage
npm run test:coverage -- --testPathPattern="[PatternName]Example"
# Watch mode for development
npm run test:watch -- [PatternName]Example
Add demo component to pattern definition:
// src/data/patterns/patterns/[pattern-slug]/index.ts
import { [PatternName]Example } from '@/components/examples/[PatternName]Example'
export const [patternSlug]Pattern = {
// ... other pattern data
demo: {
component: [PatternName]Example,
description: 'Interactive demonstration of [pattern name]',
}
}
npm run dev
# Visit http://localhost:3000/patterns/[pattern-slug]
# Test the interactive demo
# Check on mobile (responsive design)
# Test dark mode toggle
dark: Tailwind utilitiesnpm run generate-missing-demos
This command:
# Start dev server
npm run dev
# Run component tests
npm test -- --testPathPattern="Example"
# Run specific demo tests
npm test -- [PatternName]Example.test.tsx
# Test coverage for demos
npm run test:coverage -- --testPathPattern="examples"
# Generate missing demos
npm run generate-missing-demos
When completed, demos appear on pattern pages:
/patterns/[pattern-slug]
├── Pattern Title & Description
├── Interactive Demo Component ← Generated here
├── Code Examples
├── Guidelines
├── Considerations
└── Benefits
When using Pattern Development skill:
Goal: Create engaging, interactive demos that showcase each AI design pattern in action, making it clear how the pattern improves user experience and AI interactions.