A bad handoff looks complete. Pixel-perfect frames, neat spacing, a shared link. Then you start building and the questions pile up: what’s the empty state, what happens on error, how fast does this animate. A good handoff answers those before you ask, in about ten lines, and it saves a week of back-and-forth.
All the states
One frame is never enough. Every component that touches data needs four drawings: empty, loading, error, and full. The empty state matters most, because it’s what a new user sees first and the one that never gets designed.
If the handoff only has the happy path, that’s not a handoff — it’s a wish. Ask for the other three states by name before you write any markup.
Names, not pixels
Don’t hand over #6D28D9. Hand over --primary. Colors, spacing, and type should arrive as token names that already exist in code, so nothing gets eyeballed or re-derived. States belong in the same vocabulary:
type ButtonState =
| 'default'
| 'hover'
| 'loading'
| 'disabled';Now “the third one” never means two different things across a review, and the design file reads like the component you’re about to write.
Motion in numbers
“Make it smooth” is not a spec. Motion needs numbers: a duration in ms, an easing curve, and the trigger. “Fade and rise 8px over 150ms, ease-out, on mount” is something you can build once and get right.
Include the reduced-motion behavior too. If nobody says what happens when a user turns motion off, someone forgets it entirely and the accessibility audit finds it later.
Who owns what
The handoff isn’t done at merge — it’s done when you both agree who owns the component next. Write one line: design owns the visual spec, engineering owns the states and a11y, and changes go through a PR you both read.
That’s the whole checklist: four states, token names, motion in numbers, and an owner. Ten lines pasted into the ticket, and the mockup stops being a mystery.