Playwright MCP over the Chrome extension for checking a frontend
Checking a frontend against a design with Claude Code, the claude-in-chrome extension kept handing back screenshots that looked broken when the build was fine, and verdicts that called an actually broken page fine. Moving the render-and-capture step to the Playwright MCP fixed both, and they turned out to be two unrelated bugs sharing one symptom.
Key takeaways
- The claude-in-chrome extension often leaves the tab backgrounded, freezing requestAnimationFrame so entrance animations stay stuck at opacity zero in screenshots.
- A frozen animation reads as missing content to Claude, producing a false broken verdict on a build that is actually fine.
- Even a clean capture gets rubber-stamped by a single-pass look-right check, since vision models are documented as biased toward passing.
- Playwright exposes controls the extension does not: disabled animations, reduced motion, a real ready signal instead of networkidle, and 2x-scale full-page capture.
- The durable fix is a reading discipline, describe first, checklist for breakage, cite evidence, cross-reference the DOM, now encoded by default in Flight Deck's design-fidelity skill.
Checking a frontend against a design with Claude Code means, at some point, taking a screenshot and asking whether it matches. I did that render-and-capture step through the claude-in-chrome extension for months, because it was already there and already connected to my Chrome session. Then I got tired of Claude calling a visibly broken or incomplete frontend fine, sometimes on the same page where I had already spotted the problem myself in the same tab. Moving that step to the Playwright MCP fixed it, but the more useful part is why, because the win is not the brand name, it is the mechanism.
Two bugs wearing one symptom
Every failed check looked the same from the outside: I ask Claude to verify a page, it comes back with a screenshot and a verdict, the verdict is wrong. But wrong in two directions that have nothing to do with each other. Sometimes the page was fine and Claude called it broken, because the capture itself was bad. Sometimes the page was actually broken and Claude called it fine, because the reading of a perfectly good capture was careless. Fixing one does nothing for the other, and for a while I was debugging both as if they were the same problem.
The false-broken read: a backgrounded tab
The first failure mode traces back to something this very portfolio's own CLAUDE.md already documents as a gotcha: the claude-in-chrome extension frequently leaves the tab it is driving in the background. A backgrounded tab means document.hidden is true, and Chromium throttles or fully pauses requestAnimationFrame for hidden tabs. Every scroll-in or mount entrance animation on this site, and on most sites built with Motion or a similar library, starts at opacity zero and animates in on a frame loop. Freeze that loop before it runs and the screenshot captures the initial state forever: content that is present in the DOM and will render correctly for a real visitor, but reads as absent in the capture. Claude then reports the section as broken, I go check the live page myself, and it is fine. That is a screenshot lying about the actual state of the page, consistently, in one direction.
What Playwright gives you that the extension does not
Playwright's screenshot API has the levers to make a capture trustworthy instead of just convenient. animations: 'disabled' on the screenshot call snaps every CSS and Web Animations transition to its end state before capturing, so a mount animation cannot be caught mid-flight. Setting the browser context to reducedMotion: 'reduce' does the same at the OS-preference level, which also exercises the accessibility path the site should already support. Bringing the page to front before capturing, rather than trusting it stayed focused, removes the backgrounded-tab problem at the root instead of working around its symptom. Fonts matter too: waiting on document.fonts.ready avoids catching a flash of unstyled or fallback text. And networkidle is a trap I fell into more than once, since it waits for zero in-flight requests, which a single long-poll or analytics beacon can hang indefinitely; a real readiness signal, like waiting for a specific element or a short fixed settle after load, is more reliable than a network heuristic that assumes well-behaved traffic. Capturing at 2x device scale, full page and per breakpoint, is the last piece: small type and border details that would be illegible at 1x are exactly the details a fidelity check needs to see.
The false-fine read: rubber-stamping a clean screenshot
The second failure mode is worse, because it survives a good capture. Hand Claude a correctly rendered screenshot and ask, in effect, whether it looks right, and a single pass through that question gets rubber-stamped more often than it should. Vision models are documented to be weak at absolute judgment on a single image with no reference to compare against, and biased toward passing rather than flagging something as wrong. So even once the capture problem was solved, I kept seeing Claude wave through pages with overflow, collapsed containers, or invisible text, because looking right was never actually checked against anything specific.
A reading protocol, not a tool
The fix here has nothing to do with Playwright; it is a discipline for how the analysis itself runs. Describe each region of the page literally before rendering any verdict, so the model commits to what is actually there instead of pattern-matching to a generically normal page. Run an explicit checklist of known breakage signatures instead of one open question: overflow, a collapsed or zero-height container, an unstyled flash, text the same color as its background, clipped or overlapping text, a broken image, a breakpoint's columns collapsing wrong, an element sitting off-screen, an unexplained empty gap, console errors. Require a PASS to cite the specific evidence that earned it rather than accept a vibe. And cross-reference the accessibility snapshot against the screenshot, because that catches the one case a screenshot alone cannot: markup that genuinely exists in the DOM but never actually painted, present in the snapshot and absent in the pixels.
Not "Playwright good, Chrome bad"
I want to be honest about the actual claim here, because it is narrower than the headline sounds. The extension is still a reasonable fallback, and nothing about it makes the false-fine failure mode worse; that one lives entirely in the reading step, tool independent. The real gains came from capture discipline, disabled animations, a real ready signal, correct scale, and from a skeptical reading protocol: describe first, run the checklist, cite evidence, cross-reference the DOM. Playwright happens to expose the controls to do both properly, in a way the extension's screenshot tool does not. If a future version of the extension exposed the same controls, most of this post would stop being a reason to switch tools and start being a reason to configure the one already in hand.
Where this lives now
This stopped being something I re-derive by hand once I folded it into Flight Deck, the open source skill package I maintain. The design-fidelityskill renders through the Playwright MCP by default now, with the claude-in-chrome extension documented as a fallback rather than the default path. A follow-up change adds the capture protocol above, disabled animations, reduced motion, a real ready signal, 2x scale, full page and per breakpoint, plus the read-before-you-score analysis protocol, as the skill's actual verification gate rather than something merely described in a prompt. The repo is public at github.com/CaseReed/flight-deck, under MIT, and the Flight Deck page on this site has the fuller walkthrough.
The lesson generalizes past this one skill. When an agent's verdict does not match reality, check whether the input it saw was even true before assuming the reasoning on top of it is at fault. Half the time here, the reasoning was fine. The screenshot was lying.