Assertions now follow the eighteen prerenderable paths, the gist dialog, and the six-case evidence set. Co-authored-by: Cursor <cursoragent@cursor.com>
10 KiB
AGENTS.md
Contributor and agent guide for this repository. Read this before changing routing, copy, styles, or SSR behaviour.
Purpose
This is a bilingual 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. Home is the direct-customer experience. /pitch is the recruiter-oriented page and is reachable without sitting in the primary navigation. 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 e2e |
Playwright + axe-core against the SSR bundle. |
npm run e2e:install |
Install the pinned Chromium build. |
npm run lighthouse |
Production build, then Lighthouse CI. |
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.OnPushon 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.scssas 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-tomixin live insrc/_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
anyComponentStylebudget (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 headingproof— only a verifiable statementplayfulLine— optional wordplay; never a capability claimcta— 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 - Primary navigation is Home, Services, Projects, About and Contact. Services is one anchored page with four sections. Stack and Pitch stay reachable from content and the terminal, not from the primary nav.
- 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 isAppLocalefromLocaleService
SSR rules
- No
window,document,navigator,localStorageormatchMediaaccess outsideafterNextRenderorisPlatformBrowserguards - Use the injected
DOCUMENTtoken when the document element is required (it works on server and browser) - No user-agent sniffing; use CSS media queries for device capability
- Exception:
isApplePlatform()insrc/app/core/platform/browser.tsmay readnavigator.userAgentData?.platform ?? navigator.platformafter hydration, only to label the Command key. There is no CSS media query for that key. It is not used for device capability. - Capability helpers live in
src/app/core/platform/browser.tsand 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
h1per page - Landmark elements (
header/nav,main,footer) - Visible
:focus-visiblering - Keyboard reachability for every action
aria-currenton 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 origin is
SITE_CONFIG.siteOrigin(https://antoniolede.de); every absolute URL in the app and tests is derived from it - Per-route metadata (description, canonical, reciprocal hreflang including
x-default, Open Graph, Twitter, robots) is written bySeoServiceduring SSR and on every client navigation - One JSON-LD
@graphscript per route: Person and ProfessionalService on Home, one Service entry per services section on the services route, CreativeWork per public case on projects, WebPage elsewhere public/robots.txt,public/sitemap.xmlandpublic/llms.txtstay synchronized withprerenderablePaths()viacrawl-assets.spec.ts- The language switch exposes
hreflangon the alternate-locale link
Browser binaries, HTML reports, traces and screenshots are not committed (test-results, playwright-report, .lighthouseci).
Test checklist
A change is not done until:
npm run lintexits 0npm run format:checkexits 0npm run test:ciexits 0 with no watcher left runningnpm run buildexits 0- New or changed routes appear in
prerenderablePaths()and are prerendered - Routing and locale contracts still have unit coverage (paths, locale helpers, navigation links)
- No new
window/document/navigatorreads were added outside an SSR-safe guard - Placeholder or legal copy was not replaced with invented professional claims
- Crawl files still match
SITE_CONFIG.siteOriginandprerenderablePaths() - Playwright (
npm run e2e) and Lighthouse CI (npm run lighthouse) still pass when the change affects public HTML, metadata or chrome
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 dock, 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 owns the bilingual copy tree behind SITE_CONTENT. The Signature branch may replace the dot-background internals but must keep the SSR-safe init/teardown contract.