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
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.
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.
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.
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/.
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.
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.
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.
Verify it yourself
The home page has a "Don't trust me. Verify." panel. When you click it, your browser checks this domain live: HTTPS, HSTS and the Content-Security-Policy are read from a same-origin request, and SPF, DMARC, CAA and DNSSEC come from DNS-over-HTTPS (Cloudflare, then Google). It never runs on page load, nothing is stored or sent to this server, the TLS version is not claimed because JavaScript cannot read it, and if the resolvers are blocked it says so instead of reporting a failure.
Architecture
Sanitized diagram
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.