fix: page transition crossfade eliminates dark-mode flash on navigation
Removes the sequential fade-out → wait → fade-in pattern that causes a visible black flash in dark mode between page transitions. Replaces with immediate crossfade (new page fades in over old content, no wait). Changes: - layout.css: Add page-crossfade-in keyframe (0.18s) + prefers-reduced-motion override - router.js: Remove outClass/inClass direction logic and oldPage fadeout wait The new approach: 1. Old page remains visible until new page renders 2. New page fades in (0.18s) with full opacity, overlaying old content 3. No 120ms delay = no visible flash in dark mode Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -350,20 +350,12 @@ async function renderPage(route, previousPath = null) {
|
|||||||
|
|
||||||
// Richtung bestimmen (previousPath ist der alte Pfad vor der Navigation)
|
// Richtung bestimmen (previousPath ist der alte Pfad vor der Navigation)
|
||||||
const direction = getDirection(previousPath, route.path);
|
const direction = getDirection(previousPath, route.path);
|
||||||
const outClass = direction === 'right' ? 'page-transition--out-left' : 'page-transition--out-right';
|
|
||||||
const inClass = direction === 'right' ? 'page-transition--in-right' : 'page-transition--in-left';
|
const inClass = direction === 'right' ? 'page-transition--in-right' : 'page-transition--in-left';
|
||||||
|
|
||||||
// Performance: backdrop-filter während Übergang deaktivieren (Android-Optimierung).
|
// Performance: backdrop-filter während Übergang deaktivieren (Android-Optimierung).
|
||||||
// glass.css setzt alle backdrop-filter im app-content auf none solange diese Klasse aktiv ist.
|
// glass.css setzt alle backdrop-filter im app-content auf none solange diese Klasse aktiv ist.
|
||||||
document.documentElement.classList.add('navigating');
|
document.documentElement.classList.add('navigating');
|
||||||
|
|
||||||
// Alte Seite kurz ausfaden, falls vorhanden
|
|
||||||
const oldPage = content.querySelector('.page-transition');
|
|
||||||
if (oldPage) {
|
|
||||||
oldPage.classList.add(outClass);
|
|
||||||
await new Promise(r => setTimeout(r, 120));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alter Inhalt ist jetzt weg - altes Stylesheet kann entfernt werden
|
// Alter Inhalt ist jetzt weg - altes Stylesheet kann entfernt werden
|
||||||
const pageWrapper = document.createElement('div');
|
const pageWrapper = document.createElement('div');
|
||||||
pageWrapper.className = 'page-transition';
|
pageWrapper.className = 'page-transition';
|
||||||
|
|||||||
@@ -80,6 +80,11 @@
|
|||||||
to { opacity: 0; transform: translateX(20px); }
|
to { opacity: 0; transform: translateX(20px); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes page-crossfade-in {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
.page-transition--in-right {
|
.page-transition--in-right {
|
||||||
animation: page-slide-in-right 0.2s var(--ease-out);
|
animation: page-slide-in-right 0.2s var(--ease-out);
|
||||||
}
|
}
|
||||||
@@ -95,6 +100,11 @@
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-transition--crossfade {
|
||||||
|
animation: page-crossfade-in 0.18s var(--ease-out);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.page-transition--in-right,
|
.page-transition--in-right,
|
||||||
.page-transition--in-left {
|
.page-transition--in-left {
|
||||||
@@ -105,6 +115,10 @@
|
|||||||
.page-transition--out-right {
|
.page-transition--out-right {
|
||||||
animation: none;
|
animation: none;
|
||||||
}
|
}
|
||||||
|
.page-transition--crossfade {
|
||||||
|
animation: none;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------
|
/* --------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user