<div id="app">
  <h1>View Transition API - Sliding Effect Demo</h1>

  <div v-if="page === 0">
    <h2>Page 0</h2>
    <hr>
    <button type="button" @click="changePage(1)">
      Page 1
    </button>
    <hr>
    <p>Here we've created a custom CSS animation and applied it to the ::view-transition-old(figure-caption) and ::view-transition-new(figure-caption) pseudo-elements. We've also added a number of other styles to both to keep them in the same place and stop the default styling from interfering with our custom animations.</p>
  </div>

  <div v-if="page === 1">
    <h2>Page 1</h2>
    <hr>
    <button type="button" @click="changePage(0)">
      Page 0
    </button>
    <button type="button" @click="changePage(2)">
      Page 2
    </button>
    <hr>
    <p>Note that we also discovered another transition option that is simpler and produced a nicer result than the above. Our final view transition ended up looking like this:</p>
  </div>

  <div v-if="page === 2">
    <h2>Page 3</h2>
    <hr>
    <button type="button" @click="changePage(1)">
      Page 1
    </button>
    <hr>
    <p>This works because, by default, ::view-transition-group transitions width and height between the old and new views. We just needed to set a fixed height on both states to make it work.</p>
  </div>
</div>
:root {
  background: #fff;
}

::view-transition-old(root) {
  animation-duration: 0.8s;
  animation-name: slide-out;
  mix-blend-mode: normal;
}

::view-transition-new(root) {
  animation-duration: 0.8s;
  animation-name: slide-in;
  box-shadow: 0 0 40px #0003;
  mix-blend-mode: normal;
}

.back::view-transition-old(root) {
  animation-duration: 0.8s;
  animation-name: back-slide-out;
  box-shadow: 0 0 40px #0003;
  mix-blend-mode: normal;
}

.back::view-transition-new(root) {
  animation-duration: 0.8s;
  animation-name: back-slide-in;
  mix-blend-mode: normal;
}

@keyframes slide-out {
  from {
    left: 0%;
  }
  to {
    left: -40%;
  }
}

@keyframes slide-in {
  from {
    left: 100%;
  }
  to {
    left: 0%;
  }
}

@keyframes back-slide-out {
  from {
    left: 0%;
    z-index: 10000;
  }
  to {
    left: 100%;
    z-index: 10000;
  }
}

@keyframes back-slide-in {
  from {
    left: -40%;
  }
  to {
    left: 0%;
  }
}
if (!document.startViewTransition) {
  document.startViewTransition = (fn) => {
    fn()
  }
}

const { createApp, ref } = Vue

createApp({
  setup() {
    const page = ref(0)

    function changePage (p) {
      let back = null

      if (page.value < p) {
        back = false
      } else if (page.value > p) {
        back = true
      } else {
        return
      }

      document.startViewTransition(() => {
        if (back) {
          document.documentElement.classList.add('back')
        } else {
          document.documentElement.classList.remove('back')
        }
        page.value = p
      })
    }

    return {
      changePage,
      page
    }
  }
}).mount('#app')

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.37/vue.global.prod.min.js