All work WEB PLATFORM / MEDICAL DEVICE in progress

26  /  WEB PLATFORM / MEDICAL DEVICE

ESMEE Prosthetics Site

Amputees and sponsors needed opposite proof, and most of ESMEE's own figures were not yet in writing.

August – September 2026Next.js 16 (App Router)React 18Hand-written CSS design system (~19k lines)Design tokens on scope classesJSON-LD structured dataInline SVG diagram componentslucide-reactVercel

The challenge

ESMEE builds bionic hands and prosthetic fingers in Kuala Lumpur. I had to persuade two audiences that need opposite kinds of persuading: amputees deciding whether a free-hand programme is real, and sponsors deciding whether their money reaches a recipient. Most of ESMEE's own figures were not yet in writing. Weights, what the $8,800 list price covers, who is eligible, what happens to an application that is refused. If I guessed at those, the site would read exactly like the sites the amputee page needs to be distinguished from.

What I built

I built a Next.js App Router site, statically prerendered so the served HTML carries the full body copy and its JSON-LD: eight routes plus ten homepage concepts. I hand-wrote the design system in CSS rather than reaching for a framework, with the whole palette expressed as design tokens on a scope class, so nine of the ten concepts run off one component tree and one set of copy. The tenth is a deliberately frozen snapshot with its own namespaced components, stylesheet and photography, so old concepts cannot drift when the live system changes. On the two programme pages, I mark anything ESMEE has not confirmed rather than estimating it: a "Confirmation pending" chip on the product pages, a standing shield-marked note on the sponsorship page. My README carries a publication checklist naming every fact ESMEE still has to confirm before launch.

How it works

NOT CONFIRMED THE PAGE REFUSES HUMAN An unconfirmed specnot yet in writing A pending symbol20 hand page, 5 finger page A neutral chipnot a hedged number ESMEE confirms iton a pre-launch checklist WHEN TWO SOURCES DISAGREE A live contradiction the site: under 450 g the flyer: about 500 g The hand product page publishes no weight at all not even in its JSON-LD
The mechanism, drawn from the build.

What it looks like

ESMEE Prosthetics Site — screenshot
Captured liveWEB PLATFORM / MEDICAL DEVICE

The detail

01

Demonstrates, never collects

The amputee application is a working six-step wizard with no action, no name attribute on any control, no file input, no persistence, and an onSubmit that always prevents default. Nothing can be serialised, even by a stray submit. The real route to ESMEE is a mailto, and I wired a role="note" panel warning against entering real medical information into the form with aria-describedby, then restated it in the step's own words on the documents and consent steps.

02

Reserved, not invented

The sponsor roster ships three reserved cards, each stamped "Placeholder image · not a recipient". I invent no name, country, amputation level, age or funding total. A filled progress bar would be a fundraising claim, so I made the reserved state a hatch rather than a percentage, because zero would be a claim too; PLACEHOLDERS.md records the stock-photo sources and licences and the two-step removal, after which the cards fall back to an empty consent frame on their own.

03

Sticky by hand

The homepage journey section holds one panel on screen while four steps advance under it. position: sticky was unavailable: the section wrapper sets overflow: clip for decorative bleed, which makes every descendant treat that wrapper, which never scrolls, as its sticky containing block. So I toggle fixed and absolute by hand against a scroll budget of steps × 80vh, computing the release threshold from the panel's own height rather than the viewport edge, so the handoff lands on a single continuous pixel.

04

Geometry over observers

I read getBoundingClientRect on scroll for the count-up figures instead of using IntersectionObserver. An observer only reports frames where the element genuinely intersects, so one jump past it (a restored scroll position, an in-page anchor, End) can skip every such frame and leave the number at its start value. On a page asking for money that means a visitor reading "$0". Asking whether the element has passed the fold is true from the moment it is passed, however fast.

05

A pending symbol

I hold unconfirmed specifications as a Symbol('confirmation pending') in the page data, standing in for 20 values on the hand page and 5 on the finger page; anything holding it renders as a neutral chip rather than a hedged number. One is a live contradiction, the site saying under 450 g and the myHand flyer about 500 g, so the hand product page publishes no weight at all, not even in its JSON-LD. Propagating that same suppression to the homepage and the free-hand page is still an open item on my pre-launch checklist.

The outcome

The site is complete and publicly reachable as a staging build. Every route returns 200 and is statically prerendered. I am also deliberately holding it back: app/robots.js disallows all crawlers, with a comment saying to delete it when the real domain goes live, and my README lists every fact ESMEE still has to confirm, noting that until those exist the application form must stay non-functional. I built ten homepage concepts; nine went to a scored vote sheet for three named reviewers, with live rank and total formulas and an explicit tie flag that hands the decision back to them. No votes have been recorded yet.

Complete and publicly reachable as a staging build. The link is held back pending the client's sign-off, since the build is deliberately crawler-blocked until the real domain goes live.