190 lines
5.0 KiB
Markdown
190 lines
5.0 KiB
Markdown
# AGENTS.md - Coding Guidelines for RagusaIT Web
|
|
|
|
## Build Commands
|
|
|
|
```bash
|
|
# Development server (port 3000)
|
|
npm run dev
|
|
|
|
# Production build
|
|
npm run build
|
|
|
|
# Preview production build
|
|
npm run preview
|
|
```
|
|
|
|
## Test Commands
|
|
|
|
```bash
|
|
# Run all tests
|
|
npx vitest
|
|
|
|
# Run tests in watch mode
|
|
npx vitest --watch
|
|
|
|
# Run tests with coverage
|
|
npx vitest --coverage
|
|
|
|
# Run a single test file
|
|
npx vitest src/components/ui/__tests__/Button.test.tsx
|
|
|
|
# Run tests matching a pattern
|
|
npx vitest --grep "Button"
|
|
|
|
# Run specific test by name
|
|
npx vitest -t "passes aria-label to the button element"
|
|
```
|
|
|
|
## Code Style Guidelines
|
|
|
|
### TypeScript
|
|
|
|
- Use **strict TypeScript** configuration
|
|
- Always define explicit return types for functions
|
|
- Use `type` for type definitions, `interface` for object shapes that may be extended
|
|
- Avoid `any` - use `unknown` when type is uncertain
|
|
|
|
### Imports (Order)
|
|
|
|
1. React imports
|
|
2. Third-party libraries (alphabetical)
|
|
3. Absolute imports from project (`@/components`, `@/utils`)
|
|
4. Relative imports
|
|
5. CSS/SCSS imports last
|
|
|
|
```typescript
|
|
import { useState, useEffect } from 'react';
|
|
import { motion } from 'motion/react';
|
|
import { Link } from 'react-router-dom';
|
|
import { useTranslation } from '../../i18n';
|
|
import { debounce } from '../../utils/debounce';
|
|
import styles from './Navbar.module.css';
|
|
```
|
|
|
|
### Component Structure
|
|
|
|
- Use **named exports** for all components
|
|
- Props interface should be named `[ComponentName]Props`
|
|
- Place props interface immediately before component
|
|
- Use CSS Modules for styling (`.module.css`)
|
|
- Destructure props in function signature with defaults
|
|
|
|
```typescript
|
|
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
variant?: 'primary' | 'secondary' | 'outline';
|
|
size?: 'sm' | 'md' | 'lg';
|
|
isLoading?: boolean;
|
|
}
|
|
|
|
export function Button({
|
|
variant = 'primary',
|
|
size = 'md',
|
|
isLoading = false,
|
|
children,
|
|
...props
|
|
}: ButtonProps) {
|
|
// Implementation
|
|
}
|
|
```
|
|
|
|
### Naming Conventions
|
|
|
|
- **Components**: PascalCase (e.g., `Navbar`, `Button`)
|
|
- **Hooks**: camelCase with `use` prefix (e.g., `useRateLimit`, `useTypingEffect`)
|
|
- **Utilities**: camelCase (e.g., `debounce`, `formatDate`)
|
|
- **Types/Interfaces**: PascalCase (e.g., `ButtonProps`, `Translations`)
|
|
- **Constants**: UPPER_SNAKE_CASE for module-level constants
|
|
- **Files**: Match default export name (e.g., `Navbar.tsx` exports `Navbar`)
|
|
|
|
### Error Handling
|
|
|
|
- Use try-catch for async operations
|
|
- Provide meaningful error messages
|
|
- Use early returns to avoid nested conditionals
|
|
- Clean up resources (timers, event listeners) in useEffect cleanup
|
|
|
|
```typescript
|
|
useEffect(() => {
|
|
const handleScroll = () => {
|
|
if (!ticking) {
|
|
window.requestAnimationFrame(() => {
|
|
setIsScrolled(window.scrollY > 20);
|
|
ticking = false;
|
|
});
|
|
ticking = true;
|
|
}
|
|
};
|
|
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
return () => window.removeEventListener('scroll', handleScroll);
|
|
}, []);
|
|
```
|
|
|
|
### Testing
|
|
|
|
- Add `// @vitest-environment jsdom` at top of test files
|
|
- Use `describe` and `it` blocks with descriptive names
|
|
- Clean up after each test with `cleanup()` from testing-library
|
|
- Mock external dependencies appropriately
|
|
- Test behavior, not implementation
|
|
|
|
```typescript
|
|
// @vitest-environment jsdom
|
|
import { render, screen, cleanup } from '@testing-library/react';
|
|
import { describe, it, expect, afterEach } from 'vitest';
|
|
|
|
describe('Button', () => {
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
it('renders with correct aria-label', () => {
|
|
render(<Button aria-label="Submit">Click</Button>);
|
|
expect(screen.getByRole('button', { name: /submit/i })).toBeTruthy();
|
|
});
|
|
});
|
|
```
|
|
|
|
### Performance
|
|
|
|
- Use `React.lazy()` for code splitting on routes
|
|
- Memoize expensive calculations with `useMemo`
|
|
- Use `requestAnimationFrame` for scroll/resize handlers
|
|
- Debounce expensive operations (use `debounce` utility)
|
|
- Add `will-change` CSS sparingly for GPU acceleration
|
|
|
|
### Accessibility
|
|
|
|
- Always include meaningful `aria-label` for interactive elements
|
|
- Use semantic HTML elements (`<header>`, `<nav>`, `<main>`)
|
|
- Ensure keyboard navigation works
|
|
- Test with screen readers
|
|
|
|
### Project Structure
|
|
|
|
```
|
|
src/
|
|
├── components/
|
|
│ ├── effects/ # Visual effects (WebGL, animations)
|
|
│ ├── layout/ # Layout components (Navbar, Footer)
|
|
│ ├── sections/ # Page sections (Hero, Services)
|
|
│ └── ui/ # Reusable UI components (Button, Input)
|
|
├── hooks/ # Custom React hooks
|
|
├── i18n/ # Internationalization (de.ts, en.ts)
|
|
├── pages/ # Route components
|
|
├── styles/ # Global styles
|
|
└── utils/ # Utility functions
|
|
```
|
|
|
|
### State Management
|
|
|
|
- Use React Context for global state (e.g., i18n)
|
|
- Prefer local state when possible
|
|
- Use `useRef` for DOM references and mutable values
|
|
- Store user preferences in localStorage with proper keys
|
|
|
|
### Comments
|
|
|
|
- Use JSDoc for exported functions
|
|
- Explain "why" not "what" in comments
|
|
- Keep comments concise and meaningful
|