14 KiB
AGENTS.md - Development Guide
📋 Project Overview
www.ermnvldmr.com is Vladimir Eremin's personal website built with modern web technologies. The project serves as a platform for sharing thoughts, creations, and personal content through a carefully crafted design system.
Core Architecture
- Framework: Astro.js v5+ (Static Site Generator)
- UI Library: React 19+ (Islands Architecture)
- Styling: Tailwind CSS v4+ with custom design system
- Language: TypeScript (Strict mode)
- Build Tool: Vite 7+
- Package Manager: pnpm
- Deployment: GitHub Pages with automated CI/CD
Design Philosophy
- Component-First: All UI elements are developed as isolated, reusable components
- Design System: Custom "rainby" color palette with consistent theming
- Performance-First: Static generation with minimal JavaScript islands
- TypeScript-First: Strict typing for all public APIs and components
- Documentation-Driven: TSDoc required for all exported elements
🏗️ Directory Structure
├── src/
│ ├── components/ # React component library
│ │ ├── shared/ # Common reusable components
│ │ └── {theme_name}/ # Theme-specific components
│ ├── www/ # Astro application (custom srcDir)
│ │ ├── layouts/ # Page layout templates (.astro)
│ │ ├── pages/ # File-based routing (.astro)
│ │ ├── shared/ # Utilities and constants
│ │ └── widgets/ # Feature-specific components
│ └── global.css # Design system + Tailwind configuration
├── content/ # Astro Content Collections
│ └── {collections}/ # Blog posts, articles, etc.
├── .storybook/ # Component development environment
├── .github/workflows/ # CI/CD automation
├── public/ # Static assets
└── [config files] # Various configuration files
Key Directory Rules
/src/components/
- Purpose: Reusable React components developed in isolation
- Structure: Organized by usage scope (
shared/for common,{theme}/for themed) - Requirements: All components must have Storybook stories
- Exports: Only manually developed components, no external UI library dependencies
/src/www/
- Purpose: Astro application code (set as custom srcDir in config)
- Constraint: Only use manually developed components from
/src/components/ - Organization: Standard Astro structure with layouts, pages, and utilities
/src/www/widgets/
- Purpose: Feature-specific components tied to particular functionality
- Usage: Components that don't fit in shared or themed categories
/content/
- Purpose: Content management using Astro Content Collections
- Structure: Organized by content type (blog, projects, etc.)
- Format: Markdown/MDX files with frontmatter validation
🛠️ Development Workflow
Component Development
- Storybook-First: All components must be developed and tested in Storybook
- Isolation: Components should work independently without page context
- Documentation: Each component requires comprehensive Storybook stories
- Manual Development: No external UI library components in
/src/www/
Code Organization Principles
- Single Responsibility: Each component serves one clear purpose
- Composition Over Inheritance: Build complex UIs by composing simple components
- TypeScript-First: All public APIs must be properly typed
- Documentation-Required: TSDoc for all exported elements
📝 Coding Conventions
TypeScript Guidelines
- Strict Mode: All TypeScript strict checks must pass
- Explicit Types: Avoid
anyand prefer explicit type definitions - Interface First: Use interfaces for component props and public APIs
- Generic Constraints: Properly constrain generic types
TSDoc Documentation Requirements
MANDATORY for all exported elements: types, functions, constants, components
/**
* Formats a date string or timestamp into human-readable format.
*
* @param input - The date to format (string or number timestamp)
* @returns Formatted date string in "Month Day, Year" format
*
* @example
* \`\`\`typescript
* formatDate('2024-01-15') // "January 15, 2024"
* formatDate(1705276800000) // "January 15, 2024"
* \`\`\`
*/
export function formatDate(input: string | number): string {
// implementation
}
/**
* Props for the PageHead component.
*/
export interface PageHeadProps {
/** The main title displayed in the header */
title: string;
/** Array of breadcrumbs, displayed above the title */
breadcrumbs?: Breadcrumb[];
/** Navigation items displayed on the right */
navItems?: NavItem[];
}
React Component Patterns
- Functional Components: Use function declarations with explicit typing
- Props Interfaces: Define props as separate interfaces with TSDoc
- Memo Usage: Wrap components with
memo()for performance when appropriate - Event Handlers: Type event handlers explicitly
/**
* A reusable button component with consistent styling and behavior.
*/
export interface ButtonProps {
/** The button text content */
children: React.ReactNode;
/** Click event handler */
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
/** Visual style variant */
variant?: 'primary' | 'secondary' | 'outline';
}
/**
* Button component following the design system patterns.
*/
export const Button = memo(function Button({
children,
onClick,
variant = 'primary'
}: ButtonProps) {
// implementation
});
File Naming Conventions
- Components: PascalCase (e.g.,
PageHead.tsx,NavigationMenu.tsx) - Utilities: camelCase (e.g.,
formatDate.ts,apiHelpers.ts) - Constants: UPPER_SNAKE_CASE for exports (e.g.,
SITE_TITLE) - Types: PascalCase with descriptive suffixes (e.g.,
ButtonProps,ApiResponse)
Module Structure Patterns
Each module in the project follows a consistent internal structure to maintain organization and scalability.
Component Modules
For widgets and shared components:
src/widgets/SomeComponent/
├── hooks/
│ └── someHook/
│ └── someHook.tsx
├── helpers/
│ └── someHelper/
│ └── someHelper.ts
├── components/
│ └── SomeSubComponent/
│ └── SomeSubComponent.tsx
├── SomeComponent.tsx
├── SomeComponent.test.tsx (if needed)
└── SomeComponent.stories.tsx
Shared Helper Modules
For standalone utility functions:
src/shared/helpers/someHelper/
├── helpers/
│ └── someSubHelper/
│ └── someSubHelper.ts
├── someHelper.ts
└── someHelper.test.ts
Shared Hook Modules
For reusable React hooks:
src/shared/hooks/someHook/
├── hooks/
│ └── someSubHook/
│ └── someSubHook.ts
├── someHook.ts
└── someHook.test.ts
Module Organization Rules
- Single Entry Point: Each module exports its main functionality from the root file
- Internal Dependencies: Sub-modules (helpers/, hooks/, components/) are implementation details
- Testing Strategy: Test only the final exported component/function, not internal dependencies
- Story Strategy: Create Storybook stories only for the main exported component
- Naming Consistency: Directory names match the main export name
- Isolation: Each module should be self-contained and independently testable
Testing Guidelines
- Focus on Public API: Test the main exported functionality, not internal helpers
- Integration Over Unit: Test how the complete module works, not individual pieces
- Story-Driven Testing: Use Storybook stories as living documentation and manual testing
- Avoid Over-Testing: Don't create separate tests for internal sub-modules
Import/Export Patterns
// Main module file (SomeComponent.tsx)
import { someHelper } from './helpers/someHelper/someHelper';
import { useSomeHook } from './hooks/someHook/someHook';
import SomeSubComponent from './components/SomeSubComponent/SomeSubComponent';
// Export only the main component
export default SomeComponent;
export type { SomeComponentProps };
🎨 Design System
Color Palette
The project uses a custom "rainby" color palette with semantic color mapping:
- Core Colors: Red, Orange, Yellow, Green, Cyan, Blue, Violet, Neutral
- Each Color: 50-950 scale following Tailwind conventions
- Semantic Mapping: Primary (cyan), Secondary (green), Accent (violet), etc.
Theme Structure
:root {
/* Light mode variables */
--color-background: var(--color-rainby-neutral-50);
--color-foreground: var(--color-rainby-neutral-950);
/* ... */
}
.dark {
/* Dark mode overrides */
--color-background: var(--color-rainby-neutral-950);
--color-foreground: var(--color-rainby-neutral-50);
/* ... */
}
Custom Utilities
frosted-glass: Backdrop blur effect for overlaysvstack: Vertical flex layout utilityhstack: Horizontal flex layout utility
Typography
- Sans: Lato (headings, UI text)
- Serif: EB Garamond (body text, content)
- Responsive: Tailwind responsive typography classes
⚙️ Configuration Files
Path Mapping (tsconfig.json)
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@*": ["./src/*"]
}
}
}
Astro Configuration
- Custom srcDir:
./src/www(Astro application code) - Integrations: MDX, Sitemap, React
- Vite Plugins: Tailwind CSS integration
Development Tools
- ESLint: Astro + React + TypeScript configurations
- Prettier: Astro + React + Tailwind plugins
- Storybook: React-Vite with Tailwind integration
🚀 Build & Deployment
Scripts
{
"dev": "astro dev", // Development server
"build": "astro build", // Production build
"preview": "astro preview", // Preview built site
"storybook": "storybook dev", // Component development
"check": "lint + typecheck" // Code quality checks
}
CI/CD Pipeline
- Semantic Release: Automated versioning and changelog generation
- GitHub Actions: Automated build and deployment to GitHub Pages
- Quality Gates: ESLint, TypeScript checking, build verification
Release Process
- Branches:
next(prerelease),release/*(stable releases) - Triggers: Release creation automatically deploys to production
- Versioning: Semantic versioning with conventional commits
📚 Content Management
Astro Content Collections
Content is managed through Astro's Content Collections system in the /content/ directory:
- Type Safety: Schema validation for frontmatter
- Organization: Content organized by collection type
- Processing: Automatic type generation and content APIs
Content Structure Example
content/
├── blog/ # Blog posts
│ ├── post-1.md
│ └── post-2.mdx
└── projects/ # Project showcases
├── project-1.md
└── project-2.md
🔧 Development Environment
Prerequisites
- Node.js: v20+
- pnpm: v9.12.3+
- TypeScript: v5.8+
Setup Commands
pnpm install # Install dependencies
pnpm dev # Start development server
pnpm storybook # Start component development
pnpm check # Run all quality checks
Development Guidelines
- Component-First: Develop components in Storybook before integration
- Type Safety: All public APIs must be properly typed with TSDoc
- Manual Components: Only use manually developed components in www
- Design System: Follow the established color palette and utilities
- Performance: Leverage Astro's static generation with minimal client-side JavaScript
- Testing: Write tests for utilities, hooks, and interactive components
🧪 Testing Strategy
Testing Framework
- Test Runner: Jest with TypeScript support
- Component Testing: React Testing Library for component interactions
- Utilities: Jest for unit testing of pure functions and hooks
- Coverage: HTML and LCOV reports for coverage tracking
Testing Patterns
Component Testing
import { render, screen } from '@/test/test-utils';
import { SomeComponent } from './SomeComponent';
/**
* Test suite for SomeComponent
*/
describe('SomeComponent', () => {
const defaultProps = {
title: 'Test Title',
};
it('renders correctly', () => {
render(<SomeComponent {...defaultProps} />);
expect(screen.getByText('Test Title')).toBeInTheDocument();
});
});
Utility Testing
import { someUtility } from './someUtility';
/**
* Test suite for utility functions
*/
describe('someUtility', () => {
it('handles expected inputs correctly', () => {
expect(someUtility('input')).toBe('expected');
});
it('handles edge cases', () => {
expect(someUtility('')).toBe('');
});
});
Testing Commands
pnpm test # Run all tests
pnpm test:watch # Run tests in watch mode
pnpm test:coverage # Run tests with coverage report
Testing Guidelines
- Focus on Public API: Test exported functionality, not implementation details
- Module-Level Testing: Test the main export of each module, not sub-dependencies
- Integration Over Unit: Test how complete modules work together
- Accessibility Testing: Use screen reader queries and accessibility matchers
- User-Centric: Test from the user's perspective (what they see and interact with)
Naming Conventions for Tests and Stories
- Test describe blocks: Use full component path format:
components/paper-kit/ComponentName/ComponentName - Storybook titles: Use full component path format:
components/paper-kit/ComponentName - Consistency: Both tests and stories should use the same path-based naming for organization
Example:
// In Component.test.tsx
describe('components/paper-kit/Button/Button', () => {
// tests
});
// In Component.stories.tsx
const meta: Meta<typeof Component> = {
title: 'components/paper-kit/Button',
component: Component,
};
This guide serves as the architectural foundation for the project. Focus on these stable patterns and structures rather than specific implementation details that may change over time.