A large share of technology project payment disputes come down to one question nobody wrote down clearly enough: were the acceptance criteria actually met. That is a staggering amount of lawyer time, invoice time, and executive time spent arguing about a word the contract was supposed to define. "Accepted" is the trigger for payment, the start of warranty, the clock on revocation. If it means "the sponsor nodded in a demo," the parties will disagree later, and the codebase will be the evidence.
The design-to-development handoff is where that disagreement usually hides. Figma says one thing, production ships another, and the SOW is silent on which one wins. The sections below walk through the distinct cases where "accepted" breaks down, and what the SOW should say about each one before the first sprint starts.
The Mockup Says 16px. Production Shipped 14
The most common fight is also the smallest. Padding is off by two pixels. A heading uses the wrong weight. The accent color on hover is a shade lighter than the file.
Individually these are nothing. In aggregate they are the reason the client refuses to sign the acceptance letter.
Spec-level drift is usually not a developer being careless. It is the SOW failing to say what "matches the design" means. Pixel-level identity is not a realistic standard across every viewport and every browser.
What is realistic is controlled fidelity: a tolerance written into the contract, with design tokens as the source of truth. If the SOW names the token library and says the shipped build must consume those tokens for spacing, type, and color, the argument stops being aesthetic. It becomes a checkable fact. Porto Theme's coverage of how shipped interfaces drift from the approved design walks through why the token layer, not the Figma file, is the version the contract can enforce.
The States Nobody Drew
A second kind of drift is harder to catch because the designer never produced the artifact to compare against. The empty state. The loading state, the error toast when the API returns a 500, the field with 94 characters in a label built for 40.
These show up in production because the developer had to invent them, and they get rejected at acceptance because the client sees them for the first time in the demo. The fix sits in the SOW's definition of a deliverable. Each screen listed in scope should carry a required set of states: empty, loading, error, long-content, disabled, focused. The acceptance checklist should treat a missing state as a nonconformity, not a change request. If the file does not contain them, the contract should say the designer, not the developer, owes them.
The Shared Component That Isn't Shared
The third case is structural. A design system and component library both exist, and three pages in production still render a one-off button because the developer under deadline wrote a bespoke component instead of importing the shared one.
The pixels may look right while the system underneath is already fragmenting, and a SOW that only tests the output misses this entirely. A SOW that treats the component library as the deliverable, and makes "all interactive elements consume the shared component" an acceptance condition, catches it on the first review.
The library is the enforceable artifact. The rendered page is one instance of it. Here is what the acceptance clause should require:
- Token conformance. Spacing, type, color, radius, and shadow values in the shipped build resolve to the named token set, not hard-coded values.
- Component reuse. Buttons, inputs, modals, and navigation render from the shared library, with any one-off component flagged and justified in the PR.
- State coverage. Every screen in scope demonstrates the required states on staging before the acceptance window opens.
- Visual regression baseline. A snapshot suite exists at delivery, and any diff above the agreed tolerance blocks sign-off until it is resolved or waived in writing.
Write the Cure Cycle Before You Need It
Even a well-written acceptance clause will trip at some point, because software is not a bolt that either fits or doesn't. The SOW needs a path for "close, but not yet." Two cure cycles before escalation is a common shape in technology agreements, and for most design-fidelity disputes that is the right pattern: a written deficiency list, a defined window to fix, a re-test against the same checklist, and only then a termination or holdback discussion.
Writing all of this down is what makes the demo boring instead of adversarial. When the acceptance criteria are a token audit, a component-reuse check, a state coverage matrix, and a visual regression report, nobody is arguing about whether the shade of blue is close enough. The contract is doing the work it was supposed to do, and the codebase has stopped breaking it.
