DEV Community

Daniel Imfeld
Daniel Imfeld

Posted on • Originally published at imfeld.dev on

Svelte Transitions for Routes and Overlapping Elements

Svelte’s transition system is praised for its ease of use, but it doesn’t handle everything for you. One common case that needs some extra care is transitioning between elements that are both supposed to occupy the same space.

Instead of the elements smoothly transitioning into the same spot, they both exist in the DOM at the same time, so the browser renders one element next to the other while the transitions run. When the outgoing element disappears, the new element then jumps into the correct place.

View this post on the website for an interactive example.

This is unsightly at best, but it’s pretty easy to fix with a quick application of CSS Grid.

The container element is a grid with one row and one column. The children elements are then all forced to be in the first row and column, so they will overlap. (Without this, the browser will assume you made a mistake and add extra grid cells.)

Then you just set up the transitions on your elements, and you’re ready to go!

<style>
  .transition-container {
    display: grid;
    template-grid-rows: 1;
    template-grid-columns: 1;
  }

  .transition-container > * {
    grid-row: 1;
    grid-column: 1;
  }
</style>

<div class="transition-container">
  {#if enabled}
    <h1 in:fly={{ x: -200 }} out:fade>Enabled!</h1>
  {:else}
    <h1 in:fly={{ x: -200 }} out:fade>Disabled!</h1>
  {/if}
</div>

Enter fullscreen mode Exit fullscreen mode

View this post on the website for an interactive example.

Route Transitions

This same technique can be used to facilitate transitions between routes in a single-page application. Here’s an example using the tinro router.

<script>
  import { Route } from 'tinro';
  import { fade } from 'svelte/transition';
</script>
<style>
  #app {
    display: grid;
    height: 100%;
    width: 100%;
    overflow: auto;
    grid-template:
      "nav" 3rem
      "main" 1fr
      / auto;
  }

  nav {
    grid-area: nav;
  }

  main {
    grid-area: main;
    display: grid;
    template-grid-rows: 1;
    template-grid-columns: 1;
  }

  /* use :global since the Route element may be in another component */
  main > :global(*) {
    grid-row: 1;
    grid-column: 1;
  }
</style>

<div id="app">
<nav>Nav Bar</nav>
<main>
  <Route path="/">
    <div transition:fade>page</div>
  </Route>

  <Route path="/:post" let:meta>
    {#key meta.match}
      <div transition:fly={{ x: 200 }}>{meta.params.post}</div>
    {/key}
  </Route>
</main>
</div>

Enter fullscreen mode Exit fullscreen mode

View this post on the website for an interactive example.

I’ll cover more advanced transition techniques in future posts. Thanks for reading!

Top comments (0)