Back to digital garden
Astro & CSS

Eliminating Layout Shifts in Astro View Transitions

How to avoid subtle 15px horizontal shifts and bounding box distortions when transitioning between static pages in Astro.

When implementing <ClientRouter /> with Astro View Transitions, two common pitfalls cause jarring layout shifts on route changes:

1. The Scrollbar Gutter Pop (15px Horizontal Jump)

When navigating from a long page with a vertical scrollbar to a short page without one, the browser scrollbar disappears. This shifts the layout container by 15-17px to the right.

Fix: Always declare stable scrollbar gutters on html:

html {
  overflow-y: scroll;
  scrollbar-gutter: stable;
}

2. Viewport Scale Distortion

Avoid animating transform: scale(0.995) or scale(1.005) on ::view-transition-old(root) or ::view-transition-new(root). Scaling the entire root viewport stretches and shrinks fixed elements (like headers and floating action buttons) during cross-fades.

Fix: Stick to clean, zero-scale opacity fades:

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 160ms;
  animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
}

::view-transition-old(root) {
  animation-name: page-fade-out;
}
::view-transition-new(root) {
  animation-name: page-fade-in;
}

@keyframes page-fade-out {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}
@keyframes page-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

3. Header Persistence

Keep fixed navigational headers stationary by assigning a persistent transition directive:

<header id="site-header" transition:persist="main-header">
  <!-- Nav items -->
</header>