Rendering, SSR and prerendering
How it is served
The build produces two things: the static browser files, and an Express server (server.ts) able to render the same pages on Node.
Static routes are prerendered at build time: the HTML arrives already written, so the content is readable before JavaScript runs and crawlers see it without executing anything.
Project detail pages and these engineering notes are prerendered from a list generated out of the real data (scripts/generate-routes.mjs), so adding a project does not require remembering to update a hand-written list.
Hydration
provideClientHydration() reuses the server HTML instead of throwing it away and repainting. One detail shows it: the terminal boot sequence is already printed in the prerendered HTML and the component notices — otherwise the window would appear twice, once on first paint and once after hydration.
SSR-safe code
window, document and localStorage do not exist on the server. Code that touches them goes through PlatformService.isBrowser or Angular's abstractions (DOCUMENT, Meta, Title). SEO metadata is set through those abstractions precisely because it has to end up in the prerendered HTML — the only one LinkedIn and Google read.
On the server the terminal boot sequence never starts: its timers would keep the zone unstable and prerendering would wait for them.
Static hosting
The site can run from a subdirectory (/portfolio/). In that case:
npm run build:pagessets--base-href /portfolio/PROFILE.siteUrlmust include the subdirectory- no URL may start with
/: the section backgrounds were written asurl("/assets/...")and returned 404 under/portfolio/, leaving the pages black. The path is now relative to the stylesheet and resolved by the bundler, which emits the image intomedia/with a relative URL - a
404.htmlthat reloads the application is required, because static hosting knows nothing about client-side routes
Two checks, verifying different things:
npm run seo:check— siteUrl, base href, robots.txt and the sitemap all tell the same storynpm run pages:check— actually builds, serves the result under/portfolio/and opens the pages in a browser, watching every network request. It fails on a 404 asset, on a request escaping the base path, or on a console error. A build finishing without errors proves nothing: it was green while the backgrounds were failing to load.