more fluent texts
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { get } from 'svelte/store';
|
||||
import { fade, fly } from 'svelte/transition';
|
||||
|
||||
import { afterNavigate, onNavigate } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import { storedTheme } from '$lib/stores/settings';
|
||||
@@ -13,6 +15,8 @@
|
||||
|
||||
import { routePath } from '$lib/i18n';
|
||||
|
||||
import { pageContentReady } from '$lib/stores/page-transition.svelte';
|
||||
|
||||
import './layout.css';
|
||||
|
||||
let { children } = $props();
|
||||
@@ -26,21 +30,74 @@
|
||||
const mq = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const apply = () => {
|
||||
const root = document.documentElement;
|
||||
const dark = theme === 'dark' || (theme === 'system' && mq.matches);
|
||||
const paint = () => root.classList.toggle('dark', dark);
|
||||
|
||||
// The very first application is just painting the stored theme - only
|
||||
// an actual switch afterwards is worth cross-fading.
|
||||
if (themeSettled) {
|
||||
root.classList.add('theme-transition');
|
||||
clearTimeout(themeTimer);
|
||||
themeTimer = window.setTimeout(() => root.classList.remove('theme-transition'), 400);
|
||||
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
if (!themeSettled || reduced) {
|
||||
themeSettled = true;
|
||||
paint();
|
||||
return;
|
||||
}
|
||||
themeSettled = true;
|
||||
root.classList.toggle('dark', theme === 'dark' || (theme === 'system' && mq.matches));
|
||||
|
||||
if (document.startViewTransition) {
|
||||
// one cross-fade of the whole document; component transitions untouched
|
||||
document.startViewTransition(paint);
|
||||
return;
|
||||
}
|
||||
|
||||
// no view transitions: fall back to fading the colours for one window
|
||||
root.classList.add('theme-transition');
|
||||
clearTimeout(themeTimer);
|
||||
themeTimer = window.setTimeout(() => root.classList.remove('theme-transition'), 400);
|
||||
paint();
|
||||
};
|
||||
|
||||
apply();
|
||||
mq.addEventListener('change', apply);
|
||||
return () => mq.removeEventListener('change', apply);
|
||||
});
|
||||
|
||||
// ── Page cross-fade ───────────────────────────────────────────────────────
|
||||
// The weather pages fetch their forecast after the route swap, so tying the
|
||||
// fade to navigation alone would cross-fade one skeleton into another and
|
||||
// then cut hard to the real content. Instead the outgoing page fades out on
|
||||
// navigation and the incoming one fades in once it reports that its data has
|
||||
// landed - with a ceiling so a slow or silent page is never left hidden.
|
||||
const FADE_OUT_MS = 170;
|
||||
const REVEAL_CEILING_MS = 2500;
|
||||
|
||||
let contentVisible = $state(true);
|
||||
let revealTimer = 0;
|
||||
|
||||
const reducedMotion = () =>
|
||||
typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
onNavigate(() => {
|
||||
if (reducedMotion()) return;
|
||||
clearTimeout(revealTimer);
|
||||
contentVisible = false;
|
||||
return new Promise((resolve) => setTimeout(resolve, FADE_OUT_MS));
|
||||
});
|
||||
|
||||
afterNavigate(() => {
|
||||
if (reducedMotion()) {
|
||||
contentVisible = true;
|
||||
return;
|
||||
}
|
||||
const startedAt = Date.now();
|
||||
const reveal = () => {
|
||||
if (get(pageContentReady) || Date.now() - startedAt > REVEAL_CEILING_MS) {
|
||||
contentVisible = true;
|
||||
return;
|
||||
}
|
||||
revealTimer = window.setTimeout(reveal, 60);
|
||||
};
|
||||
reveal();
|
||||
});
|
||||
|
||||
// the maps page embeds a full-bleed map: no padding, no scrolling
|
||||
let fullBleed = $derived(routePath($page.url.pathname).startsWith('/weather/maps'));
|
||||
|
||||
@@ -110,7 +167,10 @@
|
||||
{:else}
|
||||
<!-- cap the content width on very large screens; the footer below
|
||||
gives the page its ending, so only modest bottom room is needed -->
|
||||
<div class="mx-auto w-full max-w-[1536px] flex-1 pb-24">
|
||||
<div
|
||||
class="page-fade mx-auto w-full max-w-[1536px] flex-1 pb-24"
|
||||
class:page-fade-hidden={!contentVisible}
|
||||
>
|
||||
{@render children()}
|
||||
</div>
|
||||
<!-- full-bleed footer inside the scroll area (its own inner max-w),
|
||||
|
||||
Reference in New Issue
Block a user