Case Study

A healthcare website rebuilt
for editorial independence.

Migrating GoInvo’s website off aging Gatsby infrastructure and rebuilding it around three things: a Sanity CMS that lets non-technical editors publish independently, an animation layer with card-morph transitions and scroll-driven reveals, and accessibility as a first-class constraint. Live and serving real traffic.

View Project (opens in new tab)
Next.js 16React 19TypeScriptSanity CMSTailwind CSS v4Framer MotionGROQView Transitions API
01

The Problem

GoInvo is a healthcare UX studio whose Gatsby site had become a liability on multiple fronts. Build times stretched to minutes as the plugin ecosystem stagnated. Every content update required a developer to write JSX, commit to Git, and wait for a deploy. Accessibility was inconsistent. Animations were absent. The studio’s writers and designers were locked out of their own site.

The brief had three pillars: a CMS that lets editors publish independently: case studies with rich media blocks, vision articles, team bios, job postings; an animation layer that matches the studio’s design quality; and accessibility built into the foundation from the start. The animation work drew on research into how the brain tracks spatial continuity: card-morph transitions maintain object permanence across page changes, reducing the cognitive reorientation cost that hard cuts impose. I wrote migration scripts to port existing content, set up a collaborative editing environment with AI-assisted workflows for incorporating interactive elements and styled explorations from Figma, and built custom components that let the team publish without touching code.

GoInvo homepage showing hero section, featured case study, and client testimonial

The rebuilt homepage: full-bleed photography, featured case study cards, and a client testimonial ribbon.

02

Architecture

Content flows from Sanity Studio through GROQ queries into React Server Components, which are statically generated at build time with incremental revalidation for updates. Only interactive elements (animations, forms, the card morph system) hydrate on the client.

Why Next.js 16 App Router: React Server Components eliminate client-side JavaScript for content-heavy pages. Streaming and partial prerendering let the shell load instantly while dynamic content hydrates progressively. Nested layouts made the persistent header/footer with animated page transitions straightforward.

03

Giving Editors Control

Before: a writer finishes a vision article and sends it to a developer. The developer converts it to JSX, adds images, commits to Git, waits for a build, and deploys. The writer sees the result hours later and asks for three changes. Each round-trip costs a day.

After: the writer opens Sanity Studio, types directly into a rich text editor, drags in images, and sees a live preview of the published page updating as they type. They hit publish. The site updates in seconds. No developer needed.

Getting there meant modeling everything the studio publishes (case studies, articles, team bios, job postings, testimonials, client logos, services, and site-wide settings) as structured Sanity documents. The portable text fields support custom blocks for pull quotes, image grids, and full-bleed sections so editors can create rich layouts without writing markup.

Beyond the schema, I designed the Studio experience itself around how the team actually works. Previews are separated into their own sections so editors see exactly where new content will appear on the live site. Each section includes a blank card with an “Add” button, so a studio member browsing the Vision page can create a new article right there, in the context where it belongs, instead of navigating the Studio’s document list and hoping it lands in the right place. Small UX decisions like these turned the CMS from a content database into a workspace that matches the team’s mental model.

Sanity Studio showing side-by-side live preview and content editing for a case study

The editor’s view: content on the left, live preview on the right. Changes appear instantly.

04

Making Navigation Feel Physical

A healthcare design studio needs its site to feel crafted. Hard page cuts between case studies undermine that. So when a user clicks a project card, the card’s image morphs into a full-width hero for the new page. Everything transforms in place.

This works because of how the brain tracks objects. The dorsal visual stream maintains continuity when something transforms in place, but registers a “new scene” when content jump-cuts. It’s the difference between opening a door and teleporting to a different room. The morph transition keeps the user oriented.

The hard part was making this work with Next.js. React’s App Router destroys the current page immediately on navigation, which kills any exit animation. The fix was a FrozenRouter that holds the old page in place during the exit, then hands off to the new page once the morph completes. Took days to debug, but the result is seamless.

The card-morph in action. The image expands from the grid into the hero position. No jump cut.

05

Results

0

Dev hours to publish content

9

Content types editors control

0.35s

Page transition duration

AA

WCAG compliance

The site is live and in active use by the studio. Content editors publish case studies, vision articles, and team updates without developer involvement. Pages load with zero client-side JavaScript for static content, hydrating only the interactive layer.

GoInvo Vision page on mobile showing responsive layout with spotlight cards and featured articles

The Vision page on mobile: responsive typography, stacked spotlight cards, and scroll-driven animations adapt gracefully to small screens.

06

What I'd Do Differently

I stopped treating frameworks as black boxes.

The page transition bug had no Stack Overflow answer. The fix was in React’s internal LayoutRouterContext, which I only found by reading the App Router source. Before this project I would have worked around the limitation. Now I go to the source when docs run out.

I design for production editing patterns now, not developer testing.

Sanity’s live events fired on every keystroke, which was invisible in my single-editor local setup. With three editors in staging, the render storms were immediate. I now stress-test collaborative features with realistic editing behavior from day one.

Small defensive patterns compound across the whole pipeline.

Making the Sanity client return empty arrays when unconfigured (instead of throwing) was a one-line change. It unlocked CI in every environment without CMS credentials and removed an entire class of deployment issues. I think more carefully now about which small decisions have outsized downstream impact.