In modern web applications orchestrating dynamic page state changes via the View Transition API, a frequent visual regression is the “flash of un-animated content” (FOUC) or sudden flickering right before an animation begins or immediately after it terminates. When an element is assigned a staggered entrance or delayed exit using animation-delay, browsers default to applying keyframe styles only while the active animation interval is executing. Before the delay expires and after the keyframe duration concludes, the pseudo-element falls back to default layout styling, causing distracting flashes on screen.
By enforcing strict animation-fill-mode: both (or forwards / backwards) on ::view-transition-old and ::view-transition-new pseudo-elements, web authors ensure keyframe styles are projected seamlessly before execution and locked at the final frame until the transition pseudo-tree is dismantled in Google Chrome.
Chromium View Transition Fill-Mode Mechanics
The View Transition API generates a pseudo-element tree rooted at the document root:
::view-transition
└─ ::view-transition-group(article-hero)
└─ ::view-transition-image-pair(article-hero)
├─ ::view-transition-old(article-hero)
└─ ::view-transition-new(article-hero)
Under default browser behaviour (from Chromium’s internal stylesheet):
animation-fill-mode: none;: Keyframe properties only apply during active execution. During ananimation-delay, the element displays with full opacity, defeating the purpose of delayed fade-ins.animation-fill-mode: backwards;: The element immediately adopts thefrom/0%keyframe styles during any preceding delay period.animation-fill-mode: forwards;: The element retains theto/100%keyframe styles after execution until the transition completes.animation-fill-mode: both;: Combines both behaviours, ensuring seamless visual continuity across the entire lifecycle.
Configuring Strict Fill Modes in Custom Stylesheets
To eliminate pre-animation flashing on delayed elements, always specify animation-fill-mode: both:
/* Delayed Modal Transition with Strict Fill Mode */
::view-transition-new(dialog-overlay) {
animation-name: modal-fade-scale;
animation-duration: 400ms;
animation-delay: 150ms;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
animation-fill-mode: both; /* Prevents 150ms flash of full opacity */
}
@keyframes modal-fade-scale {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
Managing Staggered Grids Without Visual Artefacts
When choreographing staggered list reveals using CSS custom properties:
/* Batch Stagger Configuration */
::view-transition-new(*.stagger-item) {
animation-name: slide-up-reveal;
animation-duration: 350ms;
animation-delay: var(--item-delay, 0ms);
animation-fill-mode: both;
}
@keyframes slide-up-reveal {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
Inspecting Fill Modes in Chrome DevTools
To audit animation lifecycle states in Google Chrome:
- Open DevTools (F12) and bring up the Animations drawer via
Ctrl+Shift+P>Show Animations. - Trigger your state transition. Chrome DevTools captures the animation and displays each track.
- Scrub the timeline to the first frame (during the delay period).
- Select
::view-transition-newin the Elements tree and verify in the Computed panel thatopacity: 0andtransform: scale(0.95)are active.
Configuring strict animation-fill-mode: both ensures rock-solid, flicker-free view transitions across Google Chrome.