feat(trip): pill radius on panel toggles, slide animation, mobile close button

- Radius: trip-panel-toggle now uses --radius-full, consistent with filter pills
- Animation: stats/cycling blocks use CSS grid-template-rows 0fr→1fr transition
  (inner trip-panel-inner div carries decoration so border/padding don't peek
  out when collapsed; display:none removed)
- Close button: ↑ Close stats / ↑ Close cycling at bottom of each panel,
  hidden ≥769px, triggers the header toggle via data-toggle attribute

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
This commit is contained in:
2026-06-21 23:09:52 +02:00
parent c3cb224402
commit 02fc666661
2 changed files with 132 additions and 95 deletions
+45 -10
View File
@@ -968,7 +968,7 @@ body::after {
.trip-panel-toggle { .trip-panel-toggle {
background: transparent; background: transparent;
border: 1px solid var(--color-border); border: 1px solid var(--color-border);
border-radius: var(--radius-sm); border-radius: var(--radius-full);
padding: var(--space-1) var(--space-3); padding: var(--space-1) var(--space-3);
font-family: var(--font-ui); font-family: var(--font-ui);
font-size: var(--text-sm); font-size: var(--text-sm);
@@ -1149,12 +1149,54 @@ body::after {
/* ── Trip page inline stats block ───────────────────────────────────────────── */ /* ── Trip page inline stats block ───────────────────────────────────────────── */
.trip-stats-block { .trip-stats-block,
.trip-cycling-block {
display: grid;
grid-template-rows: 0fr;
overflow: hidden;
margin-bottom: 0;
transition: grid-template-rows 0.35s ease, margin-bottom 0.35s ease;
}
.trip-stats-block.is-open,
.trip-cycling-block.is-open {
grid-template-rows: 1fr;
margin-bottom: var(--space-6);
}
.trip-panel-inner {
min-height: 0;
overflow: hidden;
background: var(--color-canvas); background: var(--color-canvas);
border: 1px solid var(--color-border); border: 1px solid var(--color-border);
border-radius: var(--radius-md); border-radius: var(--radius-md);
padding: var(--space-6); padding: var(--space-6);
margin-bottom: var(--space-6); }
.trip-panel-close {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
margin-top: var(--space-6);
padding: var(--space-2) var(--space-4);
background: transparent;
border: 1px solid var(--color-border);
border-radius: var(--radius-full);
font-family: var(--font-ui);
font-size: var(--text-sm);
color: var(--color-ink-muted);
cursor: pointer;
transition: color 0.15s, border-color 0.15s;
}
.trip-panel-close:hover {
color: var(--color-ink);
border-color: var(--color-ink-muted);
}
@media (min-width: 769px) {
.trip-panel-close { display: none; }
} }
.trip-stats-grid { .trip-stats-grid {
@@ -1181,13 +1223,6 @@ body::after {
/* ── Trip page cycling panel ─────────────────────────────────────────────────── */ /* ── Trip page cycling panel ─────────────────────────────────────────────────── */
.trip-cycling-block {
background: var(--color-canvas);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--space-6);
margin-bottom: var(--space-6);
}
.trip-cycling-header { .trip-cycling-header {
display: flex; display: flex;
+87 -85
View File
@@ -137,78 +137,84 @@
</div> </div>
</div> </div>
<div id="trip-stats-block" class="trip-stats-block" style="display:none"> <div id="trip-stats-block" class="trip-stats-block">
<div class="trip-stats-grid"> <div class="trip-panel-inner">
<div class="stat-block"> <div class="trip-stats-grid">
<span class="stat-value">{{ days_on_road }}</span> <div class="stat-block">
<span class="stat-label">{{ days_on_road == 1 ? 'day' : 'days' }} on the road</span> <span class="stat-value">{{ days_on_road }}</span>
</div> <span class="stat-label">{{ days_on_road == 1 ? 'day' : 'days' }} on the road</span>
<div class="stat-block"> </div>
<span class="stat-value">{{ journal_count }}</span> <div class="stat-block">
<span class="stat-label">{{ journal_count == 1 ? 'entry' : 'entries' }} posted</span> <span class="stat-value">{{ journal_count }}</span>
</div> <span class="stat-label">{{ journal_count == 1 ? 'entry' : 'entries' }} posted</span>
<div class="stat-block"> </div>
<span class="stat-value">{{ country_display|length }}</span> <div class="stat-block">
<span class="stat-label">{{ country_display|length == 1 ? 'country' : 'countries' }} visited</span> <span class="stat-value">{{ country_display|length }}</span>
</div> <span class="stat-label">{{ country_display|length == 1 ? 'country' : 'countries' }} visited</span>
<div class="stat-block"> </div>
<span class="stat-value">{{ city_display|length }}</span> <div class="stat-block">
<span class="stat-label">{{ city_display|length == 1 ? 'city' : 'cities' }} visited</span> <span class="stat-value">{{ city_display|length }}</span>
</div> <span class="stat-label">{{ city_display|length == 1 ? 'city' : 'cities' }} visited</span>
<div class="stat-block"> </div>
<span class="stat-value" id="stat-distance">—</span> <div class="stat-block">
<span class="stat-label">{{ has_gpx ? '🚴 km cycled' : '🧭 km roamed' }}</span> <span class="stat-value" id="stat-distance">—</span>
</div> <span class="stat-label">{{ has_gpx ? '🚴 km cycled' : '🧭 km roamed' }}</span>
<div class="stat-block"> </div>
{% if temp_min is not null %} <div class="stat-block">
<span class="stat-value">{{ temp_min == temp_max ? temp_min : temp_min ~ ' → ' ~ temp_max }}</span> {% if temp_min is not null %}
{% else %} <span class="stat-value">{{ temp_min == temp_max ? temp_min : temp_min ~ ' → ' ~ temp_max }}</span>
<span class="stat-value">—</span> {% else %}
{% endif %} <span class="stat-value">—</span>
<span class="stat-label">°C range</span> {% endif %}
<span class="stat-label">°C range</span>
</div>
</div> </div>
{% if country_display|length > 0 %}
<p class="trip-stats-countries">{{ country_display|join(' · ') }}</p>
{% endif %}
<p class="trip-stats-note">{{ has_gpx ? 'Distance based on GPS track data.' : 'Distance is approximate — straight lines between entry locations.' }}</p>
<button class="trip-panel-close" data-toggle="trip-stats-toggle">↑ Close stats</button>
</div> </div>
{% if country_display|length > 0 %}
<p class="trip-stats-countries">{{ country_display|join(' · ') }}</p>
{% endif %}
<p class="trip-stats-note">{{ has_gpx ? 'Distance based on GPS track data.' : 'Distance is approximate — straight lines between entry locations.' }}</p>
</div> </div>
{% if has_gpx %} {% if has_gpx %}
<div id="trip-cycling-block" class="trip-cycling-block" style="display:none"> <div id="trip-cycling-block" class="trip-cycling-block">
<div class="trip-cycling-header"> <div class="trip-panel-inner">
<span class="trip-cycling-icon">🚴</span> <div class="trip-cycling-header">
<span class="trip-cycling-title">Cycling Stats</span> <span class="trip-cycling-icon">🚴</span>
</div> <span class="trip-cycling-title">Cycling Stats</span>
<div class="trip-cycling-grid">
<div class="stat-block">
<span class="stat-value" id="cyc-distance">—</span>
<span class="stat-label">km distance</span>
</div> </div>
<div class="stat-block"> <div class="trip-cycling-grid">
<span class="stat-value" id="cyc-ele-gain">—</span> <div class="stat-block">
<span class="stat-label">m ↑ gain</span> <span class="stat-value" id="cyc-distance">—</span>
</div> <span class="stat-label">km distance</span>
<div class="stat-block"> </div>
<span class="stat-value" id="cyc-ele-loss">—</span> <div class="stat-block">
<span class="stat-label">m ↓ loss</span> <span class="stat-value" id="cyc-ele-gain">—</span>
</div> <span class="stat-label">m ↑ gain</span>
<div class="stat-block"> </div>
<span class="stat-value" id="cyc-highest">—</span> <div class="stat-block">
<span class="stat-label">m highest</span> <span class="stat-value" id="cyc-ele-loss">—</span>
</div> <span class="stat-label">m ↓ loss</span>
<div class="stat-block"> </div>
<span class="stat-value" id="cyc-lowest">—</span> <div class="stat-block">
<span class="stat-label">m lowest</span> <span class="stat-value" id="cyc-highest">—</span>
</div> <span class="stat-label">m highest</span>
<div class="stat-block"> </div>
<span class="stat-value" id="cyc-moving-time">—</span> <div class="stat-block">
<span class="stat-label">moving time</span> <span class="stat-value" id="cyc-lowest">—</span>
</div> <span class="stat-label">m lowest</span>
<div class="stat-block"> </div>
<span class="stat-value" id="cyc-avg-speed">—</span> <div class="stat-block">
<span class="stat-label">km/h avg speed</span> <span class="stat-value" id="cyc-moving-time">—</span>
<span class="stat-label">moving time</span>
</div>
<div class="stat-block">
<span class="stat-value" id="cyc-avg-speed">—</span>
<span class="stat-label">km/h avg speed</span>
</div>
</div> </div>
<button class="trip-panel-close" data-toggle="trip-cycling-toggle">↑ Close cycling</button>
</div> </div>
</div> </div>
{% endif %} {% endif %}
@@ -478,29 +484,25 @@ function parseGpxFiles(urls, callback) {
} }
} }
// Stats toggle function makePanelToggle(toggleId, blockId) {
var statsToggle = document.getElementById('trip-stats-toggle'); var toggle = document.getElementById(toggleId);
var statsBlock = document.getElementById('trip-stats-block'); var block = document.getElementById(blockId);
if (statsToggle && statsBlock) { if (!toggle || !block) return;
statsToggle.addEventListener('click', function() { toggle.addEventListener('click', function() {
var isOpen = statsBlock.style.display !== 'none'; var isOpen = block.classList.contains('is-open');
statsBlock.style.display = isOpen ? 'none' : ''; block.classList.toggle('is-open', !isOpen);
statsToggle.classList.toggle('is-active', !isOpen); toggle.classList.toggle('is-active', !isOpen);
statsToggle.setAttribute('aria-expanded', isOpen ? 'false' : 'true'); toggle.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
}); });
} }
makePanelToggle('trip-stats-toggle', 'trip-stats-block');
makePanelToggle('trip-cycling-toggle', 'trip-cycling-block');
// Cycling toggle (only present when has_gpx) // Close buttons inside panels (mobile only via CSS)
var cycToggle = document.getElementById('trip-cycling-toggle'); document.querySelectorAll('.trip-panel-close').forEach(function(btn) {
var cycBlock = document.getElementById('trip-cycling-block'); var toggleBtn = document.getElementById(btn.getAttribute('data-toggle'));
if (cycToggle && cycBlock) { if (toggleBtn) btn.addEventListener('click', function() { toggleBtn.click(); });
cycToggle.addEventListener('click', function() { });
var isOpen = cycBlock.style.display !== 'none';
cycBlock.style.display = isOpen ? 'none' : '';
cycToggle.classList.toggle('is-active', !isOpen);
cycToggle.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
});
}
})(); })();
/* ── Back to top ─────────────────────────────────────────── */ /* ── Back to top ─────────────────────────────────────────── */