Design QA is the practice of verifying that what engineering built matches what the design specified — not just visually, but in behavior across states, edge cases, and devices. It is not a final "looks done" glance before launch. It is a structured pass through every screen state, using a checklist, before the feature ships.
Quick answer: Design QA means checking behavior against a spec — every state (empty, loading, error, success), every breakpoint, and every accessibility requirement — not just comparing a screenshot to a Figma file. Run it before code freeze, not after.
Most PMs treat design review as a one-time event: the designer shares mocks, the PM nods, engineering builds, and someone eyeballs the result before release. That approach catches maybe 30% of what actually ships wrong. The other 70% lives in the states nobody screenshotted — the empty list, the 500 error, the field that's 40 characters instead of 4, the modal on a 375px viewport. This piece gives you a repeatable process to catch those before your users do.
Why "Looks Done" Isn't the Same as "Behaves as Specified"
A screen can match its Figma file pixel-for-pixel and still be functionally wrong, because static mockups only ever show one state at a time — usually the happiest one. The gap between "looks done" and "behaves as specified" is where most post-launch bugs live.
Designers typically produce three to five key-state frames per flow: empty, populated, and maybe one error toast. Engineering then has to infer the other 80% of real-world states — pagination edges, partial failures, slow networks, permission-denied views — often without a spec to check against. Design QA closes that inference gap by making every state an explicit, checkable item rather than an assumption.
This matters more as products mature. Jakob Nielsen's usability heuristics — specifically "help users recognize, diagnose, and recover from errors" and "visibility of system status" — are consistently among the most-violated in shipped software, precisely because error and loading states are the least-designed and least-QA'd parts of any interface. If you've never formally reviewed how a screen degrades, you don't actually know if it's done.
The Three Failure Modes
Most hand-off gaps fall into one of three buckets:
- Missing states — the design never specified what happens when data is empty, loading, or partial.
- Silent behavior drift — engineering made a reasonable interpretation that diverges from intent (e.g., truncating text differently than designed).
- Responsive collapse — the layout was designed at one breakpoint and never verified at the others.
Naming which failure mode you're looking at helps you file a sharper bug report later, which we'll get to. For now, treat "looks done" as a necessary but wholly insufficient bar — a point echoed in the broader product design and UX guide, which frames design maturity as a spectrum from decoration to systemized intent.
Building the State Matrix: What "Complete" Actually Means
A state matrix is a table listing every meaningful condition a screen or component can be in, cross-referenced against whether a design and a spec exist for it. Building one before development starts — or retrofitting one during QA — is the single highest-leverage design QA activity a PM can do.
Here's a minimal state matrix for a typical list or dashboard screen:
| State | Design exists? | Spec'd behavior | Common gap |
|---|---|---|---|
| Empty (no data yet) | Often | CTA to first action | Generic "no data" text, no CTA |
| Loading | Rarely | Skeleton vs. spinner, timing | Layout shift when content arrives |
| Populated (typical) | Always | — | — |
| Populated (max/overflow) | Rarely | Truncation, scroll, pagination | Text overflow breaks layout |
| Partial error | Rarely | Which parts still render | Whole screen fails on one bad row |
| Full error | Sometimes | Retry action, message tone | Dead end, no recovery path |
| Permission-denied | Rarely | Redirect vs. inline message | Blank screen, no explanation |
| Offline/slow network | Rarely | Stale-data indicator | Silent failure, no feedback |
Run this matrix against every screen in the flow, not just the primary one. A checkout flow, for instance, needs this matrix for the cart, the payment step, and the confirmation screen independently — each has its own empty/error/edge behavior. This is the same discipline behind mapping a customer journey end-to-end rather than optimizing one touchpoint in isolation.
Edge Cases Worth Naming Explicitly
Beyond the matrix, a short list of edge cases catches the long tail that state tables miss:
- Extreme content lengths — a 200-character name, a zero-length field, a number with 12 digits.
- Concurrent actions — two users editing the same record, a double-submit on slow network.
- Boundary counts — exactly zero items, exactly one item (singular vs. plural copy), exactly the pagination limit.
- Interrupted flows — user navigates away mid-form, session expires mid-task.
- Localization stress — German or Finnish compound words, right-to-left text, currency formatting.
You don't need every edge case designed. You need every edge case decided — even if the decision is "show a generic fallback." Undecided is what turns into a bug report three weeks post-launch.
The Hand-Off Checklist: Five Categories to Verify Before Code Freeze
A hand-off checklist works only if it's specific enough to catch real gaps and short enough that people actually run it. Five categories cover the vast majority of design-intent bugs: states, edge cases, empty/error, responsive, and accessibility.
1. States and Interaction Behavior
- Every interactive element has defined hover, focus, active, and disabled states — not just default.
- Transitions and animations match spec timing (a "subtle" fade shouldn't take 800ms).
- Form validation triggers at the specified moment (on blur vs. on submit vs. live).
- Multi-step flows preserve state correctly on back-navigation.
2. Edge Cases
- Text truncation behaves as designed (ellipsis, line clamp, tooltip-on-hover) at real content lengths, not lorem ipsum.
- Lists handle zero, one, and maximum items correctly, including plural/singular copy.
- Numeric fields handle negative, zero, and very large values per spec.
3. Empty and Error States
- Every empty state has a message and, where appropriate, a next action — not a blank rectangle.
- Error messages are specific enough to act on ("Card was declined" vs. "Something went wrong").
- Partial failures degrade gracefully — one broken widget shouldn't blank the whole page.
- Retry and recovery paths exist and actually retry the failed action, not the whole flow.
4. Responsive Behavior
- Layout is checked at minimum, common, and maximum breakpoints — not just desktop and one mobile size.
- Touch targets meet minimum size (44x44px per Apple's Human Interface Guidelines, 48x48dp per Google's Material Design) on mobile.
- Content reflow doesn't clip, overlap, or force horizontal scroll at any tested width.
5. Accessibility
- Color contrast meets WCAG 2.1 AA (4.5:1 for normal text, 3:1 for large text and UI components).
- All interactive elements are keyboard-reachable and show a visible focus indicator.
- Images and icons carry meaningful
alttext oraria-labels; decorative ones are marked as such. - Form errors are announced to screen readers, not conveyed by color alone.
Treat this as a living checklist, not a one-time template — add a row every time a shipped bug reveals a category you missed. Understanding why these categories matter, not just checking boxes, connects back to basic design literacy for PMs: a PM who understands why touch-target size matters will catch it without being told, rather than mechanically ticking a box.
Testing Across Devices and Breakpoints Without a QA Team
You don't need a dedicated QA team or a device lab to do responsive verification well — three real breakpoints, tested in browser dev tools plus one real mobile device, catch most layout failures. The mistake is testing only the two sizes that were designed (desktop and one phone) and assuming everything between and beyond is fine.
A practical minimum testing set:
| Breakpoint | Why it matters | Common failure |
|---|---|---|
| 320-375px (small phone) | Smallest common viewport | Buttons overlap, text clips |
| 768px (tablet/split-view) | Often untested — "not mobile, not desktop" | Layout awkwardly stretches |
| 1024-1280px (small laptop) | Common real-world laptop width | Content feels cramped, not "desktop" |
| 1440px+ (large desktop) | Wide screens | Line lengths too long, content floats in whitespace |
Use actual browser resize (not just device presets) to catch the in-between widths — a card grid might work at 375px and 1024px but overlap at 600px. This is exactly the kind of gap that silent cognitive load creeps in through: a layout that technically renders but forces the user to work harder to parse it.
Nielsen Norman Group's research on responsive design testing recommends checking layouts at the actual content extremes your data can produce — the longest real name in your database, not "John Smith." Pull five real (or realistically synthetic) records and run the matrix against them, not the three clean examples in the design file.
Filing Design Bugs Without Friction
A design bug report that gets fixed fast names the expected behavior, the actual behavior, the exact state/breakpoint/device it occurred in, and severity — in that order, in one place engineering already checks. Vague reports ("this looks off") get deprioritized because nobody can act on them without a follow-up conversation.
A friction-free bug template:
- State/context: "Empty state, mobile, 375px width"
- Expected: "Should show illustration + 'Add your first project' CTA per spec [link]"
- Actual: "Shows blank white space, no CTA"
- Severity: blocks launch / should-fix-before-launch / backlog
- Evidence: screenshot or screen recording, not a description of one
Keep this in the same tracker engineering already uses — a separate "design bugs" spreadsheet is where reports go to die. And resist the urge to file every deviation as urgent; reserve "blocks launch" for behavior that breaks a task, and let cosmetic drift go to backlog. Filing bugs this way is also a form of design critique done without overstepping — you're pointing at a spec gap, not relitigating a visual decision after the fact.
Where Design QA Fits in the Release Timeline
Design QA works best as two checkpoints, not one: a state-matrix review right after design hand-off (catching missing specs before a single line of code is written), and a full checklist pass at code freeze (catching implementation drift). Doing it only at the end means every gap you find is now a rushed fix under launch pressure.
Bringing a Single Source of Truth to the QA Pass
Design QA breaks down fastest when the spec itself is scattered — a Figma comment here, a Slack thread there, an outdated PRD nobody re-reads. The checklist above only works if everyone is checking against the same version of "what was intended."
This is the specific problem Prodinja's Spec Studio is built around: it maintains the PRD as a living document with PR-style diffs, so when a requirement changes, the spec changes visibly rather than silently. Its engineering hand-off export packages the spec together with readiness gates — the states, edge cases, and acceptance criteria bundled as one artifact — so a PM running design QA is checking the build against the same source engineering built from, not a memory of a meeting three weeks earlier. It's designed as a way to remove the "which version is current" ambiguity that undermines most hand-off checklists in practice, not a promise that it eliminates review work.
Key Takeaways
- Design QA verifies intent, not pixels — a screen can match its mockup exactly and still behave wrong in states the mockup never showed.
- Build a state matrix (empty, loading, error, permission-denied, offline) for every screen before calling design "complete."
- Name edge cases explicitly — extreme content lengths, concurrent actions, boundary counts — even when the decision is a simple fallback.
- Test at least four real breakpoints, including the awkward in-between widths, not just the two sizes that were designed.
- Accessibility checks (contrast, keyboard nav, alt text) belong in the same pass, not a separate later audit.
- File bugs with expected/actual/state/severity in the tracker engineering already uses, so reports get acted on instead of re-litigated.
- A single, versioned source of truth — not scattered comments — is what makes a hand-off checklist actually enforceable.
Frequently Asked Questions
What is design QA in product management?
Design QA is the process of verifying that a built feature matches its design intent — including behavior across states, edge cases, and devices — rather than just confirming visual similarity to a mockup. It's typically run by a PM or designer before code freeze, using a checklist that covers states, responsive behavior, and accessibility.
How is design QA different from regular QA testing?
Regular QA testing focuses on functional correctness (does the button submit the form?), while design QA focuses on whether the experience matches intent (does the error message match tone and specificity guidelines? does the empty state guide the user to a next action?). The two overlap but design QA catches gaps that functional test scripts don't check for.
What should a design hand-off checklist include?
A solid checklist covers five categories: interaction states (hover, focus, disabled), edge cases (extreme content lengths, boundary counts), empty and error states with recovery paths, responsive behavior across at least four breakpoints, and accessibility basics (contrast, keyboard navigation, screen-reader labels).
How do I write a design bug report engineers will actually act on?
State the exact context (screen, state, breakpoint), the expected behavior with a link to the spec, the actual behavior, and a severity level — supported by a screenshot or recording rather than a text description. File it in the same tracker engineering already checks, not a separate design-only list.
How often should design QA happen during a release cycle?
Run it at two points: right after design hand-off, to catch missing specs before code is written, and again at code freeze, to catch behavior that drifted during implementation. Doing it only once, right before launch, means every gap becomes a rushed last-minute fix.