diff --git a/package.json b/package.json index 42236b70..bed5121c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homepage", - "version": "1.92.0", + "version": "1.93.0", "private": true, "type": "module", "scripts": { diff --git a/src/app.css b/src/app.css index 188fec36..2df529f6 100644 --- a/src/app.css +++ b/src/app.css @@ -533,16 +533,32 @@ html.vt-exit-hikes::view-transition-old(.hike-fly-in):only-child { animation: hikes-fly-down 700ms cubic-bezier(0.4, 0.1, 0.4, 1) both; } -/* Photo strip slides in from the right when arriving at the detail page, - * and slides back out when returning to /hikes. Overrides the global root - * `animation: none` only for these hike-specific transitions. */ +/* Photo strip slides in from the right when arriving at a detail page, + * and slides back out whenever the detail page is left for any other + * route (back to /hikes, off to /, /hikes/route-builder, …). Both exit + * scopes (vt-enter-hikes for the back-nav case, vt-exit-hike-detail for + * everywhere else) trigger the same animation. */ html.vt-enter-hike-detail::view-transition-new(hike-strip):only-child { animation: hike-strip-in-right 600ms cubic-bezier(0.2, 0.7, 0.2, 1) both; } -html.vt-enter-hikes::view-transition-old(hike-strip):only-child { +html.vt-enter-hikes::view-transition-old(hike-strip):only-child, +html.vt-exit-hike-detail::view-transition-old(hike-strip):only-child { animation: hike-strip-out-right 600ms cubic-bezier(0.4, 0.1, 0.4, 1) both; } +/* Everything below the photo strip on a detail page (metrics, tags, + * elevation chart, scroll area, meta footer) slides up from the bottom + * on enter and back down on any exit. Wrapper element carries + * `view-transition-name: hike-below-strip`; the rest of the page chrome + * still cross-fades via the root-pseudo rule above. */ +html.vt-enter-hike-detail::view-transition-new(hike-below-strip):only-child { + animation: hikes-fly-up 700ms cubic-bezier(0.2, 0.7, 0.2, 1) both; +} +html.vt-enter-hikes::view-transition-old(hike-below-strip):only-child, +html.vt-exit-hike-detail::view-transition-old(hike-below-strip):only-child { + animation: hikes-fly-down 700ms cubic-bezier(0.4, 0.1, 0.4, 1) both; +} + /* Cross-fade the rest of the page (root pseudo) during hike transitions so * the destination's chrome — metrics + content + footer on the detail page, * overview hero + credit on the index — phases in instead of snapping in @@ -550,12 +566,14 @@ html.vt-enter-hikes::view-transition-old(hike-strip):only-child { * other routes' transitions on their existing instant-swap behavior. */ html.vt-enter-hike-detail::view-transition-old(root), html.vt-enter-hikes::view-transition-old(root), -html.vt-exit-hikes::view-transition-old(root) { +html.vt-exit-hikes::view-transition-old(root), +html.vt-exit-hike-detail::view-transition-old(root) { animation: hikes-root-fade-out 450ms ease-out both; } html.vt-enter-hike-detail::view-transition-new(root), html.vt-enter-hikes::view-transition-new(root), -html.vt-exit-hikes::view-transition-new(root) { +html.vt-exit-hikes::view-transition-new(root), +html.vt-exit-hike-detail::view-transition-new(root) { animation: hikes-root-fade-in 450ms ease-out both; } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 20f4df1f..b176c387 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -79,15 +79,22 @@ // (Covers /hikes → / AND /hikes → /hikes/[slug], where the clicked // card pairs into the hero and the rest fly out.) // - vt-enter-hike-detail: arriving at a hike detail page (card → zoom). + // - vt-exit-hike-detail: leaving a hike detail page for anywhere + // else (back to /hikes, off to /, route-builder, …) → photo strip + // slides back out to the right and the below-strip block flies + // down. Excluded for slug → slug navigations (both sides share the + // same route.id, so paired UA transitions handle them). const intoHikesIndex = toId === '/hikes' && fromId !== '/hikes'; const outOfHikesIndex = fromId === '/hikes' && toId !== '/hikes'; const intoHikeDetail = toId === '/hikes/[slug]'; + const outOfHikeDetail = fromId === '/hikes/[slug]' && toId !== '/hikes/[slug]'; return new Promise((resolve) => { const root = document.documentElement; if (intoHikesIndex) root.classList.add('vt-enter-hikes'); if (outOfHikesIndex) root.classList.add('vt-exit-hikes'); if (intoHikeDetail) root.classList.add('vt-enter-hike-detail'); + if (outOfHikeDetail) root.classList.add('vt-exit-hike-detail'); const transition = (/** @type {any} */ (document)).startViewTransition(async () => { resolve(); await navigation.complete; @@ -96,6 +103,7 @@ root.classList.remove('vt-enter-hikes'); root.classList.remove('vt-exit-hikes'); root.classList.remove('vt-enter-hike-detail'); + root.classList.remove('vt-exit-hike-detail'); }); }); });