Font rendering, device pixel ratio and browser version all differ between a developer machine and a CI runner, and a screenshot comparison notices all three.
// playwright.config.js
export default defineConfig({
expect: { toHaveScreenshot: { maxDiffPixels: 0 } },
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
})
// and the update, which must run in the same container
// docker run --rm -v "$PWD:/w" -w /w
// mcr.microsoft.com/playwright:v1.28.0-focal
// npx playwright test --update-snapshots
A zero pixel tolerance is achievable with a pinned container and is what makes the check meaningful — a non-zero threshold hides exactly the one-pixel regressions this exists to catch. Self-hosting the fonts in the test image removes the other source of intermittent difference, which is a webfont arriving after the screenshot.