149 lines
8.2 KiB
Markdown
149 lines
8.2 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
|
|
- 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.
|