How to Review a UI Change When There Is No Screenshot
The pull request arrives with a title like "Refine checkout empty state" and a description that reads, in full, "Aligns spacing and improves hierarchy." There is a Figma link, but the file was last touched eleven days ago and the frame inside it is a grey rectangle with three lines of placeholder text. The reviewer is asked to approve a change nobody can see. This happens constantly, in teams that ship every day, and the standard response, which is to say "looks good to me" and move on, is how a hundred small regressions get born.
Reviewing a UI change without a screenshot is possible. It just requires giving up the fantasy that visual review is somehow separate from the rest of code review. The diff is the screenshot. It is a slower, uglier, more honest screenshot, and reading it well is a skill that most teams never bother to teach.
Start with the diff, not the description
The description is the author's theory of the change. The diff is the change. Those two things drift apart more often than anyone likes to admit, usually because the author made a last-minute adjustment and forgot to update the paragraph above the code. A reviewer who reads the description first and the diff second has already been primed to see what the author intended, which is the worst possible posture for catching what the author actually did.
So the first pass is mechanical: open the diff, ignore the title, and read the changed lines in order. Mark every line that touches a layout property, a class name, a conditional render, or a string of user-facing copy. Those four categories cover most of what a UI change actually is. Everything else is plumbing.
Count the pixels nobody wrote down
A spacing change in a stylesheet is a sentence in a language with no grammar checker. The reviewer's job is to reconstruct the geometry from the numbers. If a padding moves from 12 to 16, the question is not whether 16 is nicer than 12. The question is what sat adjacent to that box and whether the gap between them just grew by four pixels in a place where the design system promised an eight-pixel rhythm.
This kind of arithmetic is tedious and it is also the only way to catch the classic silent breakage: a margin that was compensating for a sibling's margin, now doubled because someone removed the sibling's. The screenshot would have shown the gap. The diff shows the cause. Reviewers who only ever look at screenshots learn to describe problems; reviewers who read the spacing learn to predict them.
Look for the state that was not updated
Most components have more states than the happy path. There is hover, focus, disabled, loading, empty, error, and the awkward one where the data arrives but arrives wrong. A UI change that only touches the default state is a change that has quietly decided the other states do not matter, and the diff will show this decision plainly: one class added, no sibling classes touched, no conditional branches modified.
The reviewer's move here is to list the states out loud, in the comment, and ask which ones the author verified. Not as an accusation. As a checklist. "This touches the default card but not the skeleton or the error card; did those get looked at?" is a question that costs fifteen seconds and prevents the screenshot-free version of a bug that ships on a Friday.
Read the copy changes as if they were headlines
String changes hide in diffs because they look like noise. A button label going from "Continue" to "Save and continue" is a two-word edit that changes the width of the button, the wrap behavior at narrow viewports, and the tone of the entire flow. None of that is visible in the diff, which is exactly why it needs to be flagged.
The habit worth building is to read every changed string at three widths in the head: a 320-pixel phone, a 1440-pixel laptop, and whatever the longest translation happens to be. German and Finnish are the traditional stress tests, but any language with compound nouns will do. A label that fits in English and overflows in German is a bug the screenshot would have caught in one glance and the diff catches only if the reviewer thinks to ask.
Ask what the change is adjacent to
No component exists alone. The card sits in a grid, the grid sits in a page, the page sits in a shell with a sidebar that collapses at a breakpoint. A padding tweak on the card is a padding tweak on every card in every grid on every page where that component appears, and the diff will not say so because the diff only knows about the component.
This is where the reviewer's knowledge of the codebase earns its keep. The grep is the tool: search for the component name across the repository and count the call sites. If the number is larger than the author's description implies, that is the comment. "This component is used in four places, including the settings drawer; does the new spacing still work there?" is a better review than any screenshot annotation.
When a preview build is cheaper than an argument
Some changes genuinely cannot be judged from a diff. A new illustration, a color shift in a gradient, a typeface swap: these are visual decisions, and asking a reviewer to approve them blind is asking for a coin flip. The right move in those cases is not to squint harder at the stylesheet. It is to spend ninety seconds standing up the branch locally, or to ask the author to attach a preview deployment link, which most CI systems produce automatically and most authors forget to mention.
The point is not that screenshots are bad. The point is that a screenshot is a claim about one viewport, one state, and one moment in time, and a reviewer who treats it as complete evidence is being flattered into laziness. The diff is incomplete in a different direction. Together they cover each other's blind spots, which is why the best review comments tend to be the ones that say "the code looks right; here is the state I could not verify."
Write the comment as a description of what will be seen
Here is a technique that sounds strange and works remarkably well. When the screenshot is missing, the reviewer writes the screenshot. In the comment, describe what the change will look like when rendered: "The empty state now sits 24 pixels below the header, the icon is 48 square, and the copy wraps to two lines on a 375-wide screen." Then the author reads it and either confirms it or corrects it. Either response is useful, and the second one is the whole reason the technique exists.
This flips the review from passive approval to active reconstruction. The reviewer who can describe the rendered result from the diff has understood the change in a way that no amount of "LGTM" ever demonstrates. And the author who reads a wrong description learns something about how legible their own code is, which is a lesson that compounds across a career.
The reviewer's real deliverable is a list of unknowns
A review without a screenshot cannot certify that the change looks good. It can do something more valuable: it can produce a precise list of what remains unverified. Three states, two breakpoints, one translation, four call sites. That list is the artifact. It tells the author exactly where to point a browser, and it tells the team exactly how much risk the merge carries.
Approving a UI change without seeing it is fine, provided the approval is honest about its own limits. "Code path looks correct; I have not seen the loading or error states, and the copy change may wrap on mobile" is a review that a person can act on. It is also a small act of resistance against the idea that review is a rubber stamp applied at the end of a pipeline. The stamp is cheap. The list of unknowns is the work.
Somewhere in a repository there is a pull request from three months ago, merged with two approvals and no screenshot, that moved a border radius from four to six on a component used in eleven places. Nobody caught it. Nobody will, until a designer opens the settings drawer on a tablet and frowns at something they cannot quite name. The diff was there the whole time, waiting to be read.

