feat(hikes): unify below-map view transition into one sliding panel

The detail-page enter/exit transition previously slid only the metric
tiles up from the bottom — the wrapper had no background, so its
snapshot was transparent and no containing panel moved. The photo strip
also animated separately, sliding in from the right.

Wrap everything below the hero map (stage nav, photo strip, metrics,
tags, elevation, scroll area, footer) in one `.below-map` element with
`view-transition-name: hike-below-map` and an opaque background, so the
whole sheet — background included — slides up on enter and down on exit
as a single panel. Drop the obsolete hike-strip right-slide rules and
keyframes; rename hike-below-strip → hike-below-map.
This commit is contained in:
2026-05-31 13:29:15 +02:00
parent cd7912fa8f
commit 9fe9d95e36
4 changed files with 35 additions and 45 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "homepage", "name": "homepage",
"version": "1.95.0", "version": "1.95.1",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
+14 -34
View File
@@ -467,10 +467,11 @@ a:focus-visible {
/* ============================================ /* ============================================
HIKES TRANSITIONS HIKES TRANSITIONS
Cards + filter fly in/out vertically, clicked card morphs into the hero Cards + filter fly in/out vertically, clicked card morphs into the hero
map (cross-fade between thumbnail and map), photo strip slides in from map (cross-fade between thumbnail and map), and the whole below-map panel
the right. Page chrome under the hero cross-fades so nothing snaps in (an opaque sheet) slides up from the bottom. Page chrome under the hero
at transition end. Lives in app.css (not the page component) so the cross-fades so nothing snaps in at transition end. Lives in app.css (not
rules are still loaded on the OLD side of a nav AWAY from /hikes. the page component) so the rules are still loaded on the OLD side of a
nav AWAY from /hikes.
============================================ */ ============================================ */
@keyframes hikes-fly-up { @keyframes hikes-fly-up {
@@ -489,15 +490,6 @@ a:focus-visible {
from { opacity: 0; } from { opacity: 0; }
to { opacity: 1; } to { opacity: 1; }
} }
@keyframes hike-strip-in-right {
from { transform: translateX(100vw); }
to { transform: translateX(0); }
}
@keyframes hike-strip-out-right {
from { transform: translateX(0); }
to { transform: translateX(100vw); }
}
/* Only-child hike-fly-in pseudos (unpaired cards/filter on enter or exit): /* Only-child hike-fly-in pseudos (unpaired cards/filter on enter or exit):
* kill UA's default fade, switch blend mode so the custom fly animation * kill UA's default fade, switch blend mode so the custom fly animation
* shows clean motion against the rest of the page. */ * shows clean motion against the rest of the page. */
@@ -533,29 +525,17 @@ 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 a detail page, /* Everything below the hero map on a detail page — stage nav, photo strip,
* and slides back out whenever the detail page is left for any other * metrics, tags, elevation chart, scroll area, meta footer — slides up from
* route (back to /hikes, off to /, /hikes/route-builder, …). Both exit * the bottom on enter and back down on any exit, as one panel. The wrapper
* scopes (vt-enter-hikes for the back-nav case, vt-exit-hike-detail for * carries `view-transition-name: hike-below-map` and an opaque background, so
* everywhere else) trigger the same animation. */ * the whole sheet (background included) moves; the hero map morphs separately
html.vt-enter-hike-detail::view-transition-new(hike-strip):only-child { * above, and the rest of the page chrome cross-fades via the root-pseudo rule. */
animation: hike-strip-in-right 600ms cubic-bezier(0.2, 0.7, 0.2, 1) both; html.vt-enter-hike-detail::view-transition-new(hike-below-map):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; 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-enter-hikes::view-transition-old(hike-below-map):only-child,
html.vt-exit-hike-detail::view-transition-old(hike-below-strip):only-child { html.vt-exit-hike-detail::view-transition-old(hike-below-map):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;
} }
+4 -4
View File
@@ -80,10 +80,10 @@
// 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 // - vt-exit-hike-detail: leaving a hike detail page for anywhere
// else (back to /hikes, off to /, route-builder, …) → photo strip // else (back to /hikes, off to /, route-builder, …) → the whole
// slides back out to the right and the below-strip block flies // below-map panel flies back down off the bottom. Excluded for
// down. Excluded for slug → slug navigations (both sides share the // slug → slug navigations (both sides share the same route.id, so
// same route.id, so paired UA transitions handle them). // 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]';
+16 -6
View File
@@ -415,21 +415,22 @@
</div> </div>
</section> </section>
<!-- Everything below the hero map — stage nav, photo strip, metrics,
tags, elevation chart, scroll area, footer — is wrapped in one panel
so view-transitions slide the whole block (with its own background)
up from the bottom on enter and down on exit. The hero map morphs
separately above this. -->
<div class="below-map" style="view-transition-name: hike-below-map">
{#if hasStages && stages} {#if hasStages && stages}
<HikeStageNav {stages} /> <HikeStageNav {stages} />
{/if} {/if}
{#if track && track.length > 0 && visibleImagePoints.length > 0} {#if track && track.length > 0 && visibleImagePoints.length > 0}
<section class="strip-area" style="view-transition-name: hike-strip"> <section class="strip-area">
<HikePhotoStrip images={visibleImagePoints} {track} {stages} /> <HikePhotoStrip images={visibleImagePoints} {track} {stages} />
</section> </section>
{/if} {/if}
<!-- Everything below the photo strip is wrapped so view-transitions
can slide the whole block (metrics, tags, elevation chart, scroll
area, footer) up from the bottom on enter and down on exit. The
hero map and strip animate separately above this. -->
<div class="below-strip" style="view-transition-name: hike-below-strip">
<section class="metrics" aria-label="Tourendaten"> <section class="metrics" aria-label="Tourendaten">
{#if hike.icon} {#if hike.icon}
<img class="route-icon" src={hike.icon} alt="" aria-hidden="true" /> <img class="route-icon" src={hike.icon} alt="" aria-hidden="true" />
@@ -985,6 +986,15 @@
opacity: 0.55; opacity: 0.55;
} }
/* The whole below-the-map block. The solid background makes its
view-transition snapshot an opaque panel, so on enter/exit the entire
sheet (background included) slides up/down from the bottom rather than
just the metric tiles appearing to float. */
.below-map {
position: relative;
background: var(--color-bg-primary);
}
.strip-area { .strip-area {
padding-inline: 1rem; padding-inline: 1rem;
margin-top: 0.5rem; margin-top: 0.5rem;