Skip to main content
BHPS, home

Stack

How this site is built

We audit other people's applications, so it would be odd if ours did not hold up. Here is what it is made of, and for each claim a way to check it. None of it is meant to be taken on trust.

Lighthouse

Performance
100
Accessibility
100
Best Practices
100
SEO
100
Agentic Browsing
3/3

Home page bhpslabs.com/cs, measured with Lighthouse in Chrome on .

In numbers

'unsafe-inline' in the CSP
0
unit tests
460+
Playwright end-to-end tests
230+
home page JS (brotli)
~180 kB
CSS (gzip)
8 kB
languages, every page in both
2

Foundation

The site is a single TypeScript application. The server and the browser share the same types, so a changed address or data shape shows up as an error in the editor, not in production.

  • TanStack Start and TanStack Router: server rendering, typed routes and parameters. Why we picked it over Next.js is on the blog.
  • React 19, Vite 8 and Nitro as the server. It runs on Node 24 in Docker as an unprivileged user.
  • Tailwind CSS 4 with our own tokens for day and night mode. Components never branch on the mode; tokens do.
  • Paraglide for translations: messages compile to typed functions, so a mistyped key is an error in the editor, not a blank on the page.
  • TypeScript 7, Oxlint and Oxfmt. Type checks, lint and formatting run as one command, pnpm run cq, and are part of CI.

Performance

Every public page is prerendered at build time. One list of addresses feeds both the prerender and the sitemap, so they cannot drift apart.

  • JS and CSS are precompressed to gzip and brotli and carry a content hash in their name, so browsers cache them for a year (immutable).
  • HTML is always revalidated (must-revalidate): a new article is visible right after a deploy.
  • Two variable fonts instead of a set of weights, both preloaded so text does not jump once they arrive.
  • The raster in the hero is drawn to a 2D canvas and has its own end-to-end frame-budget test. It runs alone, so it measures rendering, not the load from other tests.

Security

The Content Security Policy is built for each HTML response separately. script-src lists the SHA-256 hashes of exactly the inline scripts in that response. A nonce would not work: the pages are prerendered and the nonce would freeze into the file.

  • Strict-Transport-Security with preload, Cross-Origin-Opener-Policy, X-Content-Type-Options, X-Frame-Options: DENY and Referrer-Policy.
  • Permissions-Policy switches off 14 browser APIs the site does not need: camera, microphone, location, payments, USB and more.
  • Only video from youtube-nocookie.com and Vimeo can be embedded. Nothing else gets into the page.
  • Traffic is measured by Umami on our own domain, without cookies and without a consent banner.
  • CI can generate a CycloneDX SBOM and check it against the requirements of the EU Cyber Resilience Act (CRA).

For search engines and AI assistants

The site has readers that are not people. Search engines and language models get the same content as a visitor, in a shape that suits them.

  • Every page has a canonical URL, hreflang for both languages and one JSON-LD graph: company, site, page, breadcrumbs and, for articles, the author.
  • The sitemap gives a change date only where we actually know it. The build date would be a false freshness signal.
  • The same address returns markdown instead of HTML when a client asks with Accept: text/markdown, so an agent reads the content without navigation or scripts.
  • /llms.txt sums up the company for models. /.well-known/agent-skills/index.json and /.well-known/ai-catalog.json describe what an agent can do on the site; a Link header on every page points to them.
  • Through WebMCP the site offers tools to in-browser agents: open a page, get contact details and send an enquiry. The tool description tells the agent to send it only for a person who asked for it and confirmed its content.
  • robots.txt allows AI crawlers and uses a Content-Signal rule to say what the content may be used for.

Blog

Articles are markdown in the repository, next to the code. At build time unified (remark and rehype) processes them and Shiki highlights the code, so no highlighter runs in the browser.

  • An image without alternative text stops the build. Inaccessible content cannot ship by accident.
  • A YouTube or Vimeo link on its own line turns into a cookie-free embedded video.
  • Each language has an RSS feed with the full text of the articles, and each author has their own.

Accessibility

Accessibility is tested like everything else: automatically.

  • Playwright runs axe over the pages in both colour modes and both languages.
  • Colours carry a contrast obligation. Orange as text has its own darkened token, because the original fails on the light background.
  • A skip link, page-change announcements for screen readers, a visible focus ring and respect for prefers-reduced-motion.

Tests

Unit tests in Vitest and Testing Library guard the logic: the CSP, language negotiation, markdown for agents, the feed, the forms. End-to-end tests in Playwright walk the site like a person: navigation, keyboard, colour modes, SEO tags and accessibility.

For the curious

The >_ mark in the top bar opens a terminal. It knows ls, cd, cat, whoami and a few more commands that let you read another page without leaving the one you are on. There is a snake in it, and rm -rf does something too.

Check it yourself

You do not have to take any of this on trust. A terminal is enough:

npx lighthouse https://bhpslabs.com/cs --view

curl -s -D - -o /dev/null https://bhpslabs.com/cs | grep -i content-security-policy

curl -s -H "Accept: text/markdown" https://bhpslabs.com/cs/audit

curl -s https://bhpslabs.com/llms.txt

curl -s https://bhpslabs.com/.well-known/agent-skills/index.json

curl -s https://bhpslabs.com/cs/blog/feed.xml