ARTICLE AD BOX
The issue is that ::view-transition-new renders into a fixed-size container that's pre-allocated before the animation runs, so the full block size is reserved from frame one, even though your keyframe starts at block-size: 0.
The fix is to animate transform: translateY combined with clip-path (or overflow: clip on the pair) instead of animating block-size. This way the element occupies no visible space at the start without the browser needing to reserve room for it.
Here's the corrected approach:
css
::view-transition-image-pair(--incoming) { overflow-y: clip; } ::view-transition-new(--incoming) { animation-duration: 3s; animation-fill-mode: both; transform-origin: top center; @media (prefers-reduced-motion: no-preference) { animation-name: --slide-down-in; } } @keyframes --slide-down-in { from { transform: translateY(-100%); clip-path: inset(100% 0 0 0); } to { transform: translateY(0%); clip-path: inset(0% 0 0 0); } }The two properties work together here:
translateY(-100%)slides the element up so it starts above its natural position, and
clip-path: inset(100% 0 0 0)clips it so nothing bleeds into the space above. As both animate to their
tovalues simultaneously, the element appears to grow downward from zero height, which is the Svelte slide effect you're after.
The key insight is that you're not actually changing the element's size (which is what caused the pre-allocation problem), you're just revealing it progressively from top to bottom while sliding it into place.
One caveat:
animation-fill-mode: bothis important here so the from state is applied immediately at frame zero, before the transition paint starts.
