Files
Portfolio/AGENTS.md
Antonio Ledebuhr 34367181d2 integration: add Playwright, axe and Lighthouse CI gates
Lock SEO, accessibility and crawlability with headless browser checks so regressions fail before a merge instead of after publication.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 18:28:24 +02:00

9.8 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 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 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.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
  • Exception: isApplePlatform() in src/app/core/platform/browser.ts may read navigator.userAgentData?.platform ?? navigator.platform after 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.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 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 by SeoService during SSR and on every client navigation
  • One JSON-LD @graph script per route: Person and ProfessionalService on Home, Service on service routes, CreativeWork per public case on projects, WebPage elsewhere
  • public/robots.txt, public/sitemap.xml and public/llms.txt stay synchronized with prerenderablePaths() via crawl-assets.spec.ts
  • The language switch exposes hreflang on 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:

  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
  9. Crawl files still match SITE_CONFIG.siteOrigin and prerenderablePaths()
  10. 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, 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.