hub detail pages: shared Back button with proper single-history

#3 Back buttons: HubShell now renders a top-left "‹ Back" on each detail page
(/word /quote /onthisday). It mirrors the News reader's rule — if you arrived by
an in-app navigation, history.back() returns you to the hub with state/scroll
intact (and the browser Back button traverses the same single history); a direct
deep-link has no app history, so it falls back to goto('/home3'). Opt-out via
back={false}; relabel via backLabel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-22 22:08:39 -04:00
parent b174d8d2a7
commit d3c7282850
+35 -3
View File
@@ -1,14 +1,35 @@
<script>
// Shared shell for the hub + its detail pages (/word, /quote, /onthisday): the editorial
// top bar, footer, fonts, and design tokens. Content goes in the default slot.
import { afterNavigate, goto } from '$app/navigation';
import HubBar from './HubBar.svelte';
let { active = '', children } = $props();
let { active = '', back = true, backLabel = 'Back', children } = $props();
// Same single-history rule as the News reader's in-page Back: if we arrived here by an
// in-app navigation, history.back() returns to wherever (the hub) with state intact; a
// direct deep-link has no app history, so fall back to the hub.
let cameFromApp = $state(false);
afterNavigate(({ from }) => { if (from) cameFromApp = true; });
function goBack() {
if (cameFromApp && typeof history !== 'undefined') history.back();
else goto('/home3');
}
</script>
<div class="page">
<HubBar {active} />
<main class="shell-main">{@render children?.()}</main>
<main class="shell-main">
{#if back}
<button class="back" onclick={goBack} aria-label="Go back">
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M15 18l-6-6 6-6" />
</svg>
{backLabel}
</button>
{/if}
{@render children?.()}
</main>
<footer class="foot">upbeatBytes — no ads, no paywalls, no doomscrolling.</footer>
</div>
@@ -29,9 +50,20 @@
.shell-main {
flex: 1; width: 100%; max-width: 1180px; margin: 0 auto; box-sizing: border-box;
padding: clamp(26px, 5vw, 56px) clamp(18px, 5vw, 44px) clamp(40px, 6vw, 72px);
padding: clamp(20px, 4vw, 40px) clamp(18px, 5vw, 44px) clamp(40px, 6vw, 72px);
}
/* top-left Back — returns to the hub (or wherever you came from), preserving history */
.back {
display: inline-flex; align-items: center; gap: 6px; margin: 0 0 clamp(14px, 3vw, 24px);
background: none; border: none; cursor: pointer; padding: 6px 10px 6px 0;
font-family: inherit; font-size: 14px; font-weight: 600; color: var(--body);
transition: color 0.15s ease;
}
.back:hover { color: var(--teal); }
.back svg { transition: transform 0.15s ease; }
.back:hover svg { transform: translateX(-2px); }
.foot {
text-align: center; max-width: 1180px; width: 100%; margin: 14px auto 0; box-sizing: border-box;
padding: 20px clamp(18px, 5vw, 44px) 30px; font-size: 13px; color: var(--muted);