Case study · This website

How this site is built

A static site with no framework, no server code and no trackers, hardened with the same discipline I use at work: strict headers, safe fallbacks, and everything verifiable.

  • HTML5
  • CSS custom properties
  • JavaScript modules
  • three.js
  • GSAP ScrollTrigger
  • Apache/LiteSpeed .htaccess

Role: Design, build, and hosting

The challenge

What needed fixing

A portfolio should load fast on a phone, read well without JavaScript, respect people who prefer reduced motion, and not leak anything: no third-party scripts, no cookies, no analytics. It also had to be simple to deploy: a zip uploaded to shared hosting, with no build step.

My approach

What I did, in order

  1. Static files only

    Every page is plain HTML with real text, three CSS files and a few small JavaScript modules. No framework, no server-side code, no database. Fonts are self-hosted so nothing is fetched from third parties.

  2. Background scene that never blocks the page

    The particle scene (one three.js Points object with a custom shader, morphing between formations as you scroll) is lazy-loaded after the page is interactive. If motion is reduced, WebGL2 is unavailable, data saver is on, or the device is weak, a still image shows instead, and the text never waits for it.

  3. Strict Content Security Policy

    Scripts, styles, fonts and images are same-origin only. There is no unsafe-inline: the two inline scripts (theme bootstrap and the import map) are allowed by SHA-256 hashes. frame-ancestors is none, base-uri and form-action are self.

  4. Transport and browser hardening

    HTTPS is forced with a single 301 (www redirects to the apex), HSTS is set for a year with subdomains, plus X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy and Cross-Origin-Opener-Policy. A security.txt is published under /.well-known/.

  5. No cookies, no trackers

    The site sets no cookies and loads no analytics or embeds. The only stored value is the light/dark choice in localStorage, on your device.

  6. Caching without stale files

    Static assets and pinned libraries cache for a year. Site CSS and JavaScript URLs, including the import map that resolves every module, carry a version that changes on each deploy, so returning visitors never mix old and new files.

  7. Accessibility

    One h1 per page, semantic landmarks, a skip link, visible focus rings, 44px tap targets, keyboard-operable toggles, and AA contrast in both themes. The reveal animations have a failsafe so content can never stay hidden.

Architecture

Sanitized diagram

Request flow: the browser gets static HTML from the host, then CSS and fonts; page scripts load after first paint; the particle scene loads last and falls back to a still image when WebGL or motion is unavailable.BrowserHTTPS onlyStatic hostno server codeSecurity headersCSP · HSTS · no cookiesHTML + CSSself-hosted fontsSite scripts~10 KB, after paintParticle scenelazy, versioned modulesFallback: still imagereduced motion · no WebGL2works with JS off
Request flow, simplified: static files and security headers first, page scripts after first paint, the particle scene last, with a still image as the fallback.

Results

What changed

  • Runs with JavaScript disabled and with reduced motion

  • No third-party requests, cookies or trackers

  • Lighthouse (local run): Accessibility, Best Practices and SEO 100; Performance 90+ on the home page

What I learned

Takeaway

The same rules that protect a clinical network apply to a personal site: default to same-origin, fail safe, version what you cache, and make every claim verifiable from the outside.

Tools used

Tools used

  • HTML5
  • CSS custom properties
  • JavaScript modules
  • three.js
  • GSAP ScrollTrigger
  • Apache/LiteSpeed .htaccess

Contact

Want the full story?

Happy to walk through the details, the trade-offs, and what I would do differently.