portfolio · this site
The portfolio home page in dark mode: a band of technology tags, the headline, a short introduction and the buttons to the work and to more about me.

Overview

The site you are reading, prerendered to static HTML in English, German and Romanian, with a first-party analytics API that hears only from readers who consent.

A recruiter opens a link to the German CV on a phone with scripting blocked. The whole CV arrives in German anyway, because the address names a file that already holds it.

  • Read four case studies, the CV and the legal pages in English, German or Romanian
  • Download the CV as a PDF, built in the browser
  • Switch between dark and light, with the choice kept for the next visit
Everything a reader does works without the API; counting starts only once they agree.
Key facts
RoleSole author, design, front end, API and deployment
ShapeA prerendered Angular 22 site, a .NET 10 analytics API and an admin dashboard
LanguagesEnglish, German and Romanian, each compiled as its own build
Pages27 pages of real HTML: nine routes in three languages
Tests636 frontend specs, 572 browser tests, 193 backend test methods
Public repositoryThe complete source of the site you are reading

Architecture

A static site that stands on its own, and an API it can do without.

Left of the dashed line is everything that still works while the API sleeps.
  • Every route is prerendered at build time, so the host is a folder of files with no server of its own.
  • Each language is its own build in its own folder, /de-DE/cv for instance, because Azure Static Web Apps cannot route on a query string.
  • The Angular app is layered data, core, shared, layout and features, the arrow pointing one way, and no feature imports another.
  • The API scales to zero, and the page never waits for it: when it does not answer, the view counter is simply absent.

Domain design

The content is typed data, so a case study that breaks the shape does not compile.

Typed content in, a folder of files out. A missing translation stops the line at ng build.
  • Every string a reader sees carries a stable message id, so a new extraction never orphans a translation.
  • A missing translation fails the build instead of falling back to English.
  • Below the features, content comes from its own file and never the barrel, or every case study would join the first download.

Data and control flow

One visit, from the address to the view counter.

The page has everything it needs before the first request to the API is made.

Testing strategy

A spec beside every unit, and browser tests against the build the host will serve.

  • The run fails when a component, service, directive, pipe or util has no spec beside it.
  • Playwright drives the production build on a desktop and a phone, served with the host's own routes, headers and CSP.
  • Every browser test fails on a console error, an uncaught exception or a request that leaves the site.
  • The .NET projects build with StyleCop and warnings as errors, and the domain references no package at all, so the compiler holds its direction.

What I would change

  • The case studies are TypeScript, so fixing a typo in one takes a build and a deploy.
  • The development server localizes one language at a time, so checking a German page needs a second command.
  • The host allows one 404 page, so a small script fetches the right language in its place. A host with a 404 per folder would not need it.
  • The feature boundaries in the lint config are listed by hand: a new feature means editing the config as well.