commit e2eba5ec553b1e5ac73f8073e060a72f36201c01 Author: Ilia Mashkov Date: Fri Mar 6 23:03:45 2026 +0300 docs(CLAUDE.md): add code style section aligned with glyphdiff conventions diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5cfbd79 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,20 @@ +node_modules +.svelte-kit +build +dist +.env +.env.* +!.env.example +.git +.gitignore +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.DS_Store +Thumbs.db +.vscode +.idea +*.swp +*.swo +*~ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5e55adb --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +# Git worktrees +.worktrees + diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..28d1e67 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["svelte.svelte-vscode"] +} diff --git a/AGENT.md b/AGENT.md new file mode 100644 index 0000000..d2860b7 --- /dev/null +++ b/AGENT.md @@ -0,0 +1,666 @@ +# AGENT.md + +## Available MCP Tools + +You have access to **five powerful MCP tool sets** for comprehensive research and analysis: + +### 1. CodeGraphContext (CGC) - Local Code Analysis + +**Purpose**: Analyze, understand, and navigate local codebases through graph-based relationships. + +**When to Use**: +- Working with existing code in the local repository +- Understanding how code works together +- Finding where functions/classes are defined or used +- Analyzing the impact of code changes +- Identifying dead code or complex code +- Tracing execution flows across files +- Refactoring or maintaining code + +**Key Tools**: +- `codegraph_find_code` - Find functions, classes, or code by keywords +- `codegraph_analyze_code_relationships` - Analyze callers, callees, inheritance, dependencies +- `codegraph_watch_directory` - Monitor active projects for real-time updates +- `codegraph_add_code_to_graph` - Index code into the graph +- `codegraph_find_dead_code` - Find potentially unused functions +- `codegraph_calculate_cyclomatic_complexity` - Measure function complexity +- `codegraph_get_repository_stats` - Understand codebase size and structure +- `codegraph_list_indexed_repositories` - Check what code is available + +**Common Queries**: +- "Where is the `process_payment` function?" +- "What functions call `get_user_by_id`?" +- "If I change `calculate_tax`, what code will be affected?" +- "Show me the call chain from `main` to `process_data`" +- "Find dead or unused code in this project" +- "What's the most complex function in this codebase?" + +**Best Practices**: +- Check `codegraph_list_indexed_repositories` first to see what code is available +- Use `codegraph_watch_directory` for projects you're actively working on +- Start with `codegraph_find_code` for simple searches +- Use `codegraph_analyze_code_relationships` for deep analysis and impact assessment +- Check `codegraph_list_watched_paths` to see what's being monitored +- Background indexing jobs return a `job_id` - use `codegraph_check_job_status` to track progress + +--- + +### 2. Context7 - Up-to-Date Library Documentation + +**Purpose**: Get current, version-specific documentation and code examples for thousands of libraries and frameworks. + +**When to Use**: +- Needing up-to-date documentation for libraries or frameworks +- Looking for code examples and best practices +- Implementing new features using external libraries +- Learning APIs you haven't used before +- Wanting to avoid hallucinated APIs or outdated code +- Working with specific versions of libraries + +**Key Tools**: +- `context7_resolve-library-id` - Find the correct library ID (max 3 calls/question) +- `context7_query-docs` - Get documentation for a library (max 3 calls/question) + +**Usage Pattern**: +1. Call `context7_resolve-library-id` with library name and your task +2. Review results, select best match based on description, trust score, benchmark score +3. Call `context7_query-docs` with the library ID and your specific question + +**If user provides library ID in `/org/project` format**, you can skip step 1. + +**Common Queries**: +- "How do I set up authentication with Supabase?" +- "Show me examples of using Next.js middleware" +- "How to implement JWT auth with Express.js" +- "What's the best way to handle errors in React hooks?" + +**Best Practices**: +- **ALWAYS use `context7_resolve-library-id` first** unless user provides exact library ID +- **Be specific** in your query: "How to set up authentication with JWT" not "auth" +- **Max 3 calls**: Do not call these tools more than 3 times per question +- **Check scores**: Prioritize libraries with higher trust and benchmark scores +- **No secrets**: Never include API keys, passwords, credentials, or personal data in queries +- **Explain choice**: Briefly explain which library was selected when multiple matches exist + +--- + +### 3. Brave - Quick Web Search + +**Purpose**: Quick web searches for current information. + +**When to Use**: +- Needing fast, general web searches +- Looking for recent news or updates +- Finding current technology information +- Quick research without deep crawling + +**Key Tools**: +- `brave_brave_web_search` - General web search +- `brave_brave_local_search` - Local business/places search + +--- + +### 4. Tavily - Advanced Search & Extraction + +**Purpose**: Comprehensive web search with advanced capabilities including content extraction and crawling. + +**When to Use**: +- Needing in-depth research on technical topics +- Extracting content from multiple URLs +- Crawling entire websites for documentation +- Finding detailed implementation examples + +**Key Tools**: +- `tavily_tavily_search` - Advanced search with detailed results +- `tavily_tavily_extract` - Extract structured content from URLs +- `tavily_tavily_crawl` - Crawl websites systematically +- `tavily_tavily_map` - Map website structure + +--- + +### 5. BrightData - Web Scraping & Data Extraction + +**Purpose**: Comprehensive web scraping, structured data extraction, and browser automation. + +**When to Use**: +- Web scraping with anti-bot bypass +- Structured data extraction (clean markdown, tables, embedded content) +- Browser automation for complex, interactive websites +- Testing web applications + +**Key Tools**: +- `brightData_search_engine` - Web search using Google Search API +- `brightData_scrape_as_markdown` - Extract and convert to markdown +- `brightData_search_engine_batch` - Multi-query searches +- Playwright browser automation (11 tools) + +--- + +## Project Context + +**Project Name**: allmywork +**Purpose**: Portfolio web application +**Stack**: SvelteKit, Bun, Biome, Tailwind CSS, Feature Sliced Design (FSD) +**Deployment**: Self-hosted on VPS in Docker container + +--- + +## Bun Usage Guidelines + +**Always use `bun` commands, never `npm`**: + +- **Package management**: `bun install`, `bun add`, `bun remove` +- **Script execution**: `bun run ` +- **Running binaries**: Use `bunx ` instead of `npx ` +- **Test runner**: `bun test` or `bun run test` (Vitest via Bun) + +**Examples**: +```bash +# Install dependencies +bun install + +# Add dependency +bun add -D tailwindcss + +# Run script +bun run dev + +# Run binary (instead of npx) +bunx @biomejs/biome init + +# Run tests (Vitest via Bun) +bun run test +``` + +--- + +## Agent Roles & Responsibilities + +### UI/UX Agent + +**Responsibilities**: +- Design and implement UI components using Svelte 5 features (runes, snippets) +- Apply Tailwind CSS classes following the mixed approach pattern: + - Use utilities directly for one-off styles + - Create component classes for repeated patterns +- Ensure responsive design and accessibility (WCAG compliance) +- Implement micro-interactions and animations +- Work within Feature Sliced Design structure + +**Naming Conventions**: +- Components: PascalCase (e.g., `UserProfile.svelte`) +- Utilities: camelCase (e.g., `formatDate.ts`) + +**Comment Style**: +```svelte +/** + * Component description + */ + +``` + +**When to Use**: +- "Design/create component", "Add styling", "UI implementation", "Add animations" + +--- + +### Feature Developer Agent + +**Responsibilities**: +- Create and manage FSD slices (pages, features, entities, widgets, shared) +- Implement business logic and data flows +- Integrate UI components with backend functionality +- Follow FSD methodology for slice composition +- Ensure proper separation of concerns across layers + +**FSD Structure**: +``` +src/ +├── pages/ # Full pages +├── features/ # User interactions +├── entities/ # Business entities +├── widgets/ # Composed components +├── shared/ # Shared code +└── app/ # SvelteKit app layer (routes) +``` + +**When to Use**: +- "Create feature", "Implement business logic", "Add page", "Create slice" + +--- + +### Test Engineer Agent + +**Responsibilities**: +- Write unit tests using Vitest +- Write E2E tests using Playwright +- Test critical user flows +- Ensure test coverage meets quality standards +- Follow test-driven approach for new features + +**Testing Infrastructure**: +- Unit tests: Vitest (Vite-native) +- E2E tests: Playwright +- Target coverage: 80%+ for new code +- Write tests alongside implementation (TDD for new features) + +**When to Use**: +- "Write tests", "Test coverage", "Add E2E test", "Test flow" + +--- + +### Documentation Agent + +**Responsibilities**: +- Write inline code comments following "Less is more" principle +- Create API documentation +- Write README files and guides +- Document FSD structure and slice usage +- Explain core logic and caveats succinctly + +**Comment Patterns**: +```typescript +/** + * Function description explaining core logic + */ +export function processPayment(data: PaymentData): Promise { + // Validate input data + const validated = validateData(data); + + // Process payment with fallback + return processWithFallback(validated); +} +``` + +**When to Use**: +- "Write documentation", "Add comments", "Document API", "Create README" + +--- + +### Architecture Reviewer Agent + +**Responsibilities**: +- Review code for FSD compliance +- Identify architectural violations and anti-patterns +- Ensure proper layer separation +- Check for circular dependencies +- Verify component and slice integration patterns + +**Review Checklist**: +- [ ] FSD layer boundaries respected +- [ ] No circular dependencies +- [ ] Proper slice composition +- [ ] Shared code appropriately extracted +- [ ] Feature isolation maintained + +**When to Use**: +- "Review architecture", "Check FSD compliance", "Analyze code structure" + +--- + +## Key Workflows + +### 1. Creating a New Feature + +**Prerequisites**: None required + +**Steps**: + +1. **Create feature branch**: + ```bash + git checkout -b feature/featureName + ``` + +2. **Plan FSD slice**: + - Determine which layers needed (page, feature, entity, widget, shared) + - Identify dependencies between layers + - Plan data flow + +3. **Implement entities** (bottom-up): + ```bash + # Example: src/entities/user/ui/UserAvatar.svelte + # Example: src/entities/user/model/types.ts + ``` + +4. **Implement features**: + ```bash + # Example: src/features/auth/ui/LoginForm.svelte + # Example: src/features/auth/model/useAuth.ts + ``` + +5. **Compose widgets** (if needed): + ```bash + # Example: src/widgets/header/ui/Header.svelte + ``` + +6. **Create page**: + ```bash + # Example: src/pages/home/ui/HomePage.svelte + # Example: src/pages/home/index.ts + ``` + +7. **Integrate in app routes**: + ```bash + # src/app/routes/+page.svelte + import { HomePage } from '$lib/pages/home'; + ``` + +8. **Write tests**: + - Unit tests for business logic + - E2E tests for critical flows + +9. **Commit with conventional format**: + ```bash + git commit -m "feat(featureName): short yet capacitive description" + ``` + +**Output**: Complete feature implementation with FSD-compliant structure + +**Success Criteria**: +- Feature works as specified +- All tests pass +- FSD layer boundaries respected +- Code follows naming conventions +- Comments explain core logic + +--- + +### 2. Creating a New Component + +**Prerequisites**: None required + +**Steps**: + +1. **Identify layer**: + - Entity: Reusable business entity component + - Feature: User interaction component + - Widget: Composed UI component + - Page: Full page component + +2. **Create component file**: + ```bash + # src/features/myFeature/ui/MyComponent.svelte + ``` + +3. **Write Svelte 5 component**: + ```svelte + + +
+ +
+ ``` + +4. **Add Tailwind styles**: + - One-off styles: Use utilities directly + - Repeated patterns: Extract to shared classes + +5. **Export from index** (if applicable): + ```typescript + // src/features/myFeature/index.ts + export { MyComponent } from './ui/MyComponent.svelte'; + ``` + +6. **Write tests**: + ```typescript + // src/features/myFeature/ui/MyComponent.test.ts + ``` + +7. **Commit**: + ```bash + git commit -m "feat(myFeature): add MyComponent for ..." + ``` + +**Output**: Working component with tests + +**Success Criteria**: +- Component renders correctly +- Follows naming conventions +- Has appropriate tests +- Styles follow Tailwind pattern + +--- + +### 3. Running Tests + +**Prerequisites**: Project initialized with Vitest and Playwright + +**Steps**: + +1. **Run unit tests**: + ```bash + bun run test + # or + bun run test:watch + ``` + +2. **Run E2E tests**: + ```bash + bun run test:e2e + ``` + +3. **Run all tests**: + ```bash + bun run test:all + ``` + +4. **Check coverage**: + ```bash + bun run test:coverage + ``` + +**Output**: Test results with pass/fail status + +**Success Criteria**: +- All tests pass +- Coverage meets 80%+ for new code +- No flaky tests + +--- + +### 4. Building for Production + +**Prerequisites**: Development complete, tests passing + +**Steps**: + +1. **Run linting and formatting**: + ```bash + bun run lint + bun run format:check + ``` + +2. **Build application**: + ```bash + bun run build + ``` + +3. **Test build locally**: + ```bash + bun run preview + ``` + +4. **Build Docker image**: + ```bash + docker build -t allmywork:latest . + ``` + +5. **Test Docker container**: + ```bash + docker run -p 3000:3000 allmywork:latest + ``` + +**Output**: Production-ready build and Docker image + +**Success Criteria**: +- Build completes without errors +- Linting passes +- Docker image runs successfully +- Application accessible at expected port + +--- + +### 5. Deploying to VPS + +**Prerequisites**: Docker image built, VPS accessible + +**Steps**: + +1. **Transfer image to VPS**: + ```bash + docker save allmywork:latest | gzip | ssh user@vps "docker load" + ``` + +2. **Or build directly on VPS**: + ```bash + ssh user@vps "cd /path/to/project && bun run build && docker build -t allmywork:latest ." + ``` + +3. **Stop old container** (if exists): + ```bash + ssh user@vps "docker stop allmywork && docker rm allmywork" + ``` + +4. **Run new container**: + ```bash + ssh user@vps "docker run -d --name allmywork -p 3000:3000 --restart unless-stopped allmywork:latest" + ``` + +5. **Verify deployment**: + ```bash + curl https://your-domain.com + ``` + +**Output**: Running application on VPS + +**Success Criteria**: +- Container running +- Application accessible +- No errors in logs +- HTTPS configured (if applicable) + +--- + +## Code Conventions + +### Naming + +- **Components**: PascalCase (e.g., `UserProfile.svelte`) +- **Files/Functions**: camelCase (e.g., `formatDate.ts`, `getUserData`) +- **Types**: PascalCase (e.g., `UserData`, `PaymentResult`) + +### Comments + +**Multiline comments** (for documentation and types): +```typescript +/** + * Function description explaining core logic and caveats + */ +``` + +**One-line comments** (for short remarks): +```typescript +// Validate input before processing +``` + +**Rule**: "Less is more" - short yet capacitive, explaining core logic and caveats + +### Commit Messages + +**Format**: `prefix(featureName/fileName): short yet capacitive one-line description` + +**Prefixes**: +- `feat` - New feature +- `fix` - Bug fix +- `chore` - Maintenance tasks +- `refactor` - Code refactoring +- `docs` - Documentation changes +- `style` - Code style changes +- `test` - Test changes +- `perf` - Performance improvements +- `ci` - CI/CD changes +- `build` - Build system changes +- `revert` - Revert previous commit + +**Examples**: +- `feat(auth): add OAuth2 token refresh` +- `fix(payment): resolve memory leak in service` +- `docs(readme): update setup instructions` + +--- + +## Tool Selection Decision Tree + +``` +Need help with code or libraries? +│ +├─ LOCAL codebase analysis? +│ └─ YES → CodeGraphContext +│ ├─ Find something → codegraph_find_code +│ ├─ Understand relationships → codegraph_analyze_code_relationships +│ ├─ Check impact → find_callers, find_all_callers +│ └─ Code quality → find_dead_code, find_most_complex_functions +│ +└─ EXTERNAL library/framework? + └─ YES → Context7 + ├─ Have exact ID? → context7_query-docs + └─ Need to find ID? → resolve-library-id → query-docs + +Need web research? +│ +├─ Quick search? +│ └─ YES → Brave +│ └─ brave_brave_web_search +│ +├─ Advanced research/extraction? +│ └─ YES → Tavily +│ ├─ tavily_tavily_search +│ ├─ tavily_tavily_extract +│ └─ tavily_tavily_crawl +│ +└─ Scraping/testing? + └─ YES → BrightData + ├─ brightData_search_engine + ├─ brightData_scrape_as_markdown + └─ Playwright tools +``` + +--- + +## Deployment Strategy + +**Self-Hosted Docker Deployment**: +- Build: `bun run build` +- Docker image: `docker build -t allmywork:latest .` +- Run: `docker run -d -p 3000:3000 --restart unless-stopped allmywork:latest` +- Update: `docker stop && docker rm && docker run ...` + +--- + +## Git Workflow + +**Branching Strategy**: +- Feature branches: `feature/featureName` +- Bugfix branches: `fix/issueName` +- Main branches: `main`, `develop` + +**Commit Process**: +1. Write code +2. Run tests: `bun run test:all` +3. Run lint: `bun run lint` +4. Stage files: `git add` +5. Commit: `git commit -m "prefix(feature): description"` +6. Push: `git push` +7. Create PR + +**Use `skip orchestrator` when committing** to avoid extra agent overhead. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4ca5c6c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,473 @@ +## Project Configuration + +- **Language**: TypeScript +- **Package Manager**: Bun +- **Add-ons**: none + +--- + +# CLAUDE.md + +## Project Overview + +**Name**: allmywork +**Purpose**: Portfolio web application +**Stack**: SvelteKit + Bun + Biome + Tailwind CSS + Feature Sliced Design (FSD) +**Deployment**: Self-hosted on VPS with Docker + +--- + +## Bun Usage Guidelines + +**Always use `bun` commands, never `npm`**: + +- **Package management**: `bun install`, `bun add`, `bun remove` +- **Script execution**: `bun run ` +- **Running binaries**: Use `bunx ` instead of `npx ` +- **Test runner**: `bun test` or `bun run test` (Vitest via Bun) + +**Examples**: +```bash +# Install dependencies +bun install + +# Add dependency +bun add -D tailwindcss + +# Run script +bun run dev + +# Run binary (instead of npx) +bunx @biomejs/biome init + +# Run tests (Vitest via Bun) +bun run test +``` + +--- + +## Agent Fleet + +Use the following agents for different tasks: + +| Agent | When to Use | +|--------|-------------| +| `technical-architect` | Design architecture, API schemas, database design | +| `builder` | Write code, implement features, fix bugs, manage Git | +| `qa-reliability-engineer` | Test strategy, performance analysis, SLOs | +| `design-specialist` | UI/UX design, component design, styling | +| `knowledge-architect` | Documentation, research, explain concepts | +| `ops-engineer` | Deployment, CI/CD, infrastructure | +| `security` | Security review, vulnerability assessment | +| `workflow-orchestrator` | Multi-agent task coordination | + +--- + +## Technology Stack + +### Core Framework +- **SvelteKit**: Full-stack framework with file-based routing +- **Svelte 5**: Using runes (`$state`, `$derived`, `$effect`) and snippets +- **TypeScript**: Strict mode enabled + +### Runtime & Build +- **Bun**: Package manager and runtime +- **Vite**: Build tool (included with SvelteKit) + +### Code Quality +- **Biome**: Linting and formatting (ESLint/Prettier replacement) +- Configuration: Includes `.svelte` files in `files.includes` + +### Styling +- **Tailwind CSS**: Utility-first CSS +- Integration: Via PostCSS with `vitePreprocess({ style: true })` +- Pattern: Mixed approach - utilities for one-off, classes for repeated patterns + +### Architecture +- **Feature Sliced Design (FSD)**: + - `pages/` - Full pages + - `features/` - User interactions + - `entities/` - Business entities + - `widgets/` - Composed components + - `shared/` - Shared code + - `app/` - SvelteKit routes (configured via `svelte.config.ts`) + +### Testing +- **Vitest**: Unit testing (Vite-native) +- **Playwright**: E2E testing +- Coverage target: 80%+ for new code + +--- + +## Development Workflow + +### 1. Start Development +```bash +# Install dependencies +bun install + +# Start dev server +bun run dev -- --open + +# Watch for changes +bun run dev +``` + +**Note**: Always use `bun` commands, never `npm`. For running binaries (like npx), use `bunx` instead. + +### 2. Code Quality +```bash +# Lint code +bun run lint + +# Format code +bun run format + +# Check format without modifying +bun run format:check + +# Fix all issues +bun run lint:fix && bun run format +``` + +### 3. Testing +```bash +# Run unit tests +bun run test + +# Run E2E tests +bun run test:e2e + +# Run all tests +bun run test:all + +# Watch mode +bun run test:watch + +# Coverage report +bun run test:coverage +``` + +### 4. Build & Deploy +```bash +# Build for production +bun run build + +# Preview build +bun run preview + +# Build Docker image +docker build -t allmywork:latest . + +# Run Docker container +docker run -d -p 3000:3000 --restart unless-stopped allmywork:latest +``` + +--- + +## Code Conventions + +### Naming + +| Type | Convention | Example | +|------|------------|----------| +| Components | PascalCase | `UserProfile.svelte` | +| Functions/Files | camelCase | `formatDate.ts`, `getUserData` | +| Types/Interfaces | PascalCase | `UserData`, `PaymentResult` | +| Constants | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` | + +### Comment Style + +**Multiline** (for documentation and types): +```typescript +/** + * Process payment with retry logic + * Handles transient failures with exponential backoff + */ +``` + +**One-line** (for short remarks): +```typescript +// Validate input before processing +``` + +**Rule**: "Less is more" - short yet capacitive, explaining core logic and caveats + +### Commit Messages + +**Format**: `prefix(featureName/fileName): short yet capacitive one-line description` + +**Prefixes**: +- `feat` - New feature introduced +- `fix` - Bug fix +- `chore` - Maintenance tasks (no code changes) +- `refactor` - Code refactoring (no functional change) +- `docs` - Documentation changes +- `style` - Code style changes +- `test` - Test additions/corrections +- `perf` - Performance improvements +- `ci` - CI/CD changes +- `build` - Build system changes +- `revert` - Revert previous commit + +**Examples**: +``` +feat(auth): add OAuth2 token refresh flow +fix(payment): resolve memory leak in service +docs(readme): update deployment instructions +``` + +--- + +## FSD Structure + +``` +src/ +├── pages/ # Full pages (top-level FSD layer) +│ ├── home/ +│ │ ├── ui/ +│ │ │ └── HomePage.svelte +│ │ └── index.ts +│ └── about/ +├── features/ # User interactions +│ ├── auth/ +│ │ ├── ui/ +│ │ │ ├── LoginForm.svelte +│ │ │ └── RegisterForm.svelte +│ │ ├── model/ +│ │ │ ├── useAuth.ts +│ │ │ └── types.ts +│ │ └── index.ts +│ └── contact/ +├── entities/ # Business entities +│ ├── user/ +│ │ ├── ui/ +│ │ │ └── UserAvatar.svelte +│ │ ├── model/ +│ │ │ └── types.ts +│ │ └── index.ts +│ └── project/ +├── widgets/ # Composed UI components +│ ├── header/ +│ │ ├── ui/ +│ │ │ └── Header.svelte +│ │ └── index.ts +│ └── footer/ +├── shared/ # Shared code +│ ├── ui/ +│ │ ├── Button.svelte +│ │ └── Input.svelte +│ ├── config/ +│ │ └── site.ts +│ └── lib/ +│ └── api.ts +└── app/ # SvelteKit app layer + └── routes/ + ├── +layout.svelte + ├── +page.svelte + └── about/ + └── +page.svelte +``` + +### FSD Rules + +- **Bottom-up composition**: entities → features → widgets → pages → app +- **Public API**: Each slice exports via `index.ts` +- **No upward dependencies**: Lower layers cannot import from higher layers +- **Shared**: Only use `shared/` for truly cross-cutting concerns + +--- + +## Key Workflows + +### Creating a Feature + +1. Create feature branch: `git checkout -b feature/featureName` +2. Plan FSD slice structure (pages → features → entities) +3. Implement entities (bottom layer) +4. Implement features with business logic +5. Compose widgets (if needed) +6. Create page +7. Integrate in `app/routes/+page.svelte` +8. Write tests (unit + E2E) +9. Commit: `git commit -m "feat(feature): description"` +10. Create PR + +### Running Tests Before Commit + +```bash +# Always run before committing +bun run lint # Check code quality +bun run test:all # Run all tests +bun run format:check # Check formatting + +# Fix issues +bun run lint:fix +bun run format +``` + +### Building for Deployment + +1. Run tests: `bun run test:all` +2. Lint code: `bun run lint` +3. Build: `bun run build` +4. Build Docker image: `docker build -t allmywork:latest .` +5. Test Docker container: `docker run -p 3000:3000 allmywork:latest` +6. Deploy to VPS (see docs/deployment.md) + +--- + +## Important Notes + +### Svelte 5 Features +- Use runes for reactivity: `$state`, `$derived`, `$effect` +- Use snippets for reusable logic +- TypeScript strict mode enabled + +### Biome Configuration +- Lints and formats `.svelte` files +- Initialize with `bunx @biomejs/biome init` +- Run `bun run lint` and `bun run format` before commits +- Configuration in `biome.json` + +### Tailwind Usage +- Direct utilities: `class="p-4 bg-blue-500 text-white"` (one-off) +- Component classes: Repeated patterns extracted to shared components +- Design tokens in `tailwind.config.ts` + +### Deployment +- Self-hosted on VPS with Docker +- No Vercel/Netlify integration +- Docker image contains full application +- Use `docker restart allmywork` for updates + +### Git Workflow +- Feature branches with PRs +- Use `skip orchestrator` when committing +- Conventional commit format enforced +- Self-review before merging + +--- + +## Documentation + +**Project Docs**: `/docs/` folder contains detailed guides +- `docs/development.md` - Development workflow +- `docs/testing.md` - Testing strategy +- `docs/deployment.md` - Deployment procedures +- `docs/fsd.md` - FSD architecture guide + +**External Resources**: +- SvelteKit: https://kit.svelte.dev/docs +- Svelte 5: https://svelte.dev/docs/runes +- Bun: https://bun.sh/docs +- Biome: https://biomejs.dev +- Tailwind: https://tailwindcss.com/docs +- FSD: https://feature-sliced.design + +--- + +## Troubleshooting + +### Build Issues +```bash +# Clear build cache +rm -rf .svelte-kit +rm -rf node_modules/.vite + +# Rebuild +bun install +bun run build +``` + +### Biome Not Formatting Svelte Files +- Check `biome.json` includes `.svelte` in `files.includes` +- Ensure Biome extension is installed in VS Code + +### Tailwind Classes Not Applying +- Check `src/app.css` includes Tailwind directives +- Verify `tailwind.config.ts` content paths include `src/**/*.{svelte,ts,js}` +- Ensure PostCSS is enabled in `svelte.config.ts` + +### FSD Import Errors +- Check slice `index.ts` exports +- Verify no upward dependencies +- Ensure proper path aliases configured + +--- + +## Getting Help + +**For code implementation**: Use `builder` agent +**For architecture decisions**: Use `technical-architect` agent +**For testing issues**: Use `qa-reliability-engineer` agent +**For UI/UX design**: Use `design-specialist` agent +**For documentation**: Use `knowledge-architect` agent +**For deployment**: Use `ops-engineer` agent +**For security review**: Use `security` agent + +**Multi-agent tasks**: Use `workflow-orchestrator` agent + +--- + +## Code Style + +### Formatting (Biome) +- Indent: tabs +- Quote style: double quotes for JS/TS, double quotes for Svelte attributes +- Line width: 120 characters +- Semicolons: always +- Trailing commas: all + +### TypeScript +- Strict mode: on +- Prefer `interface` over `type` for object shapes +- Always type component props via an `interface Props { ... }` block +- Use `$props()` rune — never `export let` +- Use `$derived()` and `$effect()` — never Svelte 4 `$:` syntax +- Use `$state()` for all reactive primitives + +### Component Conventions +- File name: PascalCase (`Button.svelte`, `ProjectCard.svelte`) +- Store files: camelCase with `.svelte.ts` suffix (`sidebarStore.svelte.ts`) +- Story files: `.stories.svelte` suffix (Storybook CSF with Svelte addon) +- Test files: `.test.ts` for unit, `.svelte.test.ts` for component tests +- Each slice exports a public API via `index.ts` + +### Svelte 5 Patterns +```svelte + + + +``` + +### Comments +- Multiline JSDoc (`/** ... */`) for exported functions and component description headers +- Single-line (`//`) for inline logic remarks +- Principle: "less is more" — explain the *why*, not the *what* + +### Import Order (Biome organizeImports) +1. Svelte/SvelteKit built-ins (`svelte`, `$app/...`) +2. FSD layer aliases (`$shared/...`, `$entities/...`, etc.) +3. Relative imports (`./`, `../`) + +### Commit Messages +Format: `prefix(scope): short description` +Prefixes: `feat`, `fix`, `chore`, `refactor`, `docs`, `style`, `test`, `perf`, `ci`, `build`, `revert` +Example: `feat(shared/ui): add Button component with brutalist variants` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1482c10 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +FROM oven/bun:1-alpine AS base + +WORKDIR /app + +FROM base AS deps +COPY package.json package-lock.json* ./ +RUN bun install --frozen-lockfile + +FROM base AS builder +COPY --from=deps /app/node_modules ./node_modules +COPY . . +RUN bun install --frozen-lockfile +RUN bun run build + +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV production + +RUN addgroup --system --gid 1001 bun +RUN adduser --system --uid 1001 bun + +COPY --from=builder /app/package.json ./ +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/build ./ + +USER bun + +EXPOSE 3000 + +CMD ["bun", "run", "index.js"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..eeb4a52 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# sv + +Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```sh +# create a new project +npx sv create my-app +``` + +To recreate this project with the same configuration: + +```sh +# recreate this project +npx sv@0.12.4 create --template minimal --types ts --install npm . +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```sh +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```sh +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..fc9c876 --- /dev/null +++ b/biome.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.5/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "includes": ["*.svelte", "*.ts", "*.js", "*.json", "*.css"] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/docs/plans/2026-03-06-portfolio-foundation.md b/docs/plans/2026-03-06-portfolio-foundation.md new file mode 100644 index 0000000..433ec08 --- /dev/null +++ b/docs/plans/2026-03-06-portfolio-foundation.md @@ -0,0 +1,1975 @@ +# Portfolio Foundation Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Bootstrap the allmywork SvelteKit portfolio with a CLAUDE.md, a brutalist design system extracted from the Figma AI prototype, shared/ui components converted from React to Svelte 5, and Storybook stories for each component. + +**Architecture:** Tailwind CSS v4 custom properties carry the design tokens (colors, typography, spacing, shadows). All UI primitives live in `src/shared/ui/`, each as a PascalCase folder with `ComponentName.svelte`, `types.ts`, `index.ts`, and `ComponentName.stories.svelte`. The FSD directory structure is already in place; this plan fills in the foundation layer only. + +**Tech Stack:** SvelteKit 2 + Svelte 5 (runes), Tailwind CSS v4, Biome (lint/format), Vitest (unit tests), Storybook 10 with `@storybook/addon-svelte-csf`, Bun as package manager and runtime. + +--- + +## Task 0: Write CLAUDE.md for the project + +**Files:** +- Modify: `/home/ilia/Documents/Projects/allmywork/CLAUDE.md` + +The existing CLAUDE.md covers project purpose and tooling. We need to add the code-style section that mirrors glyphdiff's conventions. + +**Step 1: Read the existing CLAUDE.md** + +Read `/home/ilia/Documents/Projects/allmywork/CLAUDE.md` in full. + +**Step 2: Append the Code Style section** + +Add the following section to the end of the existing CLAUDE.md (before any trailing lines): + +```markdown +--- + +## Code Style + +### Formatting (Biome) +- Indent: tabs +- Quote style: double quotes for JS/TS, double quotes for Svelte attributes +- Line width: 120 characters +- Semicolons: always +- Trailing commas: all + +### TypeScript +- Strict mode: on +- Prefer `interface` over `type` for object shapes +- Always type component props via an `interface Props { ... }` block +- Use `$props()` rune — never `export let` +- Use `$derived()` and `$effect()` — never Svelte 4 `$:` syntax +- Use `$state()` for all reactive primitives + +### Component Conventions +- File name: PascalCase (`Button.svelte`, `ProjectCard.svelte`) +- Store files: camelCase with `.svelte.ts` suffix (`sidebarStore.svelte.ts`) +- Story files: `.stories.svelte` suffix (Storybook CSF with Svelte addon) +- Test files: `.test.ts` for unit, `.svelte.test.ts` for component tests +- Each slice exports a public API via `index.ts` + +### Svelte 5 Patterns +```svelte + + + +``` + +### Comments +- Multiline JSDoc (`/** ... */`) for exported functions and component description headers +- Single-line (`//`) for inline logic remarks +- Principle: "less is more" — explain the *why*, not the *what* + +### Import Order (Biome organizeImports) +1. Svelte/SvelteKit built-ins (`svelte`, `$app/...`) +2. FSD layer aliases (`$shared/...`, `$entities/...`, etc.) +3. Relative imports (`./`, `../`) + +### Commit Messages +Format: `prefix(scope): short description` +Prefixes: `feat`, `fix`, `chore`, `refactor`, `docs`, `style`, `test`, `perf`, `ci`, `build`, `revert` +Example: `feat(shared/ui): add Button component with brutalist variants` +``` + +**Step 3: Verify the file looks correct** + +Read the first and last 20 lines of the updated CLAUDE.md to confirm the edit landed cleanly. + +**Step 4: Commit** + +```bash +cd /home/ilia/Documents/Projects/allmywork +git add CLAUDE.md +git commit -m "docs(CLAUDE.md): add code style section aligned with glyphdiff conventions" +``` + +--- + +## Task 1: Install Storybook and required dev dependencies + +The project currently has no Storybook. This task wires it up before any stories are written. + +**Files:** +- Modify: `/home/ilia/Documents/Projects/allmywork/package.json` (via bun add) +- Create: `/home/ilia/Documents/Projects/allmywork/.storybook/main.ts` +- Create: `/home/ilia/Documents/Projects/allmywork/.storybook/preview.ts` + +**Step 1: Install Storybook and addons** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bunx storybook@latest init --skip-install +``` + +Expected output: Storybook scaffolds `.storybook/` and adds scripts to package.json. Answer "yes" to any prompts. + +**Step 2: Verify generated .storybook/main.ts** + +Read `/home/ilia/Documents/Projects/allmywork/.storybook/main.ts`. +It should reference `@storybook/svelte-vite` as the framework. + +If the `init` command did not produce a valid Svelte-Vite config, write `.storybook/main.ts` manually: + +```typescript +import type { StorybookConfig } from '@storybook/svelte-vite'; + +const config: StorybookConfig = { + stories: ['../src/**/*.stories.svelte'], + addons: [ + '@storybook/addon-svelte-csf', + '@storybook/addon-a11y', + '@storybook/addon-docs', + ], + framework: { + name: '@storybook/svelte-vite', + options: {}, + }, +}; + +export default config; +``` + +**Step 3: Install missing addons if needed** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun add -D @storybook/addon-svelte-csf @storybook/addon-a11y @storybook/addon-docs +``` + +**Step 4: Add Storybook scripts to package.json (if not already present)** + +Open `/home/ilia/Documents/Projects/allmywork/package.json` and confirm the `scripts` section contains: + +```json +"storybook": "storybook dev -p 6006", +"build-storybook": "storybook build" +``` + +If missing, add them. + +**Step 5: Run Storybook to confirm it starts** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run storybook & +``` + +Expected: Storybook starts on http://localhost:6006 with no fatal errors. +Kill it after confirming: `kill %1` or `Ctrl+C`. + +**Step 6: Commit** + +```bash +cd /home/ilia/Documents/Projects/allmywork +git add .storybook package.json +git commit -m "chore(storybook): install and configure Storybook 10 with svelte-vite" +``` + +--- + +## Task 2: Set up the design system — CSS custom properties and Tailwind v4 tokens + +Extract the design tokens from the Figma prototype's `theme.css` into the project's `app.css` and register them as Tailwind v4 theme values. + +**Files:** +- Modify: `/home/ilia/Documents/Projects/allmywork/src/app/styles/app.css` + +**Reference:** `/home/ilia/Downloads/High-End Portfolio Design System(1)/src/styles/theme.css` and `fonts.css` + +**Step 1: Open app.css and replace its contents** + +The file currently only contains `@import "tailwindcss";`. Replace it with the following: + +```css +@import "tailwindcss"; + +/* ============================================================ + FONTS + ============================================================ */ +@import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght,SOFT,WONK@0,9..144,100..900,0..100,0..1;1,9..144,100..900,0..100,0..1&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,100..900;1,100..900&display=swap'); + +/* ============================================================ + DESIGN TOKENS + ============================================================ */ +:root { + /* Typography scale — Augmented Fourth (×1.414) */ + --font-size-base: 16px; + --text-xs: 0.707rem; /* 11.31px */ + --text-sm: 0.840rem; /* 13.44px */ + --text-base: 1rem; /* 16px */ + --text-lg: 1.414rem; /* 22.62px */ + --text-xl: 2rem; /* 32px */ + --text-2xl: 2.828rem; /* 45.25px */ + --text-3xl: 4rem; /* 64px */ + --text-4xl: 5.657rem; /* 90.51px */ + --text-5xl: 8rem; /* 128px */ + + /* Font families */ + --font-heading: 'Fraunces', serif; + --font-body: 'Public Sans', sans-serif; + + /* Font weights */ + --font-weight-heading: 900; + --font-weight-body: 600; + --font-weight-normal: 400; + + /* Line heights */ + --line-height-tight: 1.2; + --line-height-normal: 1.5; + + /* Fraunces variable font axes */ + --fraunces-wonk: 1; + --fraunces-soft: 0; + + /* Palette — Sanzo Wada inspired */ + --ochre-clay: #D9B48F; + --slate-indigo: #3B4A59; + --burnt-oxide: #A64B35; + --carbon-black: #121212; + + /* Semantic color aliases */ + --background: var(--ochre-clay); + --foreground: var(--carbon-black); + --card: var(--ochre-clay); + --card-foreground: var(--carbon-black); + --primary: var(--burnt-oxide); + --primary-foreground: var(--ochre-clay); + --secondary: var(--slate-indigo); + --secondary-foreground: var(--ochre-clay); + --muted: var(--slate-indigo); + --muted-foreground: var(--ochre-clay); + --accent: var(--burnt-oxide); + --accent-foreground: var(--ochre-clay); + --destructive: #d4183d; + --destructive-foreground:#ffffff; + --border: var(--carbon-black); + --ring: var(--carbon-black); + + /* Spacing — 8pt linear scale */ + --space-1: 0.5rem; /* 8px */ + --space-2: 1rem; /* 16px */ + --space-3: 1.5rem; /* 24px */ + --space-4: 2rem; /* 32px */ + --space-5: 2.5rem; /* 40px */ + --space-6: 3rem; /* 48px */ + --space-8: 4rem; /* 64px */ + --space-10: 5rem; /* 80px */ + --space-12: 6rem; /* 96px */ + --space-16: 8rem; /* 128px */ + --space-20: 10rem; /* 160px */ + + /* Borders */ + --border-width: 3px; + --radius: 0px; + + /* Brutalist hard shadows */ + --shadow-brutal-sm: 4px 4px 0 var(--carbon-black); + --shadow-brutal: 8px 8px 0 var(--carbon-black); + --shadow-brutal-lg: 12px 12px 0 var(--carbon-black); +} + +/* ============================================================ + TAILWIND v4 THEME REGISTRATION + ============================================================ */ +@theme inline { + /* Colors */ + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-ring: var(--ring); + + /* Palette direct access */ + --color-ochre-clay: var(--ochre-clay); + --color-slate-indigo: var(--slate-indigo); + --color-burnt-oxide: var(--burnt-oxide); + --color-carbon-black: var(--carbon-black); + + /* Border radius (zero for brutalist aesthetic) */ + --radius-sm: var(--radius); + --radius-md: var(--radius); + --radius-lg: var(--radius); + + /* Box shadows */ + --shadow-brutal-sm: var(--shadow-brutal-sm); + --shadow-brutal: var(--shadow-brutal); + --shadow-brutal-lg: var(--shadow-brutal-lg); + + /* Font families */ + --font-heading: var(--font-heading); + --font-body: var(--font-body); +} + +/* ============================================================ + BASE LAYER — GLOBAL ELEMENT DEFAULTS + ============================================================ */ +@layer base { + html { + font-size: var(--font-size-base); + } + + body { + background-color: var(--background); + color: var(--foreground); + font-family: var(--font-body); + font-weight: var(--font-weight-body); + line-height: var(--line-height-tight); + overflow-x: hidden; + position: relative; + } + + /* Paper grain texture overlay */ + body::before { + content: ''; + position: fixed; + inset: 0; + background-image: + repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,0,0,.02) 2px, rgba(0,0,0,.02) 4px), + repeating-linear-gradient(90deg, transparent, transparent 2px, rgba(0,0,0,.02) 2px, rgba(0,0,0,.02) 4px); + opacity: .4; + pointer-events: none; + z-index: 1; + } + + /* Ensure page content renders above texture */ + body > * { + position: relative; + z-index: 2; + } + + h1, .h1 { font-size: var(--text-4xl); } + h2, .h2 { font-size: var(--text-3xl); } + h3, .h3 { font-size: var(--text-2xl); } + h4, .h4 { font-size: var(--text-xl); } + h5, .h5 { font-size: var(--text-lg); } + + h1, h2, h3, h4, h5, + .h1, .h2, .h3, .h4, .h5 { + font-family: var(--font-heading); + font-weight: var(--font-weight-heading); + line-height: var(--line-height-tight); + font-variation-settings: 'WONK' var(--fraunces-wonk), 'SOFT' var(--fraunces-soft); + color: var(--carbon-black); + margin: 0; + } + + p { + font-family: var(--font-body); + font-size: var(--text-base); + font-weight: var(--font-weight-body); + line-height: var(--line-height-tight); + color: var(--carbon-black); + margin: 0; + } + + label { + font-family: var(--font-body); + font-size: var(--text-sm); + font-weight: var(--font-weight-body); + text-transform: uppercase; + letter-spacing: .05em; + line-height: var(--line-height-tight); + } + + button { + font-family: var(--font-body); + font-size: var(--text-base); + font-weight: var(--font-weight-body); + line-height: var(--line-height-tight); + cursor: pointer; + } + + input, textarea, select { + font-family: var(--font-body); + font-size: var(--text-base); + font-weight: var(--font-weight-body); + line-height: var(--line-height-tight); + } + + blockquote { + font-family: var(--font-heading); + font-size: var(--text-xl); + font-weight: var(--font-weight-heading); + font-variation-settings: 'WONK' var(--fraunces-wonk), 'SOFT' var(--fraunces-soft); + line-height: var(--line-height-tight); + border-left: var(--border-width) solid var(--carbon-black); + padding-left: var(--space-4); + margin: var(--space-6) 0; + } + + a { + color: var(--burnt-oxide); + text-decoration: none; + border-bottom: 2px solid var(--carbon-black); + transition: border-bottom-width .15s ease; + } + + a:hover { + border-bottom-width: 4px; + } +} + +/* ============================================================ + BRUTALIST UTILITY CLASSES + ============================================================ */ +.brutal-border { border: var(--border-width) solid var(--carbon-black); } +.brutal-border-top { border-top: var(--border-width) solid var(--carbon-black); } +.brutal-border-bottom { border-bottom: var(--border-width) solid var(--carbon-black); } +.brutal-border-left { border-left: var(--border-width) solid var(--carbon-black); } +.brutal-border-right { border-right: var(--border-width) solid var(--carbon-black); } + +.brutal-shadow { box-shadow: var(--shadow-brutal); } +.brutal-shadow-sm { box-shadow: var(--shadow-brutal-sm); } +.brutal-shadow-lg { box-shadow: var(--shadow-brutal-lg); } + +/* ============================================================ + GRID UTILITIES + ============================================================ */ +.grid-muller { + display: grid; + gap: var(--space-3); + grid-template-columns: repeat(4, 1fr); +} + +@media (min-width: 1024px) { + .grid-muller { + grid-template-columns: repeat(12, 1fr); + } +} + +/* ============================================================ + ANIMATION + ============================================================ */ +@keyframes fadeInUp { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +} + +.animate-fade-in-up { + animation: fadeInUp .5s cubic-bezier(.4, 0, .2, 1); +} +``` + +**Step 2: Verify dev server still compiles** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run dev & +``` + +Expected: No CSS errors in the terminal. Visit http://localhost:5173 — the page background should be ochre-clay (#D9B48F). +Kill dev server: `kill %1` + +**Step 3: Commit** + +```bash +cd /home/ilia/Documents/Projects/allmywork +git add src/app/styles/app.css +git commit -m "feat(design-system): add brutalist design tokens and Tailwind v4 theme from Figma prototype" +``` + +--- + +## Task 3: Add the `cn` utility to shared/lib + +Many components will use `cn` (clsx + tailwind-merge) to compose class names. This task adds the helper before any component uses it. + +**Files:** +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/lib/cn.ts` +- Modify: `/home/ilia/Documents/Projects/allmywork/src/shared/lib/index.ts` + +**Step 1: Install clsx and tailwind-merge** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun add clsx tailwind-merge +``` + +Expected: Both packages appear in `package.json` under `dependencies`. + +**Step 2: Write failing test** + +Create `/home/ilia/Documents/Projects/allmywork/src/shared/lib/cn.test.ts`: + +```typescript +import { describe, expect, it } from 'vitest'; +import { cn } from './cn'; + +describe('cn', () => { + it('merges class strings', () => { + expect(cn('foo', 'bar')).toBe('foo bar'); + }); + + it('deduplicates conflicting Tailwind classes (last wins)', () => { + expect(cn('p-4', 'p-8')).toBe('p-8'); + }); + + it('ignores falsy values', () => { + expect(cn('foo', false, undefined, null, 'bar')).toBe('foo bar'); + }); +}); +``` + +**Step 3: Run test to confirm it fails** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run test src/shared/lib/cn.test.ts +``` + +Expected: FAIL — `Cannot find module './cn'` + +**Step 4: Implement cn.ts** + +```typescript +import { clsx, type ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +/** Merge Tailwind classes without conflicts. Later classes win on collision. */ +export function cn(...inputs: ClassValue[]): string { + return twMerge(clsx(inputs)); +} +``` + +**Step 5: Run test to confirm it passes** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run test src/shared/lib/cn.test.ts +``` + +Expected: PASS (3 tests) + +**Step 6: Export from shared/lib/index.ts** + +Open `/home/ilia/Documents/Projects/allmywork/src/shared/lib/index.ts` and add: + +```typescript +export { cn } from './cn'; +``` + +**Step 7: Commit** + +```bash +cd /home/ilia/Documents/Projects/allmywork +git add src/shared/lib/cn.ts src/shared/lib/cn.test.ts src/shared/lib/index.ts package.json +git commit -m "feat(shared/lib): add cn utility (clsx + tailwind-merge)" +``` + +--- + +## Task 4: Implement `Badge` component + +**Files:** +- Modify: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/` — replace flat files with folder-per-component layout +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Badge/types.ts` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Badge/Badge.svelte` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Badge/index.ts` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Badge/Badge.stories.svelte` + +**Reference prototype:** `/home/ilia/Downloads/High-End Portfolio Design System(1)/src/app/components/Badge.tsx` + +**Step 1: Create types.ts** + +```typescript +export type BadgeVariant = 'default' | 'primary' | 'secondary' | 'outline'; +``` + +**Step 2: Write failing component test** + +Create `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Badge/Badge.test.ts`: + +```typescript +import { render } from '@testing-library/svelte'; +import { describe, expect, it } from 'vitest'; +import Badge from './Badge.svelte'; + +describe('Badge', () => { + it('renders children text', () => { + const { getByText } = render(Badge, { props: { children: () => 'hello' } }); + expect(getByText('hello')).toBeTruthy(); + }); + + it('applies default variant class', () => { + const { container } = render(Badge, { props: { variant: 'default' } }); + expect(container.querySelector('span')?.className).toContain('bg-carbon-black'); + }); +}); +``` + +**Step 3: Run test to confirm failure** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run test src/shared/ui/Badge/Badge.test.ts +``` + +Expected: FAIL — component file not found. + +**Step 4: Implement Badge.svelte** + +```svelte + + + + + {#if children} + {@render children()} + {/if} + +``` + +**Step 5: Create index.ts** + +```typescript +export { default as Badge } from './Badge.svelte'; +export type { BadgeVariant } from './types'; +``` + +**Step 6: Run test to confirm it passes** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run test src/shared/ui/Badge/Badge.test.ts +``` + +Expected: PASS + +**Step 7: Write Badge.stories.svelte** + +```svelte + + + + {#snippet template(args)} + Tag + {/snippet} + + + + {#snippet template(args)} + Primary + {/snippet} + + + + {#snippet template(args)} + Secondary + {/snippet} + + + + {#snippet template(args)} + Outline + {/snippet} + + + + {#snippet template()} +
+ Default + Primary + Secondary + Outline +
+ {/snippet} +
+``` + +**Step 8: Commit** + +```bash +cd /home/ilia/Documents/Projects/allmywork +git add src/shared/ui/Badge/ +git commit -m "feat(shared/ui): add Badge component with brutalist variants and Storybook story" +``` + +--- + +## Task 5: Implement `Button` component (replacing the placeholder) + +**Files:** +- Delete: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Button.svelte` (placeholder) +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Button/types.ts` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Button/Button.svelte` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Button/index.ts` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Button/Button.stories.svelte` +- Modify: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/index.ts` + +**Reference prototype:** `/home/ilia/Downloads/High-End Portfolio Design System(1)/src/app/components/Button.tsx` + +**Step 1: Create types.ts** + +```typescript +export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost'; +export type ButtonSize = 'sm' | 'md' | 'lg'; +``` + +**Step 2: Write failing test** + +Create `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Button/Button.test.ts`: + +```typescript +import { render } from '@testing-library/svelte'; +import { describe, expect, it } from 'vitest'; +import Button from './Button.svelte'; + +describe('Button', () => { + it('renders children', () => { + const { getByText } = render(Button, { props: { children: () => 'Click me' } }); + expect(getByText('Click me')).toBeTruthy(); + }); + + it('is disabled when disabled prop is true', () => { + const { container } = render(Button, { props: { disabled: true } }); + expect(container.querySelector('button')?.disabled).toBe(true); + }); + + it('applies primary variant styles', () => { + const { container } = render(Button, { props: { variant: 'primary' } }); + expect(container.querySelector('button')?.className).toContain('bg-burnt-oxide'); + }); +}); +``` + +**Step 3: Run test to confirm failure** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run test src/shared/ui/Button/Button.test.ts +``` + +Expected: FAIL + +**Step 4: Implement Button.svelte** + +```svelte + + + + +``` + +**Step 5: Create index.ts** + +```typescript +export { default as Button } from './Button.svelte'; +export type { ButtonVariant, ButtonSize } from './types'; +``` + +**Step 6: Run test to confirm pass** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run test src/shared/ui/Button/Button.test.ts +``` + +Expected: PASS (3 tests) + +**Step 7: Update shared/ui/index.ts** + +Open `/home/ilia/Documents/Projects/allmywork/src/shared/ui/index.ts`. Replace the existing content that referenced the flat `Button.svelte` and `Input.svelte` with: + +```typescript +export * from './Badge'; +export * from './Button'; +``` + +(More exports will be appended as components are added in subsequent tasks.) + +**Step 8: Remove the placeholder flat files** + +```bash +rm /home/ilia/Documents/Projects/allmywork/src/shared/ui/Button.svelte +rm /home/ilia/Documents/Projects/allmywork/src/shared/ui/Input.svelte +``` + +Note: `Input.svelte` is removed here because Task 8 will add a proper `Input/` folder. + +**Step 9: Fix import in HomePage.svelte** + +Open `/home/ilia/Documents/Projects/allmywork/src/pages/home/ui/HomePage.svelte`. + +The existing import `import { Button } from '$shared/ui';` should still work because index.ts now re-exports Button. Verify the dev server compiles: + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run dev & +# visit http://localhost:5173 — page should load +kill %1 +``` + +**Step 10: Write Button.stories.svelte** + +```svelte + + + + {#snippet template(args)} + + {/snippet} + + + + {#snippet template(args)} + + {/snippet} + + + + {#snippet template(args)} + + {/snippet} + + + + {#snippet template(args)} + + {/snippet} + + + + {#snippet template()} +
+ + + +
+ {/snippet} +
+ + + {#snippet template()} + + {/snippet} + +``` + +**Step 11: Commit** + +```bash +cd /home/ilia/Documents/Projects/allmywork +git add src/shared/ui/Button/ src/shared/ui/index.ts +git commit -m "feat(shared/ui): implement brutalist Button component replacing placeholder" +``` + +--- + +## Task 6: Implement `Card` component + +**Files:** +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Card/types.ts` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Card/Card.svelte` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Card/index.ts` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Card/Card.stories.svelte` + +**Reference prototype:** `/home/ilia/Downloads/High-End Portfolio Design System(1)/src/app/components/Card.tsx` + +**Step 1: Create types.ts** + +```typescript +export type CardBackground = 'ochre' | 'slate' | 'white'; +``` + +**Step 2: Write failing test** + +Create `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Card/Card.test.ts`: + +```typescript +import { render } from '@testing-library/svelte'; +import { describe, expect, it } from 'vitest'; +import Card from './Card.svelte'; + +describe('Card', () => { + it('renders with default ochre background', () => { + const { container } = render(Card); + expect(container.querySelector('div')?.className).toContain('bg-ochre-clay'); + }); + + it('renders with slate background', () => { + const { container } = render(Card, { props: { background: 'slate' } }); + expect(container.querySelector('div')?.className).toContain('bg-slate-indigo'); + }); + + it('renders with white background', () => { + const { container } = render(Card, { props: { background: 'white' } }); + expect(container.querySelector('div')?.className).toContain('bg-white'); + }); +}); +``` + +**Step 3: Run test to confirm failure** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run test src/shared/ui/Card/Card.test.ts +``` + +Expected: FAIL + +**Step 4: Implement Card.svelte** + +```svelte + + + +
+ {#if children} + {@render children()} + {/if} +
+``` + +**Step 5: Create index.ts** + +```typescript +export { default as Card } from './Card.svelte'; +export type { CardBackground } from './types'; +``` + +**Step 6: Run test to confirm pass** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run test src/shared/ui/Card/Card.test.ts +``` + +Expected: PASS (3 tests) + +**Step 7: Update shared/ui/index.ts** — append: + +```typescript +export * from './Card'; +``` + +**Step 8: Write Card.stories.svelte** + +```svelte + + + + {#snippet template(args)} + Card content goes here + {/snippet} + + + + {#snippet template(args)} + Card content goes here + {/snippet} + + + + {#snippet template(args)} + Card content goes here + {/snippet} + + + + {#snippet template(args)} + +
Header
+
Body
+
+ {/snippet} +
+``` + +**Step 9: Commit** + +```bash +cd /home/ilia/Documents/Projects/allmywork +git add src/shared/ui/Card/ src/shared/ui/index.ts +git commit -m "feat(shared/ui): add Card component with background variants and Storybook story" +``` + +--- + +## Task 7: Implement `Section` and `Container` components + +**Files:** +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Section/types.ts` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Section/Section.svelte` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Section/Container.svelte` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Section/index.ts` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Section/Section.stories.svelte` + +**Reference prototype:** `/home/ilia/Downloads/High-End Portfolio Design System(1)/src/app/components/Section.tsx` + +**Step 1: Create types.ts** + +```typescript +export type SectionBackground = 'ochre' | 'slate' | 'white'; +export type ContainerSize = 'default' | 'wide' | 'ultra-wide'; +``` + +**Step 2: Write failing test** + +Create `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Section/Section.test.ts`: + +```typescript +import { render } from '@testing-library/svelte'; +import { describe, expect, it } from 'vitest'; +import Section from './Section.svelte'; + +describe('Section', () => { + it('renders a
element', () => { + const { container } = render(Section); + expect(container.querySelector('section')).toBeTruthy(); + }); + + it('applies ochre background by default', () => { + const { container } = render(Section); + expect(container.querySelector('section')?.className).toContain('bg-ochre-clay'); + }); + + it('applies brutal border when bordered=true', () => { + const { container } = render(Section, { props: { bordered: true } }); + const cls = container.querySelector('section')?.className ?? ''; + expect(cls).toContain('brutal-border-top'); + expect(cls).toContain('brutal-border-bottom'); + }); +}); +``` + +**Step 3: Run test — expect FAIL** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run test src/shared/ui/Section/Section.test.ts +``` + +**Step 4: Implement Section.svelte** + +```svelte + + + +
+ {#if children} + {@render children()} + {/if} +
+``` + +**Step 5: Implement Container.svelte** + +```svelte + + + +
+ {#if children} + {@render children()} + {/if} +
+``` + +**Step 6: Create index.ts** + +```typescript +export { default as Section } from './Section.svelte'; +export { default as Container } from './Container.svelte'; +export type { SectionBackground, ContainerSize } from './types'; +``` + +**Step 7: Run test — expect PASS** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run test src/shared/ui/Section/Section.test.ts +``` + +**Step 8: Update shared/ui/index.ts** — append: + +```typescript +export * from './Section'; +``` + +**Step 9: Write Section.stories.svelte** + +```svelte + + + + {#snippet template()} +
+ +

Section content

+
+
+ {/snippet} +
+ + + {#snippet template()} +
+ +

Section content on slate

+
+
+ {/snippet} +
+ + + {#snippet template()} +
+ +

Section with brutal top and bottom borders

+
+
+ {/snippet} +
+``` + +**Step 10: Commit** + +```bash +cd /home/ilia/Documents/Projects/allmywork +git add src/shared/ui/Section/ src/shared/ui/index.ts +git commit -m "feat(shared/ui): add Section and Container components with layout presets" +``` + +--- + +## Task 8: Implement `Input` and `Textarea` components + +**Files:** +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Input/types.ts` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Input/Input.svelte` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Input/Textarea.svelte` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Input/index.ts` +- Create: `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Input/Input.stories.svelte` + +**Reference prototype:** `/home/ilia/Downloads/High-End Portfolio Design System(1)/src/app/components/Input.tsx` + +**Step 1: Create types.ts** + +```typescript +// No custom types needed beyond HTML attributes; file reserved for future extension. +export type InputSize = 'sm' | 'md' | 'lg'; +``` + +**Step 2: Write failing test** + +Create `/home/ilia/Documents/Projects/allmywork/src/shared/ui/Input/Input.test.ts`: + +```typescript +import { render } from '@testing-library/svelte'; +import { describe, expect, it } from 'vitest'; +import Input from './Input.svelte'; + +describe('Input', () => { + it('renders an element', () => { + const { container } = render(Input); + expect(container.querySelector('input')).toBeTruthy(); + }); + + it('renders label when label prop is set', () => { + const { getByText } = render(Input, { props: { label: 'Email' } }); + expect(getByText('Email')).toBeTruthy(); + }); + + it('renders error message when error prop is set', () => { + const { getByText } = render(Input, { props: { error: 'Required' } }); + expect(getByText('Required')).toBeTruthy(); + }); +}); +``` + +**Step 3: Run test — expect FAIL** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run test src/shared/ui/Input/Input.test.ts +``` + +**Step 4: Implement Input.svelte** + +```svelte + + + +
+ {#if label} + + {/if} + + + + {#if error} + {error} + {/if} +
+``` + +**Step 5: Implement Textarea.svelte** + +```svelte + + + +
+ {#if label} + + {/if} + + + + {#if error} + {error} + {/if} +
+``` + +**Step 6: Create index.ts** + +```typescript +export { default as Input } from './Input.svelte'; +export { default as Textarea } from './Textarea.svelte'; +export type { InputSize } from './types'; +``` + +**Step 7: Run test — expect PASS** + +```bash +cd /home/ilia/Documents/Projects/allmywork +bun run test src/shared/ui/Input/Input.test.ts +``` + +**Step 8: Update shared/ui/index.ts** — append: + +```typescript +export * from './Input'; +``` + +**Step 9: Write Input.stories.svelte** + +```svelte + + + + {#snippet template()} + + {/snippet} + + + + {#snippet template()} + + {/snippet} + + + + {#snippet template()} + + {/snippet} + + + + {#snippet template()} + + {/snippet} + + + + {#snippet template()} +