# 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 - 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.