/ Colophon

How this site is built: a technical exhibit

This page is a portfolio piece in its own right. No build step you cannot read, no dependency you cannot name. Here is exactly what runs it.

The framework

The site runs on Pulsar, my own PHP 8.5 and Rust framework. Dependency injection is compiled, so the container resolves at build time and there is no reflection on the hot path. The domain, application and infrastructure layers are kept honestly separate, and the critical modules carry formal verification with Creusot.

The token system

Everything visual derives from one set of custom properties, the Precision Noir tokens. Three identity accents, swapped per section with a single attribute. No hard-coded colour lives in a component.

#0039CBDevelopment
#D4A843Photography
#2D8F47Explore
1--accent, swapped
theme.css
/* one accent, re-pointed per section */[data-section="photography"] {  --accent: var(--color-photo);  --accent-glow: var(--color-photo-glow);}

Performance and accessibility

Two self-hosted variable fonts, preloaded, with font-display swap. Responsive images with AVIF then WebP, explicit dimensions so nothing shifts. JavaScript is minimal, deferred, and the core content works with it off. Targets are real: LCP under 2.5s, CLS under 0.1, INP under 200ms.

Contrast passes WCAG 2.2 AA in both themes and on the pure-black galleries. Every interactive element has a visible focus ring, motion collapses under reduced-motion, and a skip link is the first thing a keyboard reaches.

Hosting

AWS EC2 with Nginx and PHP-FPM 8.5. HTTP/3 and caching at the edge through CloudFront, transactional mail through SES, certificates from ACM. Nothing exotic, all boring on purpose.

Recommended security headers

Shipped at the edge. Here is the set, as an exhibit.

Form hardening

The contact and booking forms are defended in layers: a hidden honeypot field that only bots fill, a time-trap that rejects sub-2.5s submissions, server-side validation and escaping, per-IP rate limiting, and an hCaptcha challenge held in reserve for anything that still looks automated. The forms also state plainly that they are for project enquiries and questions, not cold sales.

headers.conf
Content-Security-Policy: default-src 'self';  img-src 'self' data:; font-src 'self';  script-src 'self'; style-src 'self'Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadX-Content-Type-Options: nosniffReferrer-Policy: strict-origin-when-cross-originPermissions-Policy: geolocation=(), camera=(), microphone=()

Images and fonts

Every image ships as AVIF with a WebP and JPEG fallback, in a responsive <picture> with explicit dimensions so nothing shifts as it loads. The hero image is marked fetchpriority="high"; everything below the fold is lazy. Fonts are self-hosted woff2, preloaded and swapped, so there is no third-party request on the critical path.

picture.html
<span class="media" style="--ar: 4 / 5">  <picture>    <source type="image/avif" srcset="portrait-480.avif 480w, portrait-960.avif 960w" sizes="(max-width: 600px) 100vw, 480px">    <source type="image/webp" srcset="portrait-480.webp 480w, portrait-960.webp 960w" sizes="(max-width: 600px) 100vw, 480px">    <img src="portrait-960.jpg" width="960" height="1200" alt="Studio portrait, soft key light" loading="lazy" decoding="async">  </picture></span>
fonts.css
/* preloaded in <head>, swapped, self-hosted woff2 */@font-face {  font-family: 'Montserrat';  src: url(/fonts/montserrat-var.woff2) format('woff2-variations');  font-weight: 100 900; font-display: swap;}