Initial commit
This commit is contained in:
611
skills/nextjs-react-implementation/SKILL.md
Normal file
611
skills/nextjs-react-implementation/SKILL.md
Normal file
@@ -0,0 +1,611 @@
|
||||
---
|
||||
name: nextjs-react-implementation
|
||||
description: Generate production-ready Next.js/React components from design specifications. Use this skill when you have a design specification document and need to create React/TypeScript components with Tailwind CSS styling, Vitest tests, and Storybook documentation.
|
||||
---
|
||||
|
||||
# Next.js React Implementation Generator
|
||||
|
||||
## Overview
|
||||
|
||||
Transform design specification documents into production-ready Next.js/React components with complete TypeScript types, Tailwind CSS styling, comprehensive tests, and Storybook documentation. This skill focuses on accurate implementation following modern React patterns and best practices.
|
||||
|
||||
**Use this skill when:**
|
||||
- Implementing components from design specifications
|
||||
- Creating React components with TypeScript
|
||||
- Generating Tailwind CSS styled components
|
||||
- Writing Vitest browser mode tests
|
||||
- Creating Storybook documentation
|
||||
- Building component libraries
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This skill expects a design specification document created by the `figma-design-analyzer` skill or a similar structured specification. The specification should include:
|
||||
|
||||
- Component name and atomic category
|
||||
- Semantic HTML element
|
||||
- Layout and spacing details
|
||||
- Typography specifications
|
||||
- Color and visual properties
|
||||
- Variants and states
|
||||
- Responsive behavior
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### Step 1: Parse Design Specification
|
||||
|
||||
Read and understand the design specification document:
|
||||
|
||||
1. **Load the specification file:**
|
||||
- Read the `.design.md` file
|
||||
- Parse front matter metadata
|
||||
- Extract all design properties
|
||||
|
||||
2. **Identify key information:**
|
||||
- Component name (PascalCase)
|
||||
- Atomic category (Atom/Molecule/Organism)
|
||||
- Semantic root element
|
||||
- Variants and their options
|
||||
- States (hover, active, disabled, etc.)
|
||||
|
||||
3. **Validate specification:**
|
||||
- Ensure all required fields are present
|
||||
- Check for contradictions or ambiguities
|
||||
- Note any missing information
|
||||
|
||||
### Step 2: Determine Component Structure
|
||||
|
||||
Plan the component implementation:
|
||||
|
||||
1. **Determine component directory:**
|
||||
```
|
||||
src/components/
|
||||
atoms/ComponentName/
|
||||
molecules/ComponentName/
|
||||
organisms/ComponentName/
|
||||
```
|
||||
|
||||
2. **Plan file structure:**
|
||||
```
|
||||
ComponentName/
|
||||
├── index.ts # Barrel export
|
||||
├── ComponentName.tsx # Component implementation
|
||||
├── ComponentName.test.tsx # Vitest tests
|
||||
└── ComponentName.stories.tsx # Storybook stories
|
||||
```
|
||||
|
||||
3. **Identify dependencies:**
|
||||
- Child components (for Molecules/Organisms)
|
||||
- Icons or assets
|
||||
- Utility functions
|
||||
|
||||
### Step 3: Generate TypeScript Props Interface
|
||||
|
||||
Create type-safe props based on the specification:
|
||||
|
||||
1. **Read variant information from spec:**
|
||||
```markdown
|
||||
## Variants
|
||||
- variant: primary | secondary | tertiary
|
||||
- size: small | medium | large
|
||||
```
|
||||
|
||||
2. **Create props interface:**
|
||||
```typescript
|
||||
export interface ButtonProps {
|
||||
/** Button variant style */
|
||||
variant?: 'primary' | 'secondary' | 'tertiary';
|
||||
/** Button size */
|
||||
size?: 'small' | 'medium' | 'large';
|
||||
/** Button content */
|
||||
children: React.ReactNode;
|
||||
/** Click handler */
|
||||
onClick?: () => void;
|
||||
/** Disabled state */
|
||||
disabled?: boolean;
|
||||
/** Additional CSS classes */
|
||||
className?: string;
|
||||
}
|
||||
```
|
||||
|
||||
3. **Set default props:**
|
||||
- Define sensible defaults based on the "Default" state in spec
|
||||
- Use TypeScript default parameters
|
||||
|
||||
### Step 4: Generate Component Implementation
|
||||
|
||||
Use the component template to create the implementation:
|
||||
|
||||
1. **Read the component template:**
|
||||
```
|
||||
assets/templates/component.tsx.template
|
||||
```
|
||||
|
||||
2. **Fill in template placeholders:**
|
||||
- `{{COMPONENT_NAME}}`: Component name in PascalCase
|
||||
- `{{COMPONENT_DESCRIPTION}}`: Brief description from spec
|
||||
- `{{ATOMIC_CATEGORY}}`: Atoms, Molecules, or Organisms
|
||||
- `{{ROOT_ELEMENT}}`: Semantic HTML element
|
||||
- `{{TAILWIND_CLASSES}}`: Base Tailwind classes from spec
|
||||
|
||||
3. **Implement variant logic:**
|
||||
```typescript
|
||||
const variantClasses = {
|
||||
primary: 'bg-blue-500 text-white hover:bg-blue-600',
|
||||
secondary: 'bg-white text-blue-500 border-blue-500 hover:bg-blue-50',
|
||||
tertiary: 'bg-transparent text-blue-500 hover:bg-blue-50'
|
||||
};
|
||||
|
||||
const sizeClasses = {
|
||||
small: 'px-4 py-2 text-sm',
|
||||
medium: 'px-6 py-3 text-base',
|
||||
large: 'px-8 py-4 text-lg'
|
||||
};
|
||||
|
||||
const className = cn(
|
||||
'inline-flex items-center justify-center rounded-lg font-semibold',
|
||||
'transition-colors duration-200',
|
||||
'disabled:opacity-50 disabled:cursor-not-allowed',
|
||||
variantClasses[variant],
|
||||
sizeClasses[size],
|
||||
props.className
|
||||
);
|
||||
```
|
||||
|
||||
4. **Handle responsive behavior:**
|
||||
- Use Tailwind responsive prefixes from spec
|
||||
- Implement mobile-first approach
|
||||
```typescript
|
||||
className = cn(
|
||||
'w-full md:w-auto', // Full width on mobile, auto on tablet+
|
||||
'px-4 md:px-6 lg:px-8', // Responsive padding
|
||||
// ... other classes
|
||||
);
|
||||
```
|
||||
|
||||
5. **Implement accessibility:**
|
||||
- Add ARIA attributes as specified
|
||||
- Ensure keyboard navigation
|
||||
- Include focus states
|
||||
```typescript
|
||||
<button
|
||||
className={className}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
aria-label={ariaLabel}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
```
|
||||
|
||||
### Step 5: Generate Vitest Browser Mode Tests
|
||||
|
||||
Create comprehensive tests using the test template:
|
||||
|
||||
1. **Read the test template:**
|
||||
```
|
||||
assets/templates/test.tsx.template
|
||||
```
|
||||
|
||||
2. **Set up test structure:**
|
||||
```typescript
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { page } from '@vitest/browser/context';
|
||||
import { render } from 'vitest-browser-react';
|
||||
import { Button } from './Button';
|
||||
|
||||
describe('Button', () => {
|
||||
// Test cases here
|
||||
});
|
||||
```
|
||||
|
||||
3. **Follow TDD principles with Arrange-Act-Assert:**
|
||||
```typescript
|
||||
it('正しくレンダリングされること', async () => {
|
||||
// Arrange
|
||||
const props = { children: 'Click Me' };
|
||||
|
||||
// Act
|
||||
await render(<Button {...props} />);
|
||||
|
||||
// Assert
|
||||
const button = page.getByRole('button', { name: 'Click Me' });
|
||||
await expect.element(button).toBeVisible();
|
||||
});
|
||||
```
|
||||
|
||||
4. **Test all variants:**
|
||||
```typescript
|
||||
it.each([
|
||||
{ variant: 'primary', expectedClass: 'bg-blue-500' },
|
||||
{ variant: 'secondary', expectedClass: 'bg-white' },
|
||||
{ variant: 'tertiary', expectedClass: 'bg-transparent' }
|
||||
])('$variant バリアントが正しくレンダリングされること', async ({ variant, expectedClass }) => {
|
||||
// Arrange & Act
|
||||
await render(<Button variant={variant}>Button</Button>);
|
||||
|
||||
// Assert
|
||||
const button = page.getByRole('button');
|
||||
await expect.element(button).toHaveClass(expectedClass);
|
||||
});
|
||||
```
|
||||
|
||||
5. **Test all states:**
|
||||
```typescript
|
||||
it('無効状態が正しく動作すること', async () => {
|
||||
// Arrange
|
||||
const handleClick = vi.fn();
|
||||
await render(
|
||||
<Button disabled onClick={handleClick}>
|
||||
Disabled
|
||||
</Button>
|
||||
);
|
||||
|
||||
// Act
|
||||
const button = page.getByRole('button');
|
||||
await button.click();
|
||||
|
||||
// Assert
|
||||
await expect.element(button).toBeDisabled();
|
||||
expect(handleClick).not.toHaveBeenCalled();
|
||||
});
|
||||
```
|
||||
|
||||
6. **Test interactions:**
|
||||
```typescript
|
||||
it('クリックイベントが動作すること', async () => {
|
||||
// Arrange
|
||||
const handleClick = vi.fn();
|
||||
await render(<Button onClick={handleClick}>Click Me</Button>);
|
||||
|
||||
// Act
|
||||
const button = page.getByRole('button');
|
||||
await button.click();
|
||||
|
||||
// Assert
|
||||
expect(handleClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
```
|
||||
|
||||
7. **Test accessibility:**
|
||||
```typescript
|
||||
it('キーボード操作が動作すること', async () => {
|
||||
// Arrange
|
||||
const handleClick = vi.fn();
|
||||
await render(<Button onClick={handleClick}>Press Enter</Button>);
|
||||
|
||||
// Act
|
||||
const button = page.getByRole('button');
|
||||
await button.focus();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// Assert
|
||||
expect(handleClick).toHaveBeenCalled();
|
||||
});
|
||||
```
|
||||
|
||||
### Step 6: Generate Storybook Stories
|
||||
|
||||
Create interactive documentation:
|
||||
|
||||
1. **Read the Storybook template:**
|
||||
```
|
||||
assets/templates/storybook.tsx.template
|
||||
```
|
||||
|
||||
2. **Set up story structure:**
|
||||
```typescript
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Button } from './Button';
|
||||
|
||||
const meta = {
|
||||
title: 'Atoms/Button',
|
||||
component: Button,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: ['primary', 'secondary', 'tertiary']
|
||||
},
|
||||
size: {
|
||||
control: 'select',
|
||||
options: ['small', 'medium', 'large']
|
||||
}
|
||||
}
|
||||
} satisfies Meta<typeof Button>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
```
|
||||
|
||||
3. **Create stories for each variant:**
|
||||
```typescript
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
variant: 'primary',
|
||||
children: 'Primary Button'
|
||||
}
|
||||
};
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
variant: 'secondary',
|
||||
children: 'Secondary Button'
|
||||
}
|
||||
};
|
||||
|
||||
export const Tertiary: Story = {
|
||||
args: {
|
||||
variant: 'tertiary',
|
||||
children: 'Tertiary Button'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
4. **Create stories for different sizes:**
|
||||
```typescript
|
||||
export const Small: Story = {
|
||||
args: {
|
||||
size: 'small',
|
||||
children: 'Small Button'
|
||||
}
|
||||
};
|
||||
|
||||
export const Medium: Story = {
|
||||
args: {
|
||||
size: 'medium',
|
||||
children: 'Medium Button'
|
||||
}
|
||||
};
|
||||
|
||||
export const Large: Story = {
|
||||
args: {
|
||||
size: 'large',
|
||||
children: 'Large Button'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
5. **Create stories for states:**
|
||||
```typescript
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
disabled: true,
|
||||
children: 'Disabled Button'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
6. **Create interactive playground:**
|
||||
```typescript
|
||||
export const Playground: Story = {
|
||||
args: {
|
||||
variant: 'primary',
|
||||
size: 'medium',
|
||||
children: 'Playground Button'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Step 7: Handle Complex Components
|
||||
|
||||
For Molecules and Organisms that use child components:
|
||||
|
||||
1. **Identify child components:**
|
||||
- Read component hierarchy from spec
|
||||
- Import child components
|
||||
|
||||
2. **Compose the component:**
|
||||
```typescript
|
||||
import { Button } from '@/components/atoms/Button';
|
||||
import { Input } from '@/components/atoms/Input';
|
||||
|
||||
export function SearchBar({ onSearch }: SearchBarProps) {
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<Input placeholder="Search..." />
|
||||
<Button onClick={onSearch}>Search</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
3. **Pass props to children:**
|
||||
- Map parent props to child props
|
||||
- Handle event delegation
|
||||
|
||||
### Step 8: Implement Responsive Behavior
|
||||
|
||||
Add responsive styles based on the specification:
|
||||
|
||||
1. **Read responsive behavior from spec:**
|
||||
```markdown
|
||||
## Responsive Behavior
|
||||
- Mobile (default): flex-col gap-4 text-sm
|
||||
- Tablet (md): md:flex-row md:gap-6 md:text-base
|
||||
- Desktop (lg): lg:gap-8 lg:text-lg
|
||||
```
|
||||
|
||||
2. **Apply Tailwind responsive prefixes:**
|
||||
```typescript
|
||||
className = cn(
|
||||
'flex flex-col gap-4 text-sm', // Mobile (default)
|
||||
'md:flex-row md:gap-6 md:text-base', // Tablet
|
||||
'lg:gap-8 lg:text-lg', // Desktop
|
||||
// ... other classes
|
||||
);
|
||||
```
|
||||
|
||||
3. **Test responsive behavior:**
|
||||
- Test at different viewport sizes
|
||||
- Verify layout changes
|
||||
|
||||
### Step 9: Add Barrel Export
|
||||
|
||||
Create index.ts for clean imports:
|
||||
|
||||
```typescript
|
||||
export { Button } from './Button';
|
||||
export type { ButtonProps } from './Button';
|
||||
```
|
||||
|
||||
### Step 10: Verify Implementation
|
||||
|
||||
Final checks before completion:
|
||||
|
||||
1. **Verify against specification:**
|
||||
- All variants implemented
|
||||
- All states handled
|
||||
- Responsive behavior correct
|
||||
- Accessibility requirements met
|
||||
|
||||
2. **Run tests:**
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
3. **Run Storybook:**
|
||||
```bash
|
||||
npm run storybook
|
||||
```
|
||||
|
||||
4. **Type check:**
|
||||
```bash
|
||||
npm run type-check
|
||||
```
|
||||
|
||||
5. **Lint:**
|
||||
```bash
|
||||
npm run lint
|
||||
```
|
||||
|
||||
## Using Context7 for Latest Documentation
|
||||
|
||||
Always use Context7 MCP to get up-to-date documentation:
|
||||
|
||||
1. **Resolve library IDs:**
|
||||
```
|
||||
mcp__context7__resolve-library-id(libraryName: "react")
|
||||
mcp__context7__resolve-library-id(libraryName: "tailwindcss")
|
||||
mcp__context7__resolve-library-id(libraryName: "vitest")
|
||||
```
|
||||
|
||||
2. **Get library documentation:**
|
||||
```
|
||||
mcp__context7__get-library-docs(
|
||||
context7CompatibleLibraryID: "/facebook/react",
|
||||
topic: "hooks"
|
||||
)
|
||||
```
|
||||
|
||||
3. **Check project versions:**
|
||||
- Read package.json to determine versions
|
||||
- Use exact version for Context7 if available
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Type Safety:**
|
||||
- Use strict TypeScript
|
||||
- Define all props with proper types
|
||||
- Avoid `any` type
|
||||
|
||||
2. **Component Composition:**
|
||||
- Keep components focused and single-purpose
|
||||
- Use composition over inheritance
|
||||
- Extract reusable logic into hooks
|
||||
|
||||
3. **Styling:**
|
||||
- Use Tailwind CSS utility classes
|
||||
- Follow project's Tailwind configuration
|
||||
- Use `cn()` utility for conditional classes
|
||||
|
||||
4. **Testing:**
|
||||
- Write tests before implementation (TDD)
|
||||
- Test all variants and states
|
||||
- Test user interactions
|
||||
- Test accessibility
|
||||
|
||||
5. **Documentation:**
|
||||
- Add JSDoc comments to props
|
||||
- Create comprehensive Storybook stories
|
||||
- Include usage examples
|
||||
|
||||
6. **Accessibility:**
|
||||
- Use semantic HTML
|
||||
- Add ARIA attributes when needed
|
||||
- Ensure keyboard navigation
|
||||
- Test with screen readers
|
||||
|
||||
7. **Performance:**
|
||||
- Avoid unnecessary re-renders
|
||||
- Use React.memo when appropriate
|
||||
- Optimize large lists with virtualization
|
||||
|
||||
## Resources
|
||||
|
||||
This skill includes templates for code generation:
|
||||
|
||||
### assets/templates/
|
||||
|
||||
- **component.tsx.template**: React/TypeScript component structure
|
||||
- **test.tsx.template**: Vitest browser mode test structure
|
||||
- **storybook.tsx.template**: Storybook story structure
|
||||
|
||||
**Usage:** Use these templates as starting points. Fill in placeholders with values from the design specification.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Ignoring the specification:**
|
||||
- Always implement exactly as specified
|
||||
- Don't make assumptions
|
||||
- Ask for clarification if ambiguous
|
||||
|
||||
2. **Incomplete variant handling:**
|
||||
- Implement all variants from spec
|
||||
- Don't skip edge cases
|
||||
|
||||
3. **Poor test coverage:**
|
||||
- Test all variants
|
||||
- Test all states
|
||||
- Test all interactions
|
||||
|
||||
4. **Accessibility oversights:**
|
||||
- Don't forget ARIA attributes
|
||||
- Ensure keyboard navigation
|
||||
- Test with assistive technologies
|
||||
|
||||
5. **Inconsistent naming:**
|
||||
- Follow project naming conventions
|
||||
- Use consistent prop names
|
||||
- Match Figma component names
|
||||
|
||||
6. **Hardcoded values:**
|
||||
- Use props and configuration
|
||||
- Avoid magic numbers
|
||||
- Use design tokens when available
|
||||
|
||||
## Example Implementation Flow
|
||||
|
||||
**Input:** Button.design.md specification
|
||||
|
||||
**Steps:**
|
||||
1. Parse specification
|
||||
2. Create directory: `src/components/atoms/Button/`
|
||||
3. Generate `Button.tsx` with variants (primary, secondary, tertiary) and sizes (small, medium, large)
|
||||
4. Generate `Button.test.tsx` with tests for all variants, sizes, and states
|
||||
5. Generate `Button.stories.tsx` with interactive stories
|
||||
6. Create `index.ts` barrel export
|
||||
7. Verify implementation against specification
|
||||
8. Run tests and Storybook
|
||||
|
||||
**Output:**
|
||||
- Production-ready Button component
|
||||
- Comprehensive test suite
|
||||
- Interactive Storybook documentation
|
||||
|
||||
---
|
||||
|
||||
**This skill transforms design specifications into production-ready Next.js/React components with complete testing and documentation.**
|
||||
Reference in New Issue
Block a user