Files
Portfolio/AGENTS.md

150 lines
8.4 KiB
Markdown

# AGENTS.md
Contributor and agent guide for this repository. Read this before changing routing, copy, styles, or SSR behaviour.
## Purpose
This is a bilingual recruiter and B2B portfolio for a software/DevOps engineer. German is the default language and is served at `/`. English is the full second version and is served under `/en`. Every public page exists in both locales.
## Architecture
- Angular 21 standalone components, no NgModules
- Zoneless change detection
- Signals for local state
- Lazy-loaded feature routes
- SSR with prerendering (`@angular/ssr`, `outputMode: "server"`)
- Express host in `src/server.ts`
## Directory map
| Path | Role |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `src/app/core/**` | Contracts, services and tokens. No UI. |
| `src/app/features/**` | Routed page components, one folder per route. |
| `src/app/shared/**` | Reusable UI primitives used by multiple features. |
| `src/app/components/**` | Existing decorative/feature widgets (skills grid, dot background). |
| `public/**` | Static SVG and public assets copied as-is. |
| `src/styles.scss` | Global design tokens and primitives. The only place raw brand hex values are defined. |
| `src/_breakpoints.scss` | Breakpoint map (`sm` 40rem, `md` 48rem, `lg` 64rem, `xl` 80rem) and the `respond-to` mixin. Components pull it in with `@use 'breakpoints'`. |
## Commands
| Script | Purpose |
| ----------------------------- | --------------------------------------------- |
| `npm start` | Dev server (`ng serve`). |
| `npm run build` | Production build (default configuration). |
| `npm run watch` | Development rebuild on change. |
| `npm test` | Unit tests in watch mode. |
| `npm run test:ci` | Unit tests once (`--watch=false`); must exit. |
| `npm run lint` | ESLint over the workspace. |
| `npm run lint:fix` | ESLint with autofix. |
| `npm run format` | Prettier write. |
| `npm run format:check` | Prettier check (CI). |
| `npm run serve:ssr` | Serve the production SSR bundle. |
| `npm run serve:ssr:Portfolio` | Alias of `serve:ssr` for existing tooling. |
| `npm run ci` | lint, format:check, test:ci, then build. |
## Code conventions
- Standalone components only
- `inject()` over constructor injection for new code
- Signals over RxJS for local state
- `ChangeDetectionStrategy.OnPush` on new components
- kebab-case file names
- No `any`
- No default exports
- Templates in separate files
## SCSS conventions
- Global design tokens live only in `src/styles.scss` as CSS custom properties
- Components consume `var(--...)` and never redefine raw hex values
- Use the documented spacing (`--space-1``--space-12`) and typography (`--text-xs``--text-3xl`) scale
- Hover-only affordances must be wrapped in `@media (hover: hover) and (pointer: fine)`
- All motion must respect `prefers-reduced-motion`
- Breakpoints and the `respond-to` mixin live in `src/_breakpoints.scss`; do not invent extra breakpoint names
- Shared glass, layout and focus treatments belong on the global primitives (`.glass-surface`, `.content-container`, `.stack`, `.cluster`), not copied into component stylesheets
- Keep component styles under the `anyComponentStyle` budget (4 kB warn, 8 kB error)
## Content rules
All user-facing copy comes from the typed bilingual content layer in `src/app/core/content`. Never inline marketing or page copy in templates.
Keep the four copy roles as separate fields:
- `headline` — the serious heading
- `proof` — only a verifiable statement
- `playfulLine` — optional wordplay; never a capability claim
- `cta` — action label, kept swappable
German and English are written idiomatically per language, never machine-translated word for word.
## Claims policy (non-negotiable)
- No unsupported superlatives
- No invented references, client names, metrics or certifications
- Unproven AI, local-LLM or multi-agent capabilities are phrased as an offering or way of working, never as a delivered reference
- The CV stations HUP, BitWiz and Cybertrading are deliberately excluded from all public pages, cases and timelines
- The legal pages (Impressum, Datenschutz) contain placeholders that MUST be reviewed by the site owner before publication and must visibly say so
## i18n rules
- German at `/`, English under `/en`
- Every route id exists in both locales
- New pages are added by extending the route-id table in `src/app/core/routing`, never by hard-coding paths
- Do not swap `LOCALE_ID`; the active locale is `AppLocale` from `LocaleService`
## SSR rules
- No `window`, `document`, `navigator`, `localStorage` or `matchMedia` access outside `afterNextRender` or `isPlatformBrowser` guards
- Use the injected `DOCUMENT` token when the document element is required (it works on server and browser)
- No user-agent sniffing; use CSS media queries for device capability
- Capability helpers live in `src/app/core/platform/browser.ts` and return conservative defaults on the server
- Helpers that use `inject()` must be called from a field initializer or constructor, never from a lifecycle hook or callback, and the resolved value must be stored on the instance
- Every addressable route must remain prerenderable
## Accessibility checklist
- Skip link as the first focusable element, targeting `#main-content`
- Single `h1` per page
- Landmark elements (`header`/`nav`, `main`, `footer`)
- Visible `:focus-visible` ring
- Keyboard reachability for every action
- `aria-current` on the active nav item
- At least 4.5:1 text contrast on the base surface
- Reduced-motion alternatives for animation and smooth scrolling
- Navigation remains usable in its server-rendered default state (no JS-only menus)
## SEO expectations
- Semantic headings
- Real locale-prefixed URLs
- Server-rendered core text
- Per-route titles from the content layer
Canonical, hreflang document tags and JSON-LD arrive in the integration phase. The language switch already exposes `hreflang` on the alternate-locale link.
## Test checklist
A change is not done until:
1. `npm run lint` exits 0
2. `npm run format:check` exits 0
3. `npm run test:ci` exits 0 with no watcher left running
4. `npm run build` exits 0
5. New or changed routes appear in `prerenderablePaths()` and are prerendered
6. Routing and locale contracts still have unit coverage (paths, locale helpers, navigation links)
7. No new `window` / `document` / `navigator` reads were added outside an SSR-safe guard
8. Placeholder or legal copy was not replaced with invented professional claims
## Domain boundaries
| Branch | Owns |
| ----------- | -------------------------------------------------------------------------------- |
| Foundation | Tooling, tokens, shell, routing and content contracts, SSR safety, test baseline |
| Content | Bilingual copy data and page composition |
| Signature | Systems Map, terminal, dot background and motion |
| Integration | SEO and cross-cutting a11y/performance hardening |
Each branch extends the shared contracts instead of duplicating paths or copy. The Content branch replaces `placeholder-content.ts` and keeps `SITE_CONTENT`. The Signature branch may replace the dot-background internals but must keep the SSR-safe init/teardown contract.