---
title: "Design QA checklist: testing the build before merchants see it"
description: "A design QA process for Shopify apps: states, copy, spacing, breakpoints, keyboard, empty and error paths, permissions, analytics, and how to file issues."
topic: "Design process and tickets"
date: 2026-10-13
updated: 2026-10-13
author: "Geetansh Saini"
url: https://www.geeetansh.com/guides/design-qa-checklist
tags: ["design qa", "uat", "shopify apps"]
---
# Design QA checklist: testing the build before merchants see it

**In short:** A design QA checklist is what a designer runs against the built feature before a merchant sees it: every state, the copy word for word, spacing against the file, each breakpoint, keyboard and screen-reader basics, the empty and error paths, permissions, and whether the analytics events actually fire, all on a real store with real data. The other half of the job is filing each issue so an engineer can fix it in one pass without asking what you meant.

## Why does a designer need to test the build?

Engineers test that the code works. Automated tests check that it keeps working. Neither checks that it matches what the merchant was promised. A button can submit the form correctly and still sit 24 pixels off the file, use the placeholder copy, and vanish on a phone. None of that fails a unit test.

The designer holds the whole picture: the flow, the states, the copy, the reason the screen exists. So the designer walks the build with that picture open on the other monitor. At Social Snowball I owned QA and UAT alongside design. This is the checklist I run, and it is short on purpose. A checklist you can run in forty minutes gets run.

One rule. Design QA is not a review of the design. If you want to change the layout while testing it, write that down separately and finish the pass.

## What is on the design QA checklist?

Ten items, in this order because the early ones are cheap and the later ones need a real store.

| Check | What I am looking for |
| --- | --- |
| States | Empty, loading, error, partial and success all exist and match the file |
| Copy | Every string matches the final copy, including errors and toasts |
| Spacing and type | Padding, gaps, sizes and weights match the file or the design system |
| Breakpoints | The layout holds at phone, tablet and desktop widths |
| Keyboard | Everything reachable and usable with Tab, Enter, Space and Escape |
| Screen reader basics | Labels, headings, focus and announcements make sense read aloud |
| Empty and error paths | The first-run screen and every failure the merchant can hit |
| Permissions and plans | Each role and plan sees what the design says they see |
| Analytics | Every event in the ticket fires once, with the right properties |
| Real data | It all still holds on a store with 3,000 orders and 60-character names |

### States

The handoff should have drawn five states for every screen. I covered that in [developer-ready design handoff](/guides/development-ready-design). In QA you check that each one was built. Trigger them on purpose: disconnect the Klaviyo token to get the error, open the page on a fresh install to get the empty state, start a sync on a large store to catch loading and partial.

### Copy, word for word

Read every string against the file. Button labels, helper text, tooltips, error messages, toasts, the text in the confirmation modal. Placeholder copy survives into production more often than any other handoff mistake. Watch for sentence case turning into Title Case and "affiliates" turning into "ambassadors".

### Spacing and type against the file

Open the Figma frame and the build side by side at the same width. Check the gaps between sections, the padding inside cards, the space between a label and its field, heading sizes and weights. If you build on Polaris, most of this is decided by the component and you only need to check that the right component was used. [Designing a Shopify app with Polaris](/guides/polaris-app-design) covers which ones.

### Breakpoints

Shopify's Built for Shopify [requirements](https://shopify.dev/docs/apps/launch/built-for-shopify/requirements) say to design your app to be responsive and adapt to different screen sizes and devices. Resize the browser from wide desktop down to phone width and watch what breaks. Tables are the usual casualty: columns that should hide do not, or the actions menu falls off the right edge.

### Keyboard and screen reader basics

You do not need to be an accessibility specialist to catch the big problems. The W3C's [Easy Checks](https://www.w3.org/WAI/test-evaluate/preliminary/) page is a first-pass review anyone can run, and its keyboard check is the one I lean on: keyboard focus should be visible and should follow a logical order through the page elements, and you should be able to tab to every element and tab away from it. Put the mouse down and work through the whole feature with Tab, Shift+Tab, Enter, Space and Escape.

Shopify's own [accessibility best practices](https://shopify.dev/apps/best-practices/accessibility) for apps, which are guidance rather than listing rules, ask that all links, buttons, dropdown navigation and form controls can be controlled using a keyboard, that the focus indicator is visible and consistent, and that critical information is announced by screen readers using aria-live. For the screen reader check, turn on VoiceOver or NVDA for five minutes and listen to the form. Unlabelled fields, icon buttons that read as "button", and a success toast that is never announced are quick to hear and quick to fix.

### Empty and error paths

The first-run empty state is the screen every new install sees. Check it on a store with nothing in it, not a store where you deleted the data, because the two often render differently. Then walk the error paths a merchant can actually hit: an expired Shopify session, an API rate limit during a sync, a discount code that already exists, a payout that fails halfway. [Empty state design](/guides/empty-state-design) has what the empty state should contain. QA is where you confirm it does.

### Permissions and plans

