skip to Main Content

I am attempting to code a Sidebar in Svelte and have run into issue related to how Svelte transitions work with dynamic classes.

Sidebar is supposed to fly into view when a button is clicked. I am using the transition:fly for it. There is also a Dimmer component covering the entire screen, which fades into view during Sidebar’s fly animation. To close the Sidebar user must click the Dimmer. Sidebar then flies out of view, and Dimmer fades out. And here is my problem.

Where the Dimmer is in its fade out animation, the page underneath cannot be interacted with. Clicking anywhere registers on the dimmer itself, Sidebar stops its fly out animation and jumps back into view.

I tried to solve it by appending isFading class to the Dimmer when the outro animation starts – the class would add pointer-events: none to the Dimmer when it fades out. But the class is appended with a delay and this solution simply does not work.

My code looks as follows:

<script lang="ts">
  import { fade } from 'svelte/transition'
  import { isSidebarOpen, toggleSidebar } from '../state'

  let isOpen: boolean = false
  let isFading: boolean = false

  isSidebarOpen.subscribe((value) => (isOpen = value))
</script>

<div class="sidebar">
  {#if isOpen}
    <div
      class="sidebar__dimmer"
      class:isFading
      on:click={toggleSidebar}
      on:outrostart={() => (isFading = true)}
      on:introstart={() => (isFading = false)}
      transition:fade={{ duration: 500 }}
    />
  {/if}
  <div>Lorem opsum dolor sit amet</div>
</div>

<style lang="scss" scoped>
  .sidebar {
    z-index: 9999;
    position: fixed;
    inset: 0;
    display: flex;
    overflow: hidden;
    pointer-events: none;

    &__dimmer {
      position: absolute;
      inset: 0;
      pointer-events: all;
    }

    &__dimmer.isFading {
      pointer-events: none;
    }
  }
</style>

My question is: how do I append the isFading class to the component instantly when the outro animation starts?

2

Answers


  1. In Svelte 4 you should not need this in browsers supporting inert, as that will be added automatically.

    You could apply the class directly using the DOM via the event:

    <div ...
      on:outrostart={e => e.target.classList.add('isFading')}
      on:introstart={e => e.target.classList.remove('isFading')} />
    

    (Might have to use target?., currentTarget or extract logic to functions if you run into TypeScript errors.)

    Since the class is applied in a way that makes it invisible to the compiler, you will have to change the selector accordingly:

    &__dimmer:global(.isFading) ...
    
    Login or Signup to reply.
  2. By using the start and end transition callbacks, the isFading class will be applied instantly when the outro animation starts, and it will be removed when the outro animation ends. This should help you achieve the desired behavior where the Dimmer doesn’t interfere with interactions while fading out.

     <script lang="ts">
          import { fly, fade } from 'svelte/transition';
          import { isSidebarOpen, toggleSidebar } from '../state';
        
          let isOpen: boolean = false;
          let isFading: boolean = false;
        
          isSidebarOpen.subscribe((value) => (isOpen = value));
        </script>
        
        <div class="sidebar">
          {#if isOpen}
            <div
              class="sidebar__dimmer"
              class:isFading
              on:click={toggleSidebar}
              transition:fade={{ duration: 500 }}
              transition:start={() => (isFading = true)}
              transition:end={() => (isFading = false)}
            />
          {/if}
          <div>Lorem opsum dolor sit amet</div>
        </div>
        
        <style lang="scss" scoped>
          .sidebar {
            z-index: 9999;
            position: fixed;
            inset: 0;
            display: flex;
            overflow: hidden;
            pointer-events: none;
        
            &__dimmer {
              position: absolute;
              inset: 0;
              pointer-events: all;
            }
        
            &__dimmer.isFading {
              pointer-events: none;
            }
          }
        </style>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search