The pull request had three approvals, a green CI build, and a design lead's thumbs-up emoji on the preview link. It shipped. The next morning, a support ticket landed with a screenshot: the primary CTA on the pricing page was eight pixels too far right, close enough to collide with a tooltip arrow on narrow laptops. Nobody on the review had seen it.
The screenshots in the PR description looked fine. Code review is poorly suited to catch this kind of defect. Reviewers read diffs; they don't rerender the page in every breakpoint, state, and theme. The handoff from design to development keeps producing these small, specific drifts, and the teams that have stopped shipping them have mostly done one thing: they put a visual check between the merge button and the user.
The Spacing Drift That Only Shows Up at 1280px
A designer sets 24px of space between cards in a grid. A developer implements it with a utility class that resolves to 1.5rem. On most machines, those match.
On a browser with a non-default root font size, or inside a container that inherits a different line-height, the spacing renders at 22px. The PR diff shows a one-line class change. The reviewer reads the class name, confirms it looks sensible, and approves.
No reviewer catches this by reading code. It surfaces when two screenshots of the same component, taken before and after the change, are compared pixel for pixel. A visual regression check flags the two pixels of movement and the shift in the shadow underneath, and the reviewer sees the actual rendered difference instead of a class name.
The pattern is familiar enough that it has its own literature. For a walk through how the handoff reads like a contract and the codebase breaks it in dozens of small places the specs never anticipated, read Porto Theme on why design handoffs break in code. That coverage frames design tokens and shared components as the enforceable version of the contract, and the framing matches what the diff tools catch.
The State Nobody Drew in Figma
Mockups usually ship with a default, a hover, and sometimes a disabled state. Production has to handle a lot more: loading, empty, error, long-string truncation, keyboard focus, right-to-left mirroring, and the combinations between them. Any of those can look fine on the designer's reference device and wrong three breakpoints later.
This is where component libraries earn their place. A story in Storybook pins each state as a known-good rendering. When a developer touches the button component to add a new variant, the test suite rerenders every existing story and compares.
If the focus ring on the destructive button shifted by a pixel because of a new box-shadow rule, the check catches it before the branch merges. That's the loop teams reach for when they want the design system to actually hold.
The Review Was Looking at the Wrong Thing
Pull request review is good at some things and poor at others. The published Google guidelines name the territory clearly: design, functionality, complexity, tests, naming, comments, and style. Nowhere on that list is rendered output across every viewport the product supports. That's a recognition that code review and visual verification are different jobs.
Teams that conflate the two keep finding the same class of bug in production. The fix is not harder reviews or longer checklists. It's a separate check that answers a separate question: did anything on the page look different after this change, and if so, did we mean it?
Start Here if You're Building This From Scratch
A full visual regression setup can feel like a lot. It doesn't have to arrive all at once. The sequence that tends to work:
- Lock the tokens. Move hard-coded colors, spacings, and radii into tokens that both the design file and the code consume. Until the tokens are the source, nothing downstream is enforceable.
- Pin the components. Put every stateful variant of your top twenty components into stories or fixtures. These become the frames the diff tool compares against.
- Wire the diff into CI. Every pull request should render the component set and post a visual check back to the PR. If a human has to remember to run it, it won't get run on the Friday afternoon merges that cause the most damage.
- Set a baseline review ritual. When the diff tool flags a change, someone has to accept or reject it. That person is usually the designer, not the engineer. Make the handoff explicit.
- Expand to pages last. Full-page visual tests are brittle and noisy. Start at the component level, where the diffs are small and the signal is clean, and only promote the few page-level flows that need end-to-end coverage.
None of this replaces design review, code review, or QA. It fills the specific hole those three leave behind, the one where the spec said 24px, the code said 1.5rem, the review said LGTM, and the user said the button is in the wrong place. If you want help putting the pieces together for your stack, that's the kind of work a custom engineering team tends to scope in a single call.