Log in as each role the design names: store owner, staff with billing, staff without, a collaborator account from an agency. Then switch plans, including a store whose trial expired yesterday. The build often shows the owner view to everyone and relies on a backend error to stop the rest.

### Analytics events firing

Open PostHog's Activity tab while you click through the feature. PostHog's [Activity docs](https://posthog.com/docs/activity) describe it as a list of the events you have captured and say it is great for checking events are being captured, and the Live view shows a feed of events as they are captured, which you can filter to one event. For each event in the ticket, confirm it fires once, with the properties you asked for. A funnel built on an event that fires on page load instead of on click is worse than no funnel.

### Real data

Everything above gets run once more on a real store. Here is why.

## Why test on a real store with real data?

Staging stores are polite. They have twelve products with short names, four orders and one affiliate called Test. Merchants have 3,000 orders, a product called "Limited Edition Oversized Hoodie (Heather Grey) Pre-Order Batch 3", prices in 14 currencies and an affiliate with an emoji in her name. The layout that held on staging wraps, truncates and overflows on the merchant's store, and the merchant finds it before you do.

So the last pass is on a development store loaded with realistic data, or on a partner's real store with their permission. Shopify's [app requirements checklist](https://shopify.dev/docs/apps/launch/app-requirements-checklist) asks you to provide a link to a development store that showcases your app for review. Build that store once, with long names, several currencies, hundreds of orders and a few deliberately broken records, and reuse it.

Two more checks belong on this pass. The same checklist says to ensure your app functions in Chrome's incognito mode, which catches cookie and session assumptions a logged-in dev machine hides. And if you want the Built for Shopify badge, its performance requirements set Web Vitals targets at the 75th percentile of page loads: Largest Contentful Paint of 2.5 seconds or less, Cumulative Layout Shift of 0.1 or less, Interaction to Next Paint of 200 milliseconds or less.

Run the pass twice: on staging before UAT, when fixes are cheap, and on production within an hour of the deploy, because caching, environment variables and feature flags all behave differently there, and analytics events in particular break between the two. If the feature is behind a flag, turn it on for your own store only.

## How do you file design QA issues so engineers fix them fast?

A QA pass produces a list. How you write it decides whether the fixes take an afternoon or a week.

- One issue per item. "Spacing is off on the settings page" is five issues wearing a coat.
- Title says where and what. "Affiliate table: actions menu clipped at phone width" beats "Mobile bug".
- Expected, with a link to the Figma frame and, for copy, the exact string.
- Actual, with evidence. A screenshot with the problem circled, or a ten-second recording for anything that moves, plus the browser, width, role and plan you used.
- Steps to reproduce, three lines at most. If it needs a specific store state, say which store.
- Severity, from a fixed scale. It is the one field that decides the order of work.

| Severity | Meaning | Example |
| --- | --- | --- |
| Blocker | A merchant cannot complete the task or sees wrong data | Payout total shows the wrong currency |
| Before release | Works, but not as designed, in a way a merchant would notice | Error message is placeholder text |
| After release | Cosmetic, or only visible at the edges | 4px gap instead of 8px between cards |

Put the issues in the same tool as the ticket, as sub-issues or a checklist on it, not in a Slack thread. The structure is the one I use for [product tickets](/guides/writing-product-tickets), cut down to what a fix needs. When the engineer marks an issue fixed, run that line of the checklist again on the deployed build. The second look costs two minutes.

## Where to start

1. Write the ten checks into a template in Linear or Notion and attach it to every feature ticket, so the pass is a checklist to tick, not a memory to rely on.
2. Build one realistic development store: hundreds of orders, long product names, several currencies, every role and plan.
3. For the feature currently in review, run the keyboard check and the analytics check first.
4. File what you find as one issue per item with expected, actual, steps and severity, on the ticket itself.
5. Re-test each fix on the deployed build before you close it.

## Frequently asked questions

### What is the difference between design QA and UAT?

Design QA is the designer checking the build against the design: states, copy, spacing, behaviour. User acceptance testing checks the feature against the merchant's outcome, usually with the acceptance criteria from the ticket and often with a CS lead playing the merchant. Design QA should finish before UAT starts, so UAT finds problems with the idea, not with the build.

### How long should a design QA pass take?

For a feature-sized ticket, about forty minutes on staging and twenty on production, if the checklist is written down and the test store is ready. If it takes a day, the handoff was missing states or copy, and the time is going into decisions rather than checks.

### Should the designer test in the Shopify mobile app as well as a narrow browser?

Yes, when the feature is one a merchant would plausibly open from their phone, like checking a payout or approving an affiliate. A narrow browser window catches layout, but the mobile app has its own navigation and iframe behaviour, and the only way to see it is to open it.

### What if the engineer disagrees that something is an issue?

Point at the design and the ticket. If the build differs from either, it is an issue, and severity decides when it gets fixed. If the design was ambiguous, that is a design gap, so fix the file first and file the issue against the new version. Arguing in the issue thread helps nobody.
