Winning tests first, then the range: SPA-aware UI, server-load
mitigation, enterprise platform quirks, and accessibility-compliant variants. Real
experiments, real outcomes, shipped to production.
Radiator Super SKU
Won ×3 · CVR +13.9% · £1.87m potential
Large UK home improvement retailer
Every radiator size was a separate SKU and page. A Super SKU surfaces all sizes on one PDP, built from a reference map with live price hydration. Won, and held up across three runs.
Read the case study →
Add-to-bag interstitial CTA
Won · CVR +10.0% · £9.84m potential
Large UK home improvement retailer
The add-to-bag interstitial led with "Checkout Now", a bigger commitment than users want at that step. Relabelled to match intent, fully instrumented on a Next.js SPA.
Read the case study →
PDP image optimisation
Won · AOV +4.34% · £1.19m in-test
Large UK home improvement retailer
The PDP gallery was constrained, with no proper fullscreen inspection. A full client-side rebuild: custom carousel, fullscreen zoom modal, mobile pinch-to-zoom, all keyboard-accessible.
Read the case study →
Most-relevant USPs
Rolled out 100% · CVR +4.15% · £11.1m potential
Large UK home improvement retailer
The strongest reasons to buy, fulfilment speed and certainty, weren't surfaced near the top. A scannable USP strip, injected client-side and SPA-aware.
Read the case study →
Sample Ratio Mismatch in SPA experiments
Why your GA4 split never matches the platform
GA4 shows 60:40 when the testing platform shows 50:50.
Random, unstable, never matches, and the experiment code is rarely the
actual cause.
Walks through the four root causes of GA4/platform Sample
Ratio Mismatch on SPAs (data layer timing, beacon loss, virtual pageview
context, bot traffic) and the Tealium/GTM tracking pattern (with
utag.link intercept and payload mirroring) that fixes most of
them.
Read the article →
Quote-step redesign on a React funnel
UK vehicle scrappage and valuation service
A revenue-critical Vehicle Details step, redesigned to a
cleaner single column with the offer in a banner, delivered client-side through
Convert on a React single-page app with no access to the codebase.
Re-skinned React's own form rather than rebuilding it: CSS
for everything it can do so it survives re-renders, JavaScript only for the
banner, icon and CTA, kept idempotent and re-applied across the funnel's route
changes, with the vehicle read live from localStorage. Built and in QA.
Read the case study →
Article
Tooling
I built a CRO build tool
abtestrig, a CRO-specific build tool, open source on
GitHub
Every CRO dev rebuilds the same Gulp/webpack rig.
Hardcoded loader-script IDs, 8 to 15s rebuilds, page reloads on every CSS tweak,
and no way to test interactions between two live experiments before
shipping.
Built abtestrig around esbuild: sub-200ms builds, a loader
that auto-discovers the active experiment and port (no script edits between
experiments), CSS hot-swap via a stable <style> slot so
the activated state is preserved, stack-mode that runs multiple experiments
in deterministic order for conflict testing, and a shared @lib
helper library so pollerLite/waitForElement stop
getting copy-pasted between projects.
Read the article →
HubSpot CMS landing page build
UK ground engineering / residential subsidence
Geobear runs a HubSpot CMS estate of 50+ pages across several
brand variants, all powered by the same theme. The brief: a new residential
landing page with a different layout and modules, without putting the 50+ live
pages at risk.
Cloned the live theme into a parallel build via
hs cms fetch / hs cms upload, then shipped nine custom
modules from scratch: sticky nav, hero with background video, case carousel,
get-advice flow, logos, testimonials, video gallery, CTA banner, and a Leadoo
form wrapper. Deferred Swiper init via IntersectionObserver, a
ResizeObserver shim for sticky-nav padding under HubSpot's
overflow: hidden wrappers, CSS custom properties for the design
system, and inline schema.org JSON-LD (HowTo,
ItemList, Review) that passed the Rich Results
Test.
Read the case study →