feat(hikes): view-transition flow across /hikes ↔ /hikes/[slug]

Cards + filter bar fly up from below when arriving at /hikes and drop
back down when leaving (in both directions of /hikes ↔ detail). Clicked
card morphs into the detail hero with a cross-fade so the thumbnail
dissolves into the map instead of snapping. Photo strip slides in from
the right. Root content cross-fades so metrics + content under the hero
phase in rather than appear at the end of the morph.

Track JSON moves from a client-side $effect into +page.ts so the strip
is in the DOM at view-transition snapshot time — also kills the brief
layout shift when it used to pop in post-load.
This commit is contained in:
2026-05-26 10:34:00 +02:00
parent f1c0304b14
commit b49a299371
7 changed files with 174 additions and 50 deletions
+22 -8
View File
@@ -227,14 +227,20 @@
</section>
<div class="below-hero">
<HikesFilterBar
hikes={data.hikes}
{filter}
resultCount={visible.length}
totalCount={data.hikes.length}
totalKm={totals.km}
totalGain={totals.gain}
/>
<!-- Wrapped in a named view-transition box so the filter bar can fly
up alongside the cards when arriving at /hikes from outside the
hikes group. Same `view-transition-class: hike-fly-in` as each
HikeCard so one CSS rule animates both. -->
<div class="filter-vt-box">
<HikesFilterBar
hikes={data.hikes}
{filter}
resultCount={visible.length}
totalCount={data.hikes.length}
totalKm={totals.km}
totalGain={totals.gain}
/>
</div>
{#if visible.length === 0}
<p class="empty">Keine Wanderung entspricht den aktuellen Filtern.</p>
@@ -393,4 +399,12 @@
gap: 1rem;
}
}
/* Wrapper sets the view-transition name/class on the filter bar so the
* same .hike-fly-in rules in app.css that animate the cards also
* animate this bar (fly-in on /hikes enter, fly-out on /hikes exit). */
.filter-vt-box {
view-transition-name: hikes-filter-bar;
view-transition-class: hike-fly-in;
}
</style>