Modern web applications frequently implement dynamic dark mode toggles or high-contrast theme switches. When switching themes across an entire document using the View Transition API, default crossfades can cause distracting colour flashes or intermediate greyscale mud if contrasting elements blend unpredictably. By applying CSS filter properties—such as invert(), hue-rotate(), and brightness()—directly to ::view-transition-old and ::view-transition-new, web authors can craft cinematic, optically pleasing transitions where colours invert seamlessly during the animation lifecycle.
Configuring strict filter inversion in Google Chrome ensures that visual transitions between dark and light themes preserve chromatic balance without flickering or layout jumps.
Chromium View Transition Filter Mechanics
During an active transition, Chromium captures document snapshots as raster layers hosted in pseudo-elements:
::view-transition
└─ ::view-transition-group(root)
└─ ::view-transition-image-pair(root)
├─ ::view-transition-old(root)
└─ ::view-transition-new(root)
When switching themes, applying filter: invert() allows you to morph the outgoing light theme into a dark representation before dissolving into the incoming live dark DOM:
/* Dark-mode toggle transition filter orchestration */
html[data-theme-transition="to-dark"]::view-transition-old(root) {
animation: 400ms cubic-bezier(0.4, 0, 0.2, 1) both theme-invert-out;
}
html[data-theme-transition="to-dark"]::view-transition-new(root) {
animation: 400ms cubic-bezier(0.16, 1, 0.3, 1) both theme-reveal-in;
}
@keyframes theme-invert-out {
0% {
filter: invert(0) brightness(1);
opacity: 1;
}
100% {
filter: invert(1) brightness(0.85);
opacity: 0;
}
}
@keyframes theme-reveal-in {
0% {
opacity: 0;
filter: brightness(1.2);
}
100% {
opacity: 1;
filter: brightness(1);
}
}
Circular Mask Inversion with Clip-Path
To produce the popular circular ripple reveal centered on the user’s toggle button, pair filter inversion with clip-path geometry:
/* Circular expansion on incoming theme */
::view-transition-new(root) {
animation: 500ms cubic-bezier(0.2, 0, 0, 1) both circle-expand;
}
::view-transition-old(root) {
animation: none;
z-index: 1;
}
@keyframes circle-expand {
from {
clip-path: circle(0px at var(--click-x, 50%) var(--click-y, 50%));
}
to {
clip-path: circle(150% at var(--click-x, 50%) var(--click-y, 50%));
}
}
Inspecting Filter Transitions in Chrome DevTools
To audit filter rendering during active transitions in Google Chrome:
- Open DevTools (F12) and bring up the Animations drawer via
Ctrl+Shift+P>Show Animations. - Click your theme toggle button to capture the transition track.
- Scrub the timeline to 50% completion to freeze the intermediate frame.
- Select
::view-transition-old(root)in the Elements tree. - In the Computed tab, verify that
filter: invert(...)computes correctly and that GPU compositor layers are retained without unaccelerated software blits.
Configuring strict filter transitions produces smooth, cinematic theme switches across Google Chrome.