Initial commit
This commit is contained in:
510
commands/component-generate.md
Normal file
510
commands/component-generate.md
Normal file
@@ -0,0 +1,510 @@
|
||||
# /specweave-frontend:component-generate
|
||||
|
||||
Generate React/Vue/Angular components with tests, stories, and documentation following Atomic Design principles.
|
||||
|
||||
You are an expert frontend developer who creates well-structured, tested, and documented components.
|
||||
|
||||
## Your Task
|
||||
|
||||
Generate production-ready components with complete test coverage, Storybook stories, and documentation.
|
||||
|
||||
### 1. Component Types
|
||||
|
||||
**Atomic Design Levels**:
|
||||
- **Atoms**: Basic UI elements (Button, Input, Icon, Text, Badge)
|
||||
- **Molecules**: Simple component combinations (FormField, SearchBar, Card)
|
||||
- **Organisms**: Complex components (Header, Form, DataTable, Modal)
|
||||
- **Templates**: Page layouts (DashboardLayout, AuthLayout)
|
||||
|
||||
### 2. React Component Template
|
||||
|
||||
**TypeScript with Props Interface**:
|
||||
```typescript
|
||||
import React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface CardProps {
|
||||
/**
|
||||
* Card title
|
||||
*/
|
||||
title?: string;
|
||||
/**
|
||||
* Card content
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
/**
|
||||
* Card variant style
|
||||
*/
|
||||
variant?: 'default' | 'outlined' | 'elevated';
|
||||
/**
|
||||
* Optional footer content
|
||||
*/
|
||||
footer?: React.ReactNode;
|
||||
/**
|
||||
* Additional CSS classes
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* Click handler
|
||||
*/
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
||||
({ title, children, variant = 'default', footer, className, onClick }, ref) => {
|
||||
const baseStyles = 'rounded-lg p-6 transition-all';
|
||||
|
||||
const variants = {
|
||||
default: 'bg-white border border-gray-200',
|
||||
outlined: 'bg-transparent border-2 border-gray-300',
|
||||
elevated: 'bg-white shadow-lg hover:shadow-xl',
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
baseStyles,
|
||||
variants[variant],
|
||||
onClick && 'cursor-pointer hover:border-blue-500',
|
||||
className
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{title && (
|
||||
<h3 className="text-lg font-semibold mb-4 text-gray-900">{title}</h3>
|
||||
)}
|
||||
<div className="text-gray-700">{children}</div>
|
||||
{footer && (
|
||||
<div className="mt-4 pt-4 border-t border-gray-200">{footer}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Card.displayName = 'Card';
|
||||
```
|
||||
|
||||
### 3. Component Test Template
|
||||
|
||||
**Comprehensive Testing**:
|
||||
```typescript
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { Card } from './Card';
|
||||
|
||||
describe('Card', () => {
|
||||
describe('Rendering', () => {
|
||||
it('renders children correctly', () => {
|
||||
render(<Card>Test content</Card>);
|
||||
expect(screen.getByText('Test content')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders title when provided', () => {
|
||||
render(<Card title="Test Title">Content</Card>);
|
||||
expect(screen.getByText('Test Title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders footer when provided', () => {
|
||||
render(
|
||||
<Card footer={<button>Action</button>}>
|
||||
Content
|
||||
</Card>
|
||||
);
|
||||
expect(screen.getByText('Action')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variants', () => {
|
||||
it('applies default variant styles', () => {
|
||||
const { container } = render(<Card>Content</Card>);
|
||||
expect(container.firstChild).toHaveClass('bg-white', 'border-gray-200');
|
||||
});
|
||||
|
||||
it('applies outlined variant styles', () => {
|
||||
const { container } = render(<Card variant="outlined">Content</Card>);
|
||||
expect(container.firstChild).toHaveClass('border-2');
|
||||
});
|
||||
|
||||
it('applies elevated variant styles', () => {
|
||||
const { container } = render(<Card variant="elevated">Content</Card>);
|
||||
expect(container.firstChild).toHaveClass('shadow-lg');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Interactions', () => {
|
||||
it('handles click events', () => {
|
||||
const onClick = vi.fn();
|
||||
render(<Card onClick={onClick}>Content</Card>);
|
||||
fireEvent.click(screen.getByText('Content'));
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('applies hover styles when clickable', () => {
|
||||
const { container } = render(<Card onClick={() => {}}>Content</Card>);
|
||||
expect(container.firstChild).toHaveClass('cursor-pointer');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('forwards ref correctly', () => {
|
||||
const ref = React.createRef<HTMLDivElement>();
|
||||
render(<Card ref={ref}>Content</Card>);
|
||||
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Storybook Stories Template
|
||||
|
||||
**Interactive Documentation**:
|
||||
```typescript
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Card } from './Card';
|
||||
|
||||
const meta: Meta<typeof Card> = {
|
||||
title: 'Molecules/Card',
|
||||
component: Card,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: ['default', 'outlined', 'elevated'],
|
||||
description: 'Visual variant of the card',
|
||||
},
|
||||
onClick: {
|
||||
action: 'clicked',
|
||||
},
|
||||
},
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div className="p-8 bg-gray-50">
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Card>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: 'This is a default card with some content.',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithTitle: Story = {
|
||||
args: {
|
||||
title: 'Card Title',
|
||||
children: 'Card content goes here with a title above.',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithFooter: Story = {
|
||||
args: {
|
||||
title: 'Card with Footer',
|
||||
children: 'Main content area',
|
||||
footer: <button className="text-blue-600 hover:underline">Learn more</button>,
|
||||
},
|
||||
};
|
||||
|
||||
export const Outlined: Story = {
|
||||
args: {
|
||||
variant: 'outlined',
|
||||
title: 'Outlined Card',
|
||||
children: 'This card has an outlined style.',
|
||||
},
|
||||
};
|
||||
|
||||
export const Elevated: Story = {
|
||||
args: {
|
||||
variant: 'elevated',
|
||||
title: 'Elevated Card',
|
||||
children: 'This card has a shadow elevation effect.',
|
||||
},
|
||||
};
|
||||
|
||||
export const Clickable: Story = {
|
||||
args: {
|
||||
title: 'Clickable Card',
|
||||
children: 'Click me to trigger an action!',
|
||||
onClick: () => alert('Card clicked!'),
|
||||
},
|
||||
};
|
||||
|
||||
export const Complex: Story = {
|
||||
args: {
|
||||
title: 'User Profile',
|
||||
variant: 'elevated',
|
||||
children: (
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-gray-600">John Doe</p>
|
||||
<p className="text-xs text-gray-500">john.doe@example.com</p>
|
||||
<div className="flex gap-2 mt-4">
|
||||
<span className="px-2 py-1 bg-blue-100 text-blue-800 rounded text-xs">Admin</span>
|
||||
<span className="px-2 py-1 bg-green-100 text-green-800 rounded text-xs">Active</span>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
footer: (
|
||||
<button className="w-full py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
|
||||
View Profile
|
||||
</button>
|
||||
),
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 5. Vue 3 Component Template (Composition API)
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
export interface CardProps {
|
||||
title?: string;
|
||||
variant?: 'default' | 'outlined' | 'elevated';
|
||||
footer?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<CardProps>(), {
|
||||
variant: 'default',
|
||||
footer: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
click: [];
|
||||
}>();
|
||||
|
||||
const cardClasses = computed(() => {
|
||||
const base = 'rounded-lg p-6 transition-all';
|
||||
const variants = {
|
||||
default: 'bg-white border border-gray-200',
|
||||
outlined: 'bg-transparent border-2 border-gray-300',
|
||||
elevated: 'bg-white shadow-lg hover:shadow-xl',
|
||||
};
|
||||
return `${base} ${variants[props.variant]}`;
|
||||
});
|
||||
|
||||
const handleClick = () => {
|
||||
emit('click');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cardClasses" @click="handleClick">
|
||||
<h3 v-if="title" class="text-lg font-semibold mb-4 text-gray-900">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<div class="text-gray-700">
|
||||
<slot />
|
||||
</div>
|
||||
<div v-if="footer || $slots.footer" class="mt-4 pt-4 border-t border-gray-200">
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 6. Angular Component Template
|
||||
|
||||
**TypeScript**:
|
||||
```typescript
|
||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
export type CardVariant = 'default' | 'outlined' | 'elevated';
|
||||
|
||||
@Component({
|
||||
selector: 'app-card',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
templateUrl: './card.component.html',
|
||||
styleUrls: ['./card.component.css'],
|
||||
})
|
||||
export class CardComponent {
|
||||
@Input() title?: string;
|
||||
@Input() variant: CardVariant = 'default';
|
||||
@Input() footer?: string;
|
||||
@Output() cardClick = new EventEmitter<void>();
|
||||
|
||||
get cardClasses(): string {
|
||||
const base = 'rounded-lg p-6 transition-all';
|
||||
const variants = {
|
||||
default: 'bg-white border border-gray-200',
|
||||
outlined: 'bg-transparent border-2 border-gray-300',
|
||||
elevated: 'bg-white shadow-lg hover:shadow-xl',
|
||||
};
|
||||
return `${base} ${variants[this.variant]}`;
|
||||
}
|
||||
|
||||
onClick(): void {
|
||||
this.cardClick.emit();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**HTML**:
|
||||
```html
|
||||
<div [class]="cardClasses" (click)="onClick()">
|
||||
<h3 *ngIf="title" class="text-lg font-semibold mb-4 text-gray-900">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<div class="text-gray-700">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
<div *ngIf="footer" class="mt-4 pt-4 border-t border-gray-200">
|
||||
<ng-content select="[footer]"></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 7. Component Documentation Template
|
||||
|
||||
**README.md**:
|
||||
```markdown
|
||||
# Card Component
|
||||
|
||||
A versatile card container component with multiple variants and optional footer.
|
||||
|
||||
## Features
|
||||
|
||||
- Multiple visual variants (default, outlined, elevated)
|
||||
- Optional title and footer sections
|
||||
- Clickable with hover effects
|
||||
- Fully typed with TypeScript
|
||||
- Responsive and accessible
|
||||
- Comprehensive test coverage
|
||||
|
||||
## Usage
|
||||
|
||||
### React
|
||||
\`\`\`tsx
|
||||
import { Card } from '@/components/molecules/Card';
|
||||
|
||||
<Card title="Welcome" variant="elevated" onClick={handleClick}>
|
||||
<p>Card content here</p>
|
||||
</Card>
|
||||
\`\`\`
|
||||
|
||||
### Vue 3
|
||||
\`\`\`vue
|
||||
<Card title="Welcome" variant="elevated" @click="handleClick">
|
||||
<p>Card content here</p>
|
||||
<template #footer>
|
||||
<button>Action</button>
|
||||
</template>
|
||||
</Card>
|
||||
\`\`\`
|
||||
|
||||
### Angular
|
||||
\`\`\`html
|
||||
<app-card title="Welcome" variant="elevated" (cardClick)="handleClick()">
|
||||
<p>Card content here</p>
|
||||
<div footer>
|
||||
<button>Action</button>
|
||||
</div>
|
||||
</app-card>
|
||||
\`\`\`
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| title | string | - | Optional card title |
|
||||
| variant | 'default' \| 'outlined' \| 'elevated' | 'default' | Visual style variant |
|
||||
| footer | ReactNode/slot | - | Optional footer content |
|
||||
| onClick | function | - | Click handler |
|
||||
|
||||
## Examples
|
||||
|
||||
See Storybook for interactive examples and all variants.
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Semantic HTML structure
|
||||
- Keyboard navigation support
|
||||
- ARIA labels where appropriate
|
||||
- Color contrast compliance (WCAG AA)
|
||||
|
||||
## Testing
|
||||
|
||||
Run tests:
|
||||
\`\`\`bash
|
||||
npm test Card.test.tsx
|
||||
\`\`\`
|
||||
|
||||
Coverage: 95%+ (statements, branches, functions, lines)
|
||||
```
|
||||
|
||||
### 8. Component File Structure
|
||||
|
||||
```
|
||||
components/{level}/{ComponentName}/
|
||||
├── index.ts # Barrel export
|
||||
├── {ComponentName}.tsx # Component implementation
|
||||
├── {ComponentName}.test.tsx # Unit tests
|
||||
├── {ComponentName}.stories.tsx # Storybook stories
|
||||
├── {ComponentName}.module.css # Scoped styles (if not using Tailwind)
|
||||
└── README.md # Documentation
|
||||
```
|
||||
|
||||
### 9. Index Export
|
||||
|
||||
**index.ts**:
|
||||
```typescript
|
||||
export { Card } from './Card';
|
||||
export type { CardProps } from './Card';
|
||||
```
|
||||
|
||||
### 10. Component Checklist
|
||||
|
||||
Before considering a component complete:
|
||||
- [ ] TypeScript interface with JSDoc comments
|
||||
- [ ] All variants implemented and tested
|
||||
- [ ] Unit tests with >80% coverage
|
||||
- [ ] Storybook stories for all variants
|
||||
- [ ] README documentation
|
||||
- [ ] Accessibility compliance (ARIA, keyboard nav)
|
||||
- [ ] Responsive design
|
||||
- [ ] Error states handled
|
||||
- [ ] Loading states (if applicable)
|
||||
- [ ] Dark mode support (if applicable)
|
||||
- [ ] Performance optimized (React.memo, useMemo)
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Ask about component requirements (type, props, variants)
|
||||
2. Determine Atomic Design level (atom/molecule/organism)
|
||||
3. Generate component implementation
|
||||
4. Create comprehensive unit tests
|
||||
5. Add Storybook stories for all variants
|
||||
6. Write documentation (README)
|
||||
7. Implement accessibility features
|
||||
8. Add to component index for easy imports
|
||||
|
||||
## Example Usage
|
||||
|
||||
**User**: "Generate a SearchBar molecule component with input and button"
|
||||
|
||||
**Response**:
|
||||
Creates complete SearchBar component with:
|
||||
- TypeScript implementation (React/Vue/Angular)
|
||||
- Props interface (query, onSearch, placeholder, etc.)
|
||||
- Unit tests (rendering, interactions, validation)
|
||||
- Storybook stories (default, with results, loading state)
|
||||
- README documentation
|
||||
- Accessibility features (ARIA labels, keyboard shortcuts)
|
||||
- Responsive design
|
||||
|
||||
## When to Use
|
||||
|
||||
- Creating new UI components
|
||||
- Refactoring existing components to design system
|
||||
- Building component libraries
|
||||
- Ensuring consistent component structure
|
||||
- Improving component documentation and testing
|
||||
|
||||
Generate production-ready components with complete tests and documentation!
|
||||
494
commands/design-system-init.md
Normal file
494
commands/design-system-init.md
Normal file
@@ -0,0 +1,494 @@
|
||||
# /specweave-frontend:design-system-init
|
||||
|
||||
Initialize a complete design system with Atomic Design principles, design tokens, and component library.
|
||||
|
||||
You are an expert design system architect who creates scalable, maintainable component libraries.
|
||||
|
||||
## Your Task
|
||||
|
||||
Create a production-ready design system foundation with design tokens, theming, and Atomic Design component structure.
|
||||
|
||||
### 1. Design System Architecture
|
||||
|
||||
**Atomic Design Hierarchy**:
|
||||
```
|
||||
src/components/
|
||||
├── atoms/ # Basic building blocks
|
||||
│ ├── Button/
|
||||
│ │ ├── Button.tsx
|
||||
│ │ ├── Button.test.tsx
|
||||
│ │ ├── Button.stories.tsx
|
||||
│ │ └── index.ts
|
||||
│ ├── Input/
|
||||
│ ├── Text/
|
||||
│ └── Icon/
|
||||
├── molecules/ # Simple component groups
|
||||
│ ├── FormField/ # Label + Input + Error
|
||||
│ ├── SearchBar/ # Input + Button
|
||||
│ └── Card/
|
||||
├── organisms/ # Complex components
|
||||
│ ├── Header/
|
||||
│ ├── Footer/
|
||||
│ ├── Form/
|
||||
│ └── Navigation/
|
||||
├── templates/ # Page layouts
|
||||
│ ├── DashboardLayout/
|
||||
│ ├── AuthLayout/
|
||||
│ └── MarketingLayout/
|
||||
└── pages/ # Full pages (if not using framework routing)
|
||||
```
|
||||
|
||||
### 2. Design Tokens
|
||||
|
||||
**tokens/colors.ts**:
|
||||
```typescript
|
||||
export const colors = {
|
||||
// Brand colors
|
||||
brand: {
|
||||
primary: {
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
500: '#3b82f6',
|
||||
600: '#2563eb',
|
||||
900: '#1e3a8a',
|
||||
},
|
||||
secondary: {
|
||||
50: '#f0fdf4',
|
||||
500: '#22c55e',
|
||||
900: '#14532d',
|
||||
},
|
||||
},
|
||||
// Neutral colors
|
||||
neutral: {
|
||||
white: '#ffffff',
|
||||
black: '#000000',
|
||||
50: '#fafafa',
|
||||
100: '#f5f5f5',
|
||||
500: '#737373',
|
||||
900: '#171717',
|
||||
},
|
||||
// Semantic colors
|
||||
semantic: {
|
||||
success: '#22c55e',
|
||||
warning: '#f59e0b',
|
||||
error: '#ef4444',
|
||||
info: '#3b82f6',
|
||||
},
|
||||
} as const;
|
||||
```
|
||||
|
||||
**tokens/typography.ts**:
|
||||
```typescript
|
||||
export const typography = {
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||
mono: ['Roboto Mono', 'monospace'],
|
||||
},
|
||||
fontSize: {
|
||||
xs: '0.75rem', // 12px
|
||||
sm: '0.875rem', // 14px
|
||||
base: '1rem', // 16px
|
||||
lg: '1.125rem', // 18px
|
||||
xl: '1.25rem', // 20px
|
||||
'2xl': '1.5rem', // 24px
|
||||
'3xl': '1.875rem', // 30px
|
||||
'4xl': '2.25rem', // 36px
|
||||
},
|
||||
fontWeight: {
|
||||
normal: 400,
|
||||
medium: 500,
|
||||
semibold: 600,
|
||||
bold: 700,
|
||||
},
|
||||
lineHeight: {
|
||||
none: 1,
|
||||
tight: 1.25,
|
||||
normal: 1.5,
|
||||
relaxed: 1.75,
|
||||
},
|
||||
} as const;
|
||||
```
|
||||
|
||||
**tokens/spacing.ts**:
|
||||
```typescript
|
||||
export const spacing = {
|
||||
0: '0',
|
||||
1: '0.25rem', // 4px
|
||||
2: '0.5rem', // 8px
|
||||
3: '0.75rem', // 12px
|
||||
4: '1rem', // 16px
|
||||
5: '1.25rem', // 20px
|
||||
6: '1.5rem', // 24px
|
||||
8: '2rem', // 32px
|
||||
10: '2.5rem', // 40px
|
||||
12: '3rem', // 48px
|
||||
16: '4rem', // 64px
|
||||
20: '5rem', // 80px
|
||||
} as const;
|
||||
```
|
||||
|
||||
**tokens/index.ts**:
|
||||
```typescript
|
||||
export { colors } from './colors';
|
||||
export { typography } from './typography';
|
||||
export { spacing } from './spacing';
|
||||
export { breakpoints } from './breakpoints';
|
||||
export { shadows } from './shadows';
|
||||
export { radii } from './radii';
|
||||
export { transitions } from './transitions';
|
||||
```
|
||||
|
||||
### 3. Theme Configuration
|
||||
|
||||
**theme/index.ts**:
|
||||
```typescript
|
||||
import { colors, typography, spacing } from '../tokens';
|
||||
|
||||
export const theme = {
|
||||
colors,
|
||||
typography,
|
||||
spacing,
|
||||
components: {
|
||||
Button: {
|
||||
variants: {
|
||||
primary: {
|
||||
bg: colors.brand.primary[500],
|
||||
color: colors.neutral.white,
|
||||
hover: {
|
||||
bg: colors.brand.primary[600],
|
||||
},
|
||||
},
|
||||
secondary: {
|
||||
bg: 'transparent',
|
||||
color: colors.brand.primary[500],
|
||||
border: `1px solid ${colors.brand.primary[500]}`,
|
||||
hover: {
|
||||
bg: colors.brand.primary[50],
|
||||
},
|
||||
},
|
||||
ghost: {
|
||||
bg: 'transparent',
|
||||
color: colors.neutral[700],
|
||||
hover: {
|
||||
bg: colors.neutral[100],
|
||||
},
|
||||
},
|
||||
},
|
||||
sizes: {
|
||||
sm: {
|
||||
height: '32px',
|
||||
px: spacing[3],
|
||||
fontSize: typography.fontSize.sm,
|
||||
},
|
||||
md: {
|
||||
height: '40px',
|
||||
px: spacing[4],
|
||||
fontSize: typography.fontSize.base,
|
||||
},
|
||||
lg: {
|
||||
height: '48px',
|
||||
px: spacing[6],
|
||||
fontSize: typography.fontSize.lg,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type Theme = typeof theme;
|
||||
```
|
||||
|
||||
### 4. Component Template (Button)
|
||||
|
||||
**components/atoms/Button/Button.tsx**:
|
||||
```typescript
|
||||
import React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
isLoading?: boolean;
|
||||
leftIcon?: React.ReactNode;
|
||||
rightIcon?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(
|
||||
{
|
||||
children,
|
||||
variant = 'primary',
|
||||
size = 'md',
|
||||
isLoading = false,
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
className,
|
||||
disabled,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const baseStyles = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2';
|
||||
|
||||
const variants = {
|
||||
primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',
|
||||
secondary: 'border border-blue-600 text-blue-600 hover:bg-blue-50 focus:ring-blue-500',
|
||||
ghost: 'text-gray-700 hover:bg-gray-100 focus:ring-gray-500',
|
||||
danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',
|
||||
};
|
||||
|
||||
const sizes = {
|
||||
sm: 'h-8 px-3 text-sm',
|
||||
md: 'h-10 px-4 text-base',
|
||||
lg: 'h-12 px-6 text-lg',
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
className={cn(
|
||||
baseStyles,
|
||||
variants[variant],
|
||||
sizes[size],
|
||||
(disabled || isLoading) && 'opacity-50 cursor-not-allowed',
|
||||
className
|
||||
)}
|
||||
disabled={disabled || isLoading}
|
||||
{...props}
|
||||
>
|
||||
{isLoading && <LoadingSpinner className="mr-2" />}
|
||||
{!isLoading && leftIcon && <span className="mr-2">{leftIcon}</span>}
|
||||
{children}
|
||||
{!isLoading && rightIcon && <span className="ml-2">{rightIcon}</span>}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Button.displayName = 'Button';
|
||||
```
|
||||
|
||||
**components/atoms/Button/Button.test.tsx**:
|
||||
```typescript
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { Button } from './Button';
|
||||
|
||||
describe('Button', () => {
|
||||
it('renders correctly', () => {
|
||||
render(<Button>Click me</Button>);
|
||||
expect(screen.getByText('Click me')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles click events', () => {
|
||||
const onClick = vi.fn();
|
||||
render(<Button onClick={onClick}>Click me</Button>);
|
||||
fireEvent.click(screen.getByText('Click me'));
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('shows loading state', () => {
|
||||
render(<Button isLoading>Click me</Button>);
|
||||
expect(screen.getByRole('button')).toBeDisabled();
|
||||
});
|
||||
|
||||
it('applies variant styles', () => {
|
||||
const { rerender } = render(<Button variant="primary">Primary</Button>);
|
||||
expect(screen.getByRole('button')).toHaveClass('bg-blue-600');
|
||||
|
||||
rerender(<Button variant="secondary">Secondary</Button>);
|
||||
expect(screen.getByRole('button')).toHaveClass('border-blue-600');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 5. Storybook Integration
|
||||
|
||||
**components/atoms/Button/Button.stories.tsx**:
|
||||
```typescript
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Button } from './Button';
|
||||
|
||||
const meta: Meta<typeof Button> = {
|
||||
title: 'Atoms/Button',
|
||||
component: Button,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: ['primary', 'secondary', 'ghost', 'danger'],
|
||||
},
|
||||
size: {
|
||||
control: 'select',
|
||||
options: ['sm', 'md', 'lg'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Button>;
|
||||
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
variant: 'primary',
|
||||
children: 'Primary Button',
|
||||
},
|
||||
};
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
variant: 'secondary',
|
||||
children: 'Secondary Button',
|
||||
},
|
||||
};
|
||||
|
||||
export const Loading: Story = {
|
||||
args: {
|
||||
variant: 'primary',
|
||||
isLoading: true,
|
||||
children: 'Loading...',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithIcons: Story = {
|
||||
args: {
|
||||
variant: 'primary',
|
||||
leftIcon: <IconPlus />,
|
||||
children: 'Add Item',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 6. Utility Function (cn)
|
||||
|
||||
**lib/utils.ts**:
|
||||
```typescript
|
||||
import { clsx, type ClassValue } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
```
|
||||
|
||||
### 7. TailwindCSS Configuration
|
||||
|
||||
**tailwind.config.ts**:
|
||||
```typescript
|
||||
import type { Config } from 'tailwindcss';
|
||||
import { colors, typography, spacing } from './src/tokens';
|
||||
|
||||
const config: Config = {
|
||||
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: colors.brand,
|
||||
neutral: colors.neutral,
|
||||
},
|
||||
fontFamily: typography.fontFamily,
|
||||
fontSize: typography.fontSize,
|
||||
spacing,
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
|
||||
export default config;
|
||||
```
|
||||
|
||||
### 8. Documentation Template
|
||||
|
||||
**components/atoms/Button/README.md**:
|
||||
```markdown
|
||||
# Button Component
|
||||
|
||||
A versatile button component with multiple variants and sizes.
|
||||
|
||||
## Usage
|
||||
|
||||
\`\`\`tsx
|
||||
import { Button } from '@/components/atoms/Button';
|
||||
|
||||
<Button variant="primary" size="md" onClick={handleClick}>
|
||||
Click me
|
||||
</Button>
|
||||
\`\`\`
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| variant | 'primary' \| 'secondary' \| 'ghost' \| 'danger' | 'primary' | Button style variant |
|
||||
| size | 'sm' \| 'md' \| 'lg' | 'md' | Button size |
|
||||
| isLoading | boolean | false | Shows loading spinner |
|
||||
| leftIcon | ReactNode | - | Icon before text |
|
||||
| rightIcon | ReactNode | - | Icon after text |
|
||||
|
||||
## Examples
|
||||
|
||||
See Storybook for interactive examples.
|
||||
```
|
||||
|
||||
### 9. Essential Dependencies
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0",
|
||||
"tailwind-merge": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/react": "^7.6.0",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"tailwindcss": "^3.4.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10. Component Checklist
|
||||
|
||||
For each component, ensure:
|
||||
- [ ] TypeScript props interface with JSDoc
|
||||
- [ ] Accessibility (ARIA labels, keyboard navigation)
|
||||
- [ ] Unit tests (>80% coverage)
|
||||
- [ ] Storybook stories
|
||||
- [ ] README documentation
|
||||
- [ ] Responsive design
|
||||
- [ ] Dark mode support (if applicable)
|
||||
- [ ] Performance optimized (React.memo if needed)
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Ask about design system requirements (brand colors, components needed)
|
||||
2. Generate design tokens (colors, typography, spacing)
|
||||
3. Set up theme configuration
|
||||
4. Create component structure (Atomic Design)
|
||||
5. Implement essential atoms (Button, Input, Text, Icon)
|
||||
6. Add Storybook for component documentation
|
||||
7. Configure Tailwind with design tokens
|
||||
8. Provide usage guidelines and examples
|
||||
|
||||
## Example Usage
|
||||
|
||||
**User**: "Initialize a design system with blue brand color and Atomic Design"
|
||||
|
||||
**Response**:
|
||||
Creates complete design system with:
|
||||
- Design tokens (colors, typography, spacing)
|
||||
- Theme configuration
|
||||
- Atomic Design component structure
|
||||
- Button, Input, Text components
|
||||
- Storybook setup
|
||||
- TailwindCSS integration
|
||||
- Testing setup
|
||||
- Documentation templates
|
||||
|
||||
## When to Use
|
||||
|
||||
- Starting new design systems
|
||||
- Standardizing component libraries
|
||||
- Rebranding existing applications
|
||||
- Creating white-label products
|
||||
- Building component libraries for teams
|
||||
|
||||
Build scalable, maintainable design systems with Atomic Design principles!
|
||||
207
commands/frontend-scaffold.md
Normal file
207
commands/frontend-scaffold.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# /specweave-frontend:frontend-scaffold
|
||||
|
||||
Scaffold a complete frontend project with modern tooling and best practices.
|
||||
|
||||
You are an expert frontend architect who creates production-ready project structures.
|
||||
|
||||
## Your Task
|
||||
|
||||
Generate a complete frontend project scaffold based on the user's requirements. Support React, Vue, Angular, and Next.js with modern tooling.
|
||||
|
||||
### 1. Supported Frameworks
|
||||
|
||||
**React**:
|
||||
- TypeScript + Vite
|
||||
- ESLint + Prettier
|
||||
- Vitest + React Testing Library
|
||||
- TailwindCSS or styled-components
|
||||
- React Router v6
|
||||
- React Query for data fetching
|
||||
|
||||
**Next.js 14+**:
|
||||
- App Router (default)
|
||||
- TypeScript
|
||||
- TailwindCSS
|
||||
- Server Components
|
||||
- Route Handlers
|
||||
- Metadata API
|
||||
|
||||
**Vue 3**:
|
||||
- TypeScript + Vite
|
||||
- Composition API
|
||||
- Vue Router v4
|
||||
- Pinia for state management
|
||||
- Vitest
|
||||
|
||||
**Angular 17+**:
|
||||
- Standalone Components
|
||||
- TypeScript (strict)
|
||||
- Angular Material
|
||||
- RxJS
|
||||
- Jest
|
||||
|
||||
### 2. Project Structure
|
||||
|
||||
**React/Next.js**:
|
||||
```
|
||||
src/
|
||||
├── app/ # Next.js App Router
|
||||
│ ├── layout.tsx
|
||||
│ ├── page.tsx
|
||||
│ └── api/
|
||||
├── components/
|
||||
│ ├── atoms/ # Atomic Design
|
||||
│ ├── molecules/
|
||||
│ ├── organisms/
|
||||
│ └── templates/
|
||||
├── hooks/
|
||||
├── lib/
|
||||
│ ├── api/
|
||||
│ ├── utils/
|
||||
│ └── constants/
|
||||
├── styles/
|
||||
│ └── globals.css
|
||||
└── types/
|
||||
```
|
||||
|
||||
**Vue**:
|
||||
```
|
||||
src/
|
||||
├── components/
|
||||
├── composables/
|
||||
├── router/
|
||||
├── stores/
|
||||
├── views/
|
||||
├── assets/
|
||||
└── types/
|
||||
```
|
||||
|
||||
### 3. Configuration Files
|
||||
|
||||
Generate these essential config files:
|
||||
|
||||
**TypeScript** (`tsconfig.json`):
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"jsx": "react-jsx",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**ESLint** (`.eslintrc.json`):
|
||||
- TypeScript rules
|
||||
- React Hooks rules
|
||||
- Accessibility rules (jsx-a11y)
|
||||
- Import ordering
|
||||
|
||||
**Prettier** (`.prettierrc`):
|
||||
- Consistent formatting
|
||||
- Tailwind plugin if using Tailwind
|
||||
|
||||
**Vite/Next Config**:
|
||||
- Path aliases
|
||||
- Environment variables
|
||||
- Build optimization
|
||||
|
||||
### 4. Essential Dependencies
|
||||
|
||||
**Core**:
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"typescript": "^5.3.0",
|
||||
"vite": "^5.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**State Management**:
|
||||
- Zustand (lightweight)
|
||||
- Redux Toolkit (complex state)
|
||||
- React Query (server state)
|
||||
|
||||
**Styling**:
|
||||
- TailwindCSS (utility-first)
|
||||
- styled-components (CSS-in-JS)
|
||||
- CSS Modules (scoped styles)
|
||||
|
||||
**Testing**:
|
||||
- Vitest (unit tests)
|
||||
- React Testing Library
|
||||
- Playwright (E2E)
|
||||
|
||||
### 5. Features to Include
|
||||
|
||||
1. **TypeScript Configuration**: Strict mode, path aliases
|
||||
2. **Linting & Formatting**: ESLint, Prettier, Husky pre-commit hooks
|
||||
3. **Testing Setup**: Unit test framework + E2E setup
|
||||
4. **CI/CD**: GitHub Actions workflow
|
||||
5. **Environment Management**: `.env` files for different environments
|
||||
6. **Error Handling**: Error boundaries, global error handling
|
||||
7. **Loading States**: Skeleton loaders, suspense boundaries
|
||||
8. **Routing**: File-based (Next.js) or configured routing
|
||||
9. **API Integration**: Fetch wrapper, error handling, typing
|
||||
10. **Performance**: Code splitting, lazy loading, memoization
|
||||
|
||||
### 6. Best Practices
|
||||
|
||||
- **Component Organization**: Atomic Design pattern
|
||||
- **Type Safety**: No `any` types, strict mode
|
||||
- **Accessibility**: ARIA labels, keyboard navigation
|
||||
- **Performance**: React.memo, useMemo, useCallback
|
||||
- **SEO**: Metadata, Open Graph tags (Next.js)
|
||||
- **Security**: CSP headers, XSS prevention
|
||||
- **Code Quality**: Consistent naming, clear structure
|
||||
|
||||
### 7. Workflow
|
||||
|
||||
1. Ask about framework choice (React/Next/Vue/Angular)
|
||||
2. Confirm styling approach (Tailwind/styled-components/CSS Modules)
|
||||
3. Verify state management needs
|
||||
4. Generate complete file structure
|
||||
5. Create configuration files
|
||||
6. Set up package.json with scripts
|
||||
7. Provide setup instructions
|
||||
|
||||
## Example Usage
|
||||
|
||||
**User**: "Scaffold a Next.js 14 project with TailwindCSS and TypeScript"
|
||||
|
||||
**Response**:
|
||||
Creates complete Next.js 14 App Router project with:
|
||||
- TypeScript configuration
|
||||
- TailwindCSS setup
|
||||
- ESLint + Prettier
|
||||
- Path aliases
|
||||
- Testing setup
|
||||
- CI/CD workflow
|
||||
- Example components
|
||||
|
||||
## When to Use
|
||||
|
||||
- Starting new frontend projects
|
||||
- Converting to TypeScript
|
||||
- Modernizing legacy projects
|
||||
- Setting up best practices
|
||||
- Creating consistent team structure
|
||||
|
||||
Scaffold production-ready frontend projects with modern tooling and best practices!
|
||||
396
commands/nextjs-setup.md
Normal file
396
commands/nextjs-setup.md
Normal file
@@ -0,0 +1,396 @@
|
||||
# /specweave-frontend:nextjs-setup
|
||||
|
||||
Set up Next.js 14+ App Router project with modern best practices and production-ready configuration.
|
||||
|
||||
You are an expert Next.js architect specializing in the App Router, Server Components, and modern React patterns.
|
||||
|
||||
## Your Task
|
||||
|
||||
Configure a complete Next.js 14+ project with best practices for performance, SEO, and developer experience.
|
||||
|
||||
### 1. App Router Structure
|
||||
|
||||
Generate the following directory structure:
|
||||
|
||||
```
|
||||
app/
|
||||
├── layout.tsx # Root layout with metadata
|
||||
├── page.tsx # Home page
|
||||
├── loading.tsx # Loading UI
|
||||
├── error.tsx # Error boundary
|
||||
├── not-found.tsx # 404 page
|
||||
├── global-error.tsx # Global error handler
|
||||
├── api/ # API routes
|
||||
│ └── hello/
|
||||
│ └── route.ts
|
||||
├── (marketing)/ # Route group
|
||||
│ ├── about/
|
||||
│ │ └── page.tsx
|
||||
│ └── contact/
|
||||
│ └── page.tsx
|
||||
└── (dashboard)/ # Protected route group
|
||||
├── layout.tsx
|
||||
└── page.tsx
|
||||
```
|
||||
|
||||
### 2. Root Layout Configuration
|
||||
|
||||
**app/layout.tsx**:
|
||||
```typescript
|
||||
import type { Metadata } from 'next';
|
||||
import { Inter } from 'next/font/google';
|
||||
import './globals.css';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: 'App Name',
|
||||
template: '%s | App Name',
|
||||
},
|
||||
description: 'App description',
|
||||
metadataBase: new URL('https://example.com'),
|
||||
openGraph: {
|
||||
type: 'website',
|
||||
locale: 'en_US',
|
||||
url: 'https://example.com',
|
||||
siteName: 'App Name',
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
creator: '@username',
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Server Components Best Practices
|
||||
|
||||
**Data Fetching** (Server Component):
|
||||
```typescript
|
||||
// app/posts/page.tsx
|
||||
async function getPosts() {
|
||||
const res = await fetch('https://api.example.com/posts', {
|
||||
next: { revalidate: 3600 }, // ISR: Revalidate every hour
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export default async function PostsPage() {
|
||||
const posts = await getPosts();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Posts</h1>
|
||||
{posts.map((post) => (
|
||||
<article key={post.id}>{post.title}</article>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Client Components** (for interactivity):
|
||||
```typescript
|
||||
'use client'; // Mark as Client Component
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
export function Counter() {
|
||||
const [count, setCount] = useState(0);
|
||||
return <button onClick={() => setCount(count + 1)}>{count}</button>;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. API Routes (Route Handlers)
|
||||
|
||||
**app/api/hello/route.ts**:
|
||||
```typescript
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const name = searchParams.get('name') || 'World';
|
||||
|
||||
return NextResponse.json({ message: `Hello, ${name}!` });
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const body = await request.json();
|
||||
|
||||
// Process request
|
||||
return NextResponse.json({ success: true, data: body });
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Middleware Configuration
|
||||
|
||||
**middleware.ts** (root level):
|
||||
```typescript
|
||||
import { NextResponse } from 'next/server';
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
// Auth check
|
||||
const token = request.cookies.get('token');
|
||||
|
||||
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
|
||||
return NextResponse.redirect(new URL('/login', request.url));
|
||||
}
|
||||
|
||||
// Add custom headers
|
||||
const response = NextResponse.next();
|
||||
response.headers.set('x-custom-header', 'value');
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: ['/dashboard/:path*'],
|
||||
};
|
||||
```
|
||||
|
||||
### 6. next.config.js
|
||||
|
||||
```javascript
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'images.example.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
experimental: {
|
||||
typedRoutes: true, // Type-safe navigation
|
||||
},
|
||||
// Headers for security
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'X-DNS-Prefetch-Control',
|
||||
value: 'on',
|
||||
},
|
||||
{
|
||||
key: 'Strict-Transport-Security',
|
||||
value: 'max-age=63072000; includeSubDomains',
|
||||
},
|
||||
{
|
||||
key: 'X-Frame-Options',
|
||||
value: 'SAMEORIGIN',
|
||||
},
|
||||
{
|
||||
key: 'X-Content-Type-Options',
|
||||
value: 'nosniff',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
```
|
||||
|
||||
### 7. Environment Variables
|
||||
|
||||
**.env.local**:
|
||||
```bash
|
||||
# Public (accessible in browser)
|
||||
NEXT_PUBLIC_API_URL=https://api.example.com
|
||||
|
||||
# Private (server-only)
|
||||
DATABASE_URL=postgresql://...
|
||||
API_SECRET_KEY=...
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
```typescript
|
||||
// Server Component or API Route
|
||||
const dbUrl = process.env.DATABASE_URL;
|
||||
|
||||
// Client Component
|
||||
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
||||
```
|
||||
|
||||
### 8. Performance Optimizations
|
||||
|
||||
**Dynamic Imports** (Code Splitting):
|
||||
```typescript
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const DynamicComponent = dynamic(() => import('@/components/HeavyComponent'), {
|
||||
loading: () => <p>Loading...</p>,
|
||||
ssr: false, // Disable SSR for this component
|
||||
});
|
||||
```
|
||||
|
||||
**Image Optimization**:
|
||||
```typescript
|
||||
import Image from 'next/image';
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<Image
|
||||
src="/hero.jpg"
|
||||
alt="Hero image"
|
||||
width={1200}
|
||||
height={600}
|
||||
priority // Load immediately
|
||||
placeholder="blur"
|
||||
blurDataURL="data:image/jpeg;base64,..."
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Font Optimization**:
|
||||
```typescript
|
||||
import { Inter, Roboto_Mono } from 'next/font/google';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' });
|
||||
const robotoMono = Roboto_Mono({ subsets: ['latin'], variable: '--font-mono' });
|
||||
|
||||
// In layout
|
||||
<body className={`${inter.variable} ${robotoMono.variable}`}>
|
||||
```
|
||||
|
||||
### 9. Data Fetching Patterns
|
||||
|
||||
**Server Actions** (experimental):
|
||||
```typescript
|
||||
'use server';
|
||||
|
||||
export async function createPost(formData: FormData) {
|
||||
const title = formData.get('title');
|
||||
|
||||
// Database operation
|
||||
await db.post.create({ data: { title } });
|
||||
|
||||
revalidatePath('/posts');
|
||||
redirect('/posts');
|
||||
}
|
||||
```
|
||||
|
||||
**Streaming with Suspense**:
|
||||
```typescript
|
||||
import { Suspense } from 'react';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<Suspense fallback={<LoadingSkeleton />}>
|
||||
<AsyncData />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 10. Essential Dependencies
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "^14.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"typescript": "^5.3.0",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-config-next": "^14.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 11. Deployment Configuration
|
||||
|
||||
**Vercel** (recommended):
|
||||
- Automatic deployments from Git
|
||||
- Edge Functions support
|
||||
- Image Optimization CDN
|
||||
- Analytics built-in
|
||||
|
||||
**Docker** (self-hosted):
|
||||
```dockerfile
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
FROM base AS deps
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV production
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
|
||||
EXPOSE 3000
|
||||
CMD ["node", "server.js"]
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Ask about project requirements (API routes, auth, database, etc.)
|
||||
2. Generate complete App Router structure
|
||||
3. Set up metadata and SEO configuration
|
||||
4. Configure middleware if needed
|
||||
5. Create route groups for organization
|
||||
6. Set up environment variables
|
||||
7. Configure next.config.js
|
||||
8. Provide setup and deployment instructions
|
||||
|
||||
## Example Usage
|
||||
|
||||
**User**: "Set up Next.js 14 with App Router and authentication"
|
||||
|
||||
**Response**:
|
||||
Creates complete Next.js setup with:
|
||||
- App Router structure with route groups
|
||||
- Middleware for auth protection
|
||||
- API routes for authentication
|
||||
- Server and Client Components
|
||||
- Metadata configuration
|
||||
- Security headers
|
||||
- Deployment configuration
|
||||
|
||||
## When to Use
|
||||
|
||||
- Starting new Next.js projects
|
||||
- Migrating from Pages Router to App Router
|
||||
- Setting up authentication flows
|
||||
- Configuring SEO and metadata
|
||||
- Optimizing performance
|
||||
- Setting up API routes
|
||||
|
||||
Build production-ready Next.js applications with modern best practices!
|
||||
Reference in New Issue
Block a user