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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user