feat(hikes): view-transition exit flow off detail page
Fires the same strip-slide-out + below-strip-fly-down animation whenever a hike detail page is left for anywhere other than another slug — not just on the back-to-/hikes path. Layout adds a new `vt-exit-hike-detail` class on the document root for that case; the css rules tag onto the existing `vt-enter-hikes` selectors so both exits drive identical animations.
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "homepage",
|
"name": "homepage",
|
||||||
"version": "1.92.0",
|
"version": "1.93.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+24
-6
@@ -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;
|
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,
|
/* Photo strip slides in from the right when arriving at a detail page,
|
||||||
* and slides back out when returning to /hikes. Overrides the global root
|
* and slides back out whenever the detail page is left for any other
|
||||||
* `animation: none` only for these hike-specific transitions. */
|
* 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 {
|
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;
|
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;
|
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
|
/* Cross-fade the rest of the page (root pseudo) during hike transitions so
|
||||||
* the destination's chrome — metrics + content + footer on the detail page,
|
* the destination's chrome — metrics + content + footer on the detail page,
|
||||||
* overview hero + credit on the index — phases in instead of snapping in
|
* 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. */
|
* other routes' transitions on their existing instant-swap behavior. */
|
||||||
html.vt-enter-hike-detail::view-transition-old(root),
|
html.vt-enter-hike-detail::view-transition-old(root),
|
||||||
html.vt-enter-hikes::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;
|
animation: hikes-root-fade-out 450ms ease-out both;
|
||||||
}
|
}
|
||||||
html.vt-enter-hike-detail::view-transition-new(root),
|
html.vt-enter-hike-detail::view-transition-new(root),
|
||||||
html.vt-enter-hikes::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;
|
animation: hikes-root-fade-in 450ms ease-out both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,15 +79,22 @@
|
|||||||
// (Covers /hikes → / AND /hikes → /hikes/[slug], where the clicked
|
// (Covers /hikes → / AND /hikes → /hikes/[slug], where the clicked
|
||||||
// card pairs into the hero and the rest fly out.)
|
// card pairs into the hero and the rest fly out.)
|
||||||
// - vt-enter-hike-detail: arriving at a hike detail page (card → zoom).
|
// - 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 intoHikesIndex = toId === '/hikes' && fromId !== '/hikes';
|
||||||
const outOfHikesIndex = fromId === '/hikes' && toId !== '/hikes';
|
const outOfHikesIndex = fromId === '/hikes' && toId !== '/hikes';
|
||||||
const intoHikeDetail = toId === '/hikes/[slug]';
|
const intoHikeDetail = toId === '/hikes/[slug]';
|
||||||
|
const outOfHikeDetail = fromId === '/hikes/[slug]' && toId !== '/hikes/[slug]';
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
if (intoHikesIndex) root.classList.add('vt-enter-hikes');
|
if (intoHikesIndex) root.classList.add('vt-enter-hikes');
|
||||||
if (outOfHikesIndex) root.classList.add('vt-exit-hikes');
|
if (outOfHikesIndex) root.classList.add('vt-exit-hikes');
|
||||||
if (intoHikeDetail) root.classList.add('vt-enter-hike-detail');
|
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 () => {
|
const transition = (/** @type {any} */ (document)).startViewTransition(async () => {
|
||||||
resolve();
|
resolve();
|
||||||
await navigation.complete;
|
await navigation.complete;
|
||||||
@@ -96,6 +103,7 @@
|
|||||||
root.classList.remove('vt-enter-hikes');
|
root.classList.remove('vt-enter-hikes');
|
||||||
root.classList.remove('vt-exit-hikes');
|
root.classList.remove('vt-exit-hikes');
|
||||||
root.classList.remove('vt-enter-hike-detail');
|
root.classList.remove('vt-enter-hike-detail');
|
||||||
|
root.classList.remove('vt-exit-hike-detail');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user