EDIT: I'm on Svelte 3.55.
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?
I managed to overcome this issue by applying fading
class to the sidebar
component instead of dimmer one. I didn't need an additional variable to check if the component is transitioning - I reused the isOpen
instead.
<div class="sidebar" class:fading={!isOpen}>
{#if isOpen}
<div
class="sidebar__dimmer"
on:click={toggleSidebar}
transition:fade={{ duration: 500 }}
/>
{/if}
<div>Lorem opsum dolor sit amet</div>
</div>
<style lang="scss" scoped>
// ...
.sidebar.fading .sidebar__dimmer {
pointer-events: none;
}
</style>
H.B. answer also gave me an idea to add an inert
attribute to sidebar whenever isOpen
is false, following how the Svelte 4+ handles transitions:
<div class="sidebar" inert={!isOpen}>
// ...
</div>