Automating Visual Regression with Storybook and Playwright In short: Chapter 4 asked you to write a story per meaningful state. Those stories are already isolated, deterministic renders — point a headless browser at them and you have a visual regression suite without writing test cases. Unit tests verify logic. Visual regression verifies what actually appears. It is the gate that catches the from the previous lesson. How it works Three steps, and the second is where teams go wrong: 1. Baseline. On , a headless browser renders each state and captures a screenshot. That is the reference. 2. Comparison. On a pull request, the same states are rendered in an identical environment and captured again. 3. Diff. The images are compared pixel by pixel. Beyond a threshold, the build fails and a diff image is produced. Step two carries the whole thing. Fonts render differently on macOS and Linux; anti-aliasing differs by GPU. Compare a screenshot from a developer's Mac against a baseline from a Linux CI runner and every test fails, for no reason. We come back to this. Layer 1: component stories Your Chapter 4 stories are already the right shape — isolated, no routing, no HTTP, one meaningful state each. Then one Playwright spec walks every story: Every story you wrote for documentation is now a test case. Add a story, get a test. Test every theme Chapter 6 gave the platform themes, and a token change can break exactly one of them. Loop: Test narrow widths Chapter 5's container queries mean a component's layout depends on its container, not the viewport. Wrap the story in a fixed-width box and snapshot at the widths that matter: The 400px boundary is where the tanker card switches from column to row. That is exactly the case worth pinning. Layer 2: whole pages Storybook is right for a card. It is the wrong tool for — mocking a router, a facade, and an HTTP layer to satisfy a story produces a fixture that proves nothing about the real page. For pages, run Playwright against the actual application with the network mocked: Mocking at the network layer rather than by providing fakes is deliberate: the adapters, facades, and components from Chapter 7 all execute for real. You are testing the assembled application, which is where Chapter 6's global styles and stacking contexts actually exist. The two layers catch different things: Stories Page snapshots --- --- --- A component's own states Yes No Every theme and width, cheaply Yes Expensive Global CSS collisions No Yes Stacking and z-index in context No Yes Grid and page layout No Yes Runs in Seconds Minutes Run stories on every pull request. Run page snapshots on the affected applications only — lesson six makes that affordable. Making it deterministic Flaky visual tests get disabled within a month, so this section is the difference between a gate and an annoyance. Pin the environment. All screenshots — baselines and comparisons — must come from one containerised environment: Pin the exact tag. A Playwright image update can change font rendering and invalidate every baseline at once. Never generate baselines locally. This is the rule people break, and it is the single biggest cause of a visual suite being abandoned. A baseline captured on a developer's machine will differ from CI on font smoothing alone. Approving an intentional change means running the update in CI : Label the PR, the bot regenerates baselines in the correct environment and pushes them. The developer reviews the image diff in the commit. Kill nondeterminism at the source: Plus a stylesheet loaded only in visual tests: And anything genuinely dynamic — clocks, relative timestamps, generated ids. "But I thought visual regression testing was too flaky to be worth it" Plenty of teams have tried this, drowned in false positives, and turned it off. That experience is real and the conclusion is a fair reading of it. The thing worth separating is that almost every flake has a specific, fixable cause , and there are only about five of them: Cause Fix --- --- Baselines from a developer's machine Generate only in the pinned container Fonts still loading at capture Animations mid-flight + a reset stylesheet Timestamps, clocks, random ids , or fix and seed the data Threshold at exactly zero Teams that abandon visual testing have usually hit the first one and concluded the technique is unreliable, when the actual finding was that two different machines render fonts differently — which is true, known, and solved by containerising. The threshold row deserves a note. Zero tolerance sounds rigorous and is counterproductive: sub-pixel anti-aliasing differences will trip it. A ratio around 0.2% ignores a handful of stray pixels while still catching anything structural. The clipped flyout from the previous lesson moves thousands of pixels — it is not a close call. There is one honest cost that does not go away: binary baselines in git . A few hundred PNGs is tens of megabytes, and every intentional design change rewrites a chunk…