Lithe
Welcome Getting started Routing Build Signals and binding Web component Templates View transitions Fetcher Multiple languages github-circle-black-transparent GitHub github-circle-black-transparent Example app

View transitions

Types

There are multiple built in transitions you can utilize. These can be used individually per anchor link or you can set them globally.
  • cross-fade
  • slide-right
  • slide-left
  • expand-from-element
  • expand-from-element-back
Expand example
<!-- Set globally -->
<meta name="view-transition" content="slide-left">
<meta name="view-transition-back" content="slide-right">


<!-- Set transition on anchor link
     These will override the global settings -->
<a href="/" view-transition="cross-fade">Welcome</a>
<mc-card href="/item/123" view-transition="expand-from-element"></mc-card>


<!-- Add a back transition
     Back will only work after the initial view-transition -->
<a href="/"
   view-transition="expand-from-element"
   view-transition-back="expand-from-element-back">Welcome</a>

<a href="/"
   view-transition="slide-left"
   view-transition-back="slide-right">Welcome</a>

          

Custom view transitions

You can add your own view transitions. Both CSS and code transitions are supported.
/* Use standard css */
::view-transition-old(slide-left) {
  animation: page-slide-left-out;
  animation-duration: 400ms;
  animation-timing-function: ease;
}
::view-transition-new(slide-left) {
  animation: page-slide-left-in;
  animation-duration: 400ms;
  animation-timing-function: ease;
}

@keyframes page-slide-left-in {
  0% {
    transform: translateX(100%);
    clip-path: inset(0px 100% 0px 0px);
  }
  100% {
    transform: translateX(0px);
    clip-path: inset(0px 0px 0px 0px);
  }
}

@keyframes page-slide-left-out {
  0% {
    transform: translateX(0px);
    clip-path: inset(0px 0px 0px 0px);
  }
  100% {
    transform: translateX(-100%);
    clip-path: inset(0px 0px 0px 100%);
  }
}
        

import { registerViewTransition } from '@thewebformula/lithe';

registerViewTransition('the-name', {
  setup(container, target) {
    const scrollTop = document.documentElement.scrollTop;
    document.documentElement.style.setProperty('--mc-view-transition-scroll-fix', `translateY(-${scrollTop}px)`);
    const containerBounds = container.getBoundingClientRect();
    const targetBounds = target.getBoundingClientRect();

    return {
      containerBounds,
      targetBounds,
      scrollTop
    };
  },
  animate(container, { containerBounds, targetBounds, scrollTop }) {
    document.documentElement.animate(
      [
        {
          transform: `translate(${targetBounds.x - containerBounds.x}px, ${targetBounds.y - containerBounds.y - scrollTop}px)`,
          height: `${targetBounds.height}px`,
          width: `${targetBounds.width}px`
        },
        {
          transform: `translate(0px, 0px)`,
          height: `${container.offsetHeight}px`,
          width: `${container.offsetWidth}px`,
        }
      ],
      {
        duration: 400,
        easing: 'cubic-bezier(0.2, 0, 0, 1)',
        pseudoElement: '::view-transition-new(expand-from-element)'
      }
    );
  }
});
          
menu