The deploy went out Friday at 5:47pm. Tests green. Lighthouse score unchanged. Sentry quiet for the whole weekend. Stripe webhooks happy. By every metric the engineering org tracks, the release was clean.
Saturday at 10:14am the client emailed.
"Hi – the pricing page looks broken? The middle plan button is a different shade of green than the others, and the CTA on /signup is now centered while every other CTA on the site is left-aligned. Did we change something?"
We had. Just not what anyone thought.
What actually happened
A junior engineer renamed a CSS custom property from --btn-primary to --color-action-primary and ran a project-wide find-and-replace in the design-tokens package. The replacement caught 47 of 48 usages. The 48th lived inside an old <style> block in a marketing component that had been copy-pasted from a Figma export six months earlier and never moved into the token system.
That one component is the middle pricing plan button.
The <style> block still referenced var(--btn-primary). After the rename, that variable was undefined. CSS doesn't throw on undefined custom properties – it just silently falls back to the initial value, which in this case meant the browser's default transparent. Combine that with the button's inherited background and you get a faded, off-shade green. Visible to anyone looking at the page side-by-side. Invisible to every automated system.
The /signup CTA alignment came from the same PR. A separate Tailwind class refactor moved text-center from a parent container to the specific elements that needed it – except the CTA wrapper, where the test snapshot still passed because the visual DOM was unchanged. Layout-only regressions don't surface in DOM snapshot tests.
Why none of the existing systems saw it
| System | What it watches | Why it missed this |
|---|---|---|
| Sentry | Thrown exceptions, network errors | CSS doesn't throw. The page rendered. |
| Lighthouse CI | Perf budgets, a11y heuristics | Same colors-in-button, same DOM order. Score unchanged. |
| Jest snapshots | Rendered DOM markup | Markup was identical. Only the visual styling changed. |
| Manual QA | What humans look at | We didn't do a smoke test after a "tiny rename PR." |
| Client UAT | What clients look at | The PR shipped Friday night. UAT happens Monday morning. |
This is the gap. Error monitoring catches what throws. Performance monitoring catches what slows down. Snapshot testing catches what changes structurally. Visual consistency – the actual look-and-feel – sits in a blind spot, and it's the one your client notices first because it's the one that breaks their trust.
The cost breakdown
The $100 in the title was the audit tool we panic-bought at 11am Saturday after the second client follow-up. It scanned the whole site, gave us a generic "design system audit" report, and didn't catch the actual regression because its heuristics look for accessibility issues, not consistency drift.
The real cost was bigger.
- 8 hours of weekend engineering time. Unbilled.
- A 35-minute apology call with the client.
- The client's next email opened with "can we have a process for this?" – which is the polite version of "we don't trust your QA anymore."
The fix itself took 6 minutes. Re-add the missing token alias and one text-center class. The expensive part was the trust repair.
What would have caught it earlier
A consistency scan of the production deploy compared against the previous deploy. The middle button's color would have shown up as a delta – different RGB value, same DOM position. The /signup CTA's bounding-box position would have shifted, even though the markup didn't.
This is exactly the gap visual consistency scoring fills. It doesn't replace Sentry, Lighthouse, or your snapshot tests. It plugs the hole all three of them share.
The principle is simple: if the rendered page is meaningfully different from the previous version in a way no human approved, you want to know about it before the client does. Especially on a Friday night.
Try it on your own site. Paste your URL into vizipy.com and get a 0–100 consistency score in 30 seconds. If a similar token-rename happened on your last deploy, you'll see it.