My zero-JS page transitions flashed white. Two bugs, two causes, one blend mode I'd never heard of. A developer building the accessible multi-page site accessible-by-default.dev discovered that custom CSS view transitions caused white flashes and brightness pulses due to the default mix-blend-mode: plus-lighter. The blend mode, designed for the default cross-fade where opacities sum to one, added colors when the custom animation kept the new page at full opacity, causing clipping to white. The fix involved setting mix-blend-mode to normal on the view transition layers. The short version, if you arrived here from a search: mix-blend-mode: plus-lighter , which was chosen for the animation you just deleted. That is your brightness pulse.The long version follows, because the reasoning is more useful than the fixes. My site accessible-by-default.dev https://accessible-by-default.dev/ is a multi-page app on purpose. Real page loads mean screen readers announce the new page, focus resets, the URL bar tells the truth, and there is no routing JavaScript to break. It is an overview hub plus four chapter pages, and I wanted moving between them to feel calm: the frame stays put, the content quietly fades. The platform hands you exactly this. One at-rule https://developer.mozilla.org/en-US/docs/Web/CSS/@view-transition in both documents: @view-transition { navigation: auto; } The browser snapshots the old page, loads the new one, and animates between them, across a real navigation, with no JavaScript. Then I customised the animation and got flashes. Bright pulses mid-fade in light mode, odd glare in dark mode, and separately a hard white blink on some navigations. I spent a while chasing them as one bug. They were not one bug. I could not reproduce any of it in my e2e setup, for an unhelpful reason: the Chromium build my test runner drives does not execute cross-document view transitions at all. Pages swapped instantly. No snapshots, nothing to observe, nothing to debug. I ended up driving my real installed Chrome over the DevTools protocol to see what users see. So: if your transition works locally but not in tests, or passes tests but misbehaves locally, verify that your automation browser actually performs cross-document view transitions before you form a single hypothesis about your CSS. Mine did not, and every conclusion I drew from those runs was noise. My custom animation is deliberately simple. The new page renders immediately underneath, and the old page's snapshot fades out on top of it: ::view-transition-new root { animation: none; } ::view-transition-old root { z-index: 1; animation: 0.28s ease both vt-content-fade; } @keyframes vt-content-fade { to { opacity: 0; } } Fade one opaque layer over another opaque layer. There is no frame in that description which should produce something brighter than either page. And yet, a visible brightness pulse, on every navigation. The answer is in the browser's own stylesheet https://drafts.csswg.org/css-view-transitions-1/ ua-styles . The spec places the old and new snapshots inside an isolated group and blends them with mix-blend-mode: plus-lighter , which adds the colours of the layers together, weighted by alpha. That default is a good decision. In the browser's default cross-fade, the old snapshot's opacity runs 1 to 0 while the new one runs 0 to 1, so the weights always sum to one, and plus-lighter makes the crossfade mathematically seamless: two half-transparent copies of similar content add back up to full brightness instead of dipping grey in the middle. But I had replaced the default animation. My new snapshot sits at full opacity for the entire transition, and early in the fade the old snapshot is still nearly opaque on top of it. Plus-lighter adds them. Almost double the light, which on a light theme clips straight to white. So the pulse was a blend mode doing precisely what it was told, under an assumption my keyframes had quietly invalidated. The fix is to restore normal painting: ::view-transition-image-pair root { isolation: auto; } ::view-transition-old root , ::view-transition-new root { mix-blend-mode: normal; } The generalisable version: if you replace the default view-transition animation with anything whose opacities do not sum to one, you have also inherited a blend mode designed for the animation you just deleted. After the blend fix, something was still there. A pure white flash on some navigations, and this one was fast enough that I could not tell where in the sequence it sat. Stretching the transition to 1.5 seconds settled it: the flash happened first, then the fade played cleanly. It was not part of the transition at all. It was the incoming document. During the handoff there can be a painted frame of the new page before its CSS has been applied, and an unstyled document paints the user-agent default, which is white. In dev this was worst, because Vite injects CSS through JavaScript, but a cold navigation in production can catch it too. The fix is unglamorous and reliable. Every entry HTML inlines one declaration, so that even a completely unstyled frame paints in the page's background colour rather than white: