Case study · Clinical trial finder for a global healthcare company

Clinical trial finder for a global healthcare company

A public platform where patients, caregivers, and healthcare professionals search a Solr-indexed registry of clinical trials by condition, biomarker, molecule, location, age, phase, and recruitment status. Two brands from one codebase, built on AEM with Vue 3, TypeScript, and Pinia, with components delivered as custom elements so content authors place them directly in templates. I was a senior frontend engineer on the platform with reviewer responsibility across frontend and QA merge requests.

Role
Senior frontend engineer, reviewer for frontend + QA merge requests (via a consultancy)
Stage
2021–2025
Scale
Two brands, one codebase · ~20 filter keys serialised bidirectionally to the URL
Stack
Vue 3, TypeScript, Pinia, Lit 3, Solr, AEM

One search state, four writers

The core architectural problem: one search state had to be readable and writable by four independent entry points. URL deep links (back/forward, reload, shared links), a guided wizard, a free-text autocomplete chip input, and faceted filter panels. The design that made it tractable was a dual filter model, pending versus committed. Filters the user is touching live in a pending set that drives live result counts; committing promotes them to the active set the API sees. Around 20 filter keys serialise bidirectionally to the URL, so every search survives refresh and browser navigation.

Two protocol quirks made it harder: full stops in molecule names collide with AEM's URL selector syntax, and commas in biomarker names collide with Solr's multi-value separators. Both are legal characters in medical vocabulary. The fix was a documented sentinel-escape scheme applied symmetrically at the API and display boundaries. The search engine also flattens the hierarchical medical condition taxonomy, so I rebuilt the tree client-side: level pairing, orphan recovery, and synthetic "show more" entries spliced back into the flat suggestion list.

A guided wizard as the conversion funnel

For users who don't know how to drive faceted search, I built a six-screen guided questionnaire (purpose, condition, biomarker, age, location). Every question, answer, label, and image is editable by content authors, per brand and per locale. Steps are deep-linkable and browser back/forward genuinely walks the wizard. Each answer round-trips a live "N matching trials" count without committing the search. Form-funnel analytics report whether the previous step was answered or skipped. One component renders two interaction models, a horizontal stepper on desktop and a vertical inline flow on mobile, inside a fully accessible modal (focus trap, scroll lock, Escape, screen reader flow).

Why this is the interesting part: the wizard and the faceted search aren't two features bolted together, they're two writers of the same pending filter set. A user can start in the wizard, bail into raw facets halfway through, and the live count keeps agreeing with itself the whole way.

Screen reader remediation against a component library

The deepest work on the platform. An accessibility audit found that each search result card was an anchor wrapping a menu button, and NVDA's virtual cursor fired the anchor every time, so keyboard and screen reader users could not open the menu at all. The strict CSP ruled out the usual javascript:void(0) tricks. The fix removes the anchor's href entirely while the inner button has focus, intercepts clicks in the capture phase, synthesises a keyboard event to open the menu, and guards against re-entrancy. Around 110 lines to close one audit finding correctly.

Alongside that: reimplementing arrow, page, home and end key scrolling for a virtualised list whose rows don't exist in the DOM, from-scratch focus traps with visibility-aware focusable queries, and live-region announcements for infinite scroll. All verified with NVDA, axe, and Percy visual regression.

Design system and tooling

I co-built the platform's Lit 3 web component library (atoms through organisms, dual Storybook instances, custom-elements-manifest docs, an a11y addon, Chromatic), including a pdf.js viewer with scroll-anchored zoom. I also owned frontend build tooling: a Vite to AEM clientlib pipeline, an SVG icon codegen step, and a three-process live-sync dev loop into running AEM instances.

  • Vue 3
  • TypeScript
  • Pinia
  • Lit 3
  • Vite
  • SCSS
  • Solr
  • AEM
  • Storybook
  • Cypress
  • axe
  • Percy

Back to selected work  ·  Next: regulated forms platform  ·  See the engagement overview  ·  Talk to me about a similar project