feat: make page transitions smoother and faster

This commit is contained in:
Elias Schneider
2026-10-01 21:48:13 +02:00
parent 1f67bb7493
commit 9c926bd66f
6 changed files with 107 additions and 78 deletions
@@ -1,63 +0,0 @@
<script lang="ts">
import { page } from '$app/state';
import appConfigStore from '$lib/stores/application-configuration-store';
import type { Snippet } from 'svelte';
let {
delay = 50,
stagger = 150,
children
}: {
delay?: number;
stagger?: number;
children: Snippet;
} = $props();
let containerNode: HTMLElement | null = $state(null);
$effect(() => {
void page.route;
applyAnimationDelays();
});
function applyAnimationDelays() {
if (containerNode) {
const childNodes = Array.from(containerNode.children);
childNodes.forEach((child, index) => {
// Skip comment nodes and text nodes
if (child.nodeType === 1) {
const itemDelay = delay + index * stagger;
(child as HTMLElement).style.setProperty('animation-delay', `${itemDelay}ms`);
}
});
}
}
</script>
<svelte:head>
<style>
/* Base styles */
.fade-wrapper {
display: contents;
overflow: hidden;
}
/* Apply these styles to all children */
.fade-wrapper > *:not(.no-fade) {
animation-fill-mode: both;
opacity: 0;
transform: translateY(10px);
animation-delay: calc(var(--animation-delay, 0ms) + 0.1s);
animation: fadeIn 0.3s ease-out forwards;
will-change: opacity, transform;
}
</style>
</svelte:head>
{#if $appConfigStore.disableAnimations}
{@render children()}
{:else}
<div class="fade-wrapper" bind:this={containerNode}>
{@render children()}
</div>
{/if}
@@ -0,0 +1,38 @@
<script lang="ts">
import { navigating } from '$app/state';
import { fade } from 'svelte/transition';
// Skip the fade when the bar never became visible, otherwise it would start growing during the fade and flash on fast navigations
function fadeIfVisible(node: HTMLElement) {
const visible = new DOMMatrixReadOnly(getComputedStyle(node).transform).a > 0;
return fade(node, { duration: visible ? 200 : 0 });
}
</script>
{#if navigating.to}
<div class="nav-progress" aria-hidden="true" out:fadeIfVisible></div>
{/if}
<style>
.nav-progress {
position: fixed;
inset: 0 0 auto 0;
z-index: 100;
height: 2px;
background: var(--primary);
transform: scaleX(0);
transform-origin: left;
pointer-events: none;
animation: progress 3s cubic-bezier(0.1, 0.6, 0.3, 1) 100ms forwards;
}
@keyframes progress {
10% {
transform: scaleX(0.25);
}
to {
transform: scaleX(0.85);
}
}
</style>