Jake Shaw
Back homeColophon

How this site is built

Short version first, longer version after. Here’s what each part of the homepage actually is.

About

This is the part where I explain who I am without a wall of bullet points. The three quick facts on the side (where I’m based, whether I’m currently open to work, what I focus on) are there for anyone skimming; the paragraphs next to them are for anyone who wants the fuller story. The photo is deliberately modest in size — this isn’t a headshot showcase, just enough to put a face to the words.

Selected work

Scroll through this and each project card sticks in place while the next one slides up over it, a bit like flicking through a small stack of index cards. Click into any of them and the card’s image and title carry across into the full case study rather than the page just jumping — a small touch, meant to feel like opening a folder rather than loading a new page. Everything on the cards, and in the case studies behind them, comes from content I edit separately, not text baked into the code.

Experience

A straightforward timeline: a line runs down the left as you scroll, and each role lights up as you reach it. Click into a role for more detail than fits in the summary. Like the projects above, this list comes from the same editable content rather than being hand-written in the code, so updating my history doesn’t need a new deploy.

Kind words

Quotes from people I’ve worked with, trimmed down to role only rather than full names — fuller, attributed versions are available on request. Flick through them with the arrow buttons, the dots underneath, or by scrolling the row directly with a trackpad, mouse wheel, or the keyboard, whichever’s easiest for you.

Contact

Nothing clever on purpose: an email button, and links to LinkedIn and GitHub if you’d rather look me up there first. The wording changes slightly depending on whether I’m actively looking for work, so what you’re reading reflects my situation right now, not a stale default.

This page exists because I make claims elsewhere on this site about accessibility, CI, and security that are easy to state and easy to leave unproven. Here is some of the evidence, drawn from the code actually running behind this page.

Motion here is opt-in, not assumed. Every GSAP timeline runs inside gsap.matchMedia("(prefers-reduced-motion: no-preference)"), so if you have told your OS you do not want animation, none of it plays. That is a real branch in the code, not a CSS media query bolted on afterwards. The testimonial carousel further down this site follows the same principle: keyboard-reachable rather than mouse-only, with dot buttons sized to a proper touch target rather than relying on you being able to grab a scrollbar.

Every GitHub Action this repo’s CI runs is pinned to a commit SHA rather than a floating version tag, so a compromised or rewritten upstream release cannot silently swap in different code on the next push. Before anything else runs, a small dependency-free script scans every committed image for embedded EXIF and XMP metadata and fails the build if it finds any (the kind of thing a camera or design tool embeds without telling you, and happily ships to every visitor if nobody checks). I added that gate after finding and fixing exactly that leak on a real project.

Content comes from Contentful, and one field, the rich case-study body, is a freeform Object type Contentful does not type-check the way it does everything else. So it is validated with Zod at the fetch boundary, and malformed content fails loudly at build time instead of quietly rendering as broken markup.

The one compromise I will own rather than hide: the CSP allows script-src 'unsafe-inline'. Next.js inlines the React server payload as inline script tags that differ on every build, so a static hash allowlist cannot cover them, and the strict alternative, a per-request nonce, needs dynamic rendering, giving up static generation for a site with no other reason to need it. I made that trade deliberately: no user input is ever accepted or reflected anywhere on this site, so there is nowhere for an attacker to inject markup in the first place, which is what the rest of the policy is written to protect.

None of this is dramatic, and that is rather the point. It is what I would want a production frontend to look like by default, not just on the projects where someone is watching. The fuller reasoning behind these decisions, and a few more, is written up in ARCHITECTURE.md in the repo.