UI Verify
Blog
7 min read

AI code review can't see the UI: one CSS diff, rendered

AI code review reads the CSS diff, never the rendered page. A real review of a three-line token change, next to what the same PR looks like in a browser.

Igor LuchenkovIgor LuchenkovBuilding UI Verify
AI code reviewVisual testingAI judgeCoding agentsCSS
Left: a tokens.css diff changing --space-3 from 12px to 16px, and an AI code review saying the change will make buttons visibly larger, then: Nothing else blocking. Right: the same pull request rendered, where one product card's Add to cart button wraps onto two lines and the next card's does not.
On this page
Our AI code reviewer already checks the style changes.

That is the most reasonable objection to visual testing in 2026, and it comes from teams that have done the work. A review bot runs on every pull request before a human opens it. The lead reads the bot's review instead of the raw diff, throws away the comments that are wrong, and approves. A team lead at an online retailer described that loop to me: the bot checks each incoming PR against the team's agreed conventions, it sometimes writes nonsense, she skims what it flagged and merges. Other teams tune the bot to their design system, so it checks that components, classes and props are used the way the library intends. It feels like coverage, and for code it is.

The gap is in the input. Every one of those bots reads text: the diff, the PR description, and if it has repository access, the files around the diff. None of them opens a browser. So I wrote a small pull request whose diff reads like housekeeping, gave it to an AI reviewer, and rendered it.

What does an AI code reviewer actually see?

An AI code reviewer sees the pull request as text: the changed lines with a few lines of context, the title and description, and, in the better setups, read access to the rest of the repository so it can follow a symbol to where it is used. That is enough to catch a wrong condition, a missing null check, a leaked secret, or a token renamed in one file and not another. What it never receives is the rendered page. Layout is computed by the browser from the CSS, the content, the viewport width and the font's actual glyph widths, and only the first of those four is in the diff.

A three-line CSS diff that reads like cleanup

The pull request is staged in a scratch repo for this post: a product grid with four cards, each footer holding a price, an Add to cart button and a save button in a flex row. The change does two tidy things. It swaps two hardcoded 12px values in the card for the --space-3 token, and it moves --space-3 from 12px to 16px so the scale lines up with the 8pt grid in the design file. The description says cards get a little more breathing room. Nothing in it would make a human reviewer slow down.

The whole pull request. A magic number becomes a token and the token moves up one step on the scale: the kind of diff that gets approved between meetings.

What did the AI reviewer say about it?

I ran the review with Claude Code in headless mode (claude -p, a Sonnet model), prompted as a review bot and given read-only access to the scratch repo, which is roughly what a hosted review bot has. It did better than many human reviewers would. It searched the stylesheet, found that --space-3 is also used by the title margin, the footer's top margin and the button's horizontal padding, and warned that the change cascades further than the description admits. It even predicted the buttons would get visibly larger, and suggested attaching before and after screenshots. Then it closed with: nothing else blocking.

The review, quoted verbatim with cuts marked, beside the render. The reviewer found every line the token reaches. It could not know that a nine-character price leaves the button too narrow for its label.

Read the review again with the render beside it. Every finding in it is correct. It knew which declarations would grow. It even asked for the one artifact that would have settled the question, because it had no way to produce it. A careful human reading the same diff lands in the same place: probably fine, roomier cards, worth a look. The look is the part nobody does.

What the render shows

Rendered with Playwright in Chromium at 1280 by 520, the same viewport before and after, the footers tell a different story. Each card is about 285 pixels wide. With 16px of padding on both sides, a 16px gap on both sides of the button and 16px of padding inside it, the button is what gives way. On the two cards with long prices, $1,249.00 and $2,099.00, Add to cart breaks onto a second line and the button grows from 35 to 52 pixels tall. On the two cards with short prices it fits. The row that used to line up across the grid now has two tall buttons and two short ones.

Baseline, pull request, pixel diff. 89,828 pixels changed at 2x (3.37% of the frame), and two of the four buttons now wrap. Which two depends on the product data, not on the CSS.

That last point is why no reviewer that reads text would catch it, however good it gets. Whether the label wraps depends on the width of a price string that comes from data, the glyph widths of the font that actually loaded, and the width of a grid column at one particular viewport. Change the catalog and different cards break. Open the page on a wider monitor and none of them do. To predict it from the diff, a model would have to run a layout engine in its head against content it was never shown.

Why isn't a pixel diff enough on its own?

A pixel diff answers whether anything moved, and on this pull request everything moved. The wider padding shifted every title, every price and every button, so the diff lights up all four cards, the two that are fine and the two that broke. That is correct and not very useful: the description said the cards would get more room, so most of those pixels are the change working as intended. Somebody still has to look and separate the roomier cards the author asked for from the wrapped buttons nobody asked for.

That separation is what the AI judge in UI Verify is for. For each changed story it looks at the baseline, the new capture and the diff, reads the pull request's stated intent, and returns a verdict, likely regression or likely intended change, with a short reason. Regressions sort to the top of the review, and intended changes are one action to accept. It advises and never moves a baseline by itself. The details are in the AI judge. I did not push this scratch repo through UI Verify, so I won't put words in the judge's mouth about this particular grid. The point of the comparison is the input: the code reviewer gets the diff, while the judge gets the pixels as well.

The same pixels are available to your agents, not only to a person in a dashboard. UI Verify exposes each changed story, its cropped before and after, and the judge's verdict over MCP, so the coding agent that wrote the token change, or an agent reviewing it, can look at the render it was missing instead of reconstructing the layout from CSS. On this pull request that is the gap between a reviewer writing that the buttons will get visibly larger and an agent seeing the two wrapped buttons, then fixing them or flagging them before anyone opens the review. The setup is in triage with your agent.

Two related questions get their own posts. Telling a real change from a render that disagrees with itself is covered in can AI tell a real regression from a flaky diff. Making the verdict hold the merge is covered in AI visual review in CI.

Is an AI code reviewer still worth running on UI pull requests?

Yes. The review above earned its tokens: it traced a design token through a stylesheet faster than a person would, and on a real codebase that tracing is what catches a renamed variable or a component used against the library's rules. Keep it. Just stop counting it as visual coverage. The two reviewers read different artifacts and fail in different places, so they split the work cleanly:

  • The code reviewer reads the text: logic, naming, security, conventions, and which files a token or prop change reaches.
  • The visual check reads the render: it screenshots the states you already cover, diffs them against an accepted baseline, and says which changes match the pull request and which do not.
  • The human lead reads both summaries, throws out whichever comment is wrong, and spends their attention on the one change neither reviewer can explain.

If a coding agent opened the pull request, that MCP connection lets it take the visual half of the split itself: it accepts the changes that match its own intent and flags the rest for you, from the same terminal it wrote the code in. The triage visual changes skill runs that loop as one command.

So the objection is half right. The AI code reviewer does check the style changes, as text, and it may tell you the buttons will get bigger. Whether they still fit on one line is a question about pixels, and the only way to answer it is to render the page and look.

Give your review bot a pair of eyes

Keep the AI code reviewer on the text. UI Verify renders every pull request, diffs the screenshots against your baseline, and an AI judge reads the PR's intent to tell an intended change from a broken layout.

Start for free

No credit card required.

ShareXLinkedIn
Related skill

Triage a build from your agent

Bucket real regressions vs noise and accept baselines, over MCP.