feat(school-detail): move Back to a standalone link, dock "Back to top" in the nav
Replicate the Autotrader pattern: pull the page-back control out of the sticky section-nav bar and place a standalone "← Back" text link above the header card, on the page background, where it scrolls away with the page. It keeps the context-aware behaviour (router.back → /search fallback); the secondary view gains the same fallback for parity (it previously used a bare router.back() that dead-ends on deep links). The pinned slot the Back button vacated now holds a "↑ Top" control that smooth-scrolls to the top, so the sticky bar keeps a useful affordance. Applies to both primary and secondary detail views. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,29 @@
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Standalone back link, sits above the header card on the page background. */
|
||||
.topBack {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
margin: 0 0 0.75rem;
|
||||
padding: 0.25rem 0;
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
color: var(--accent-coral, #e07256);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
line-height: 1.2;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.topBack:hover {
|
||||
color: var(--accent-coral-dark, #c85a3e);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Header Section */
|
||||
.header {
|
||||
background: var(--bg-card, white);
|
||||
|
||||
@@ -106,6 +106,10 @@ export function SchoolDetailView({
|
||||
}
|
||||
};
|
||||
|
||||
const scrollToTop = () => {
|
||||
if (typeof window !== 'undefined') window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const el = sectionLinksRef.current;
|
||||
if (!el) return;
|
||||
@@ -306,6 +310,13 @@ export function SchoolDetailView({
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{/* Standalone back link, above the header — returns to wherever the
|
||||
user came from. Scrolls away with the page (the sticky bar keeps a
|
||||
"back to top" control in its place). */}
|
||||
<button type="button" onClick={handleBack} className={styles.topBack}>
|
||||
<span aria-hidden="true">←</span> Back
|
||||
</button>
|
||||
|
||||
{/* Header */}
|
||||
<header className={styles.header}>
|
||||
<div className={styles.headerContent}>
|
||||
@@ -439,9 +450,9 @@ export function SchoolDetailView({
|
||||
|
||||
{/* Sticky Section Navigation — docks under the global header */}
|
||||
<nav className={styles.sectionNav} aria-label="Page sections">
|
||||
<button onClick={handleBack} className={styles.sectionNavBack} aria-label="Back">
|
||||
<span aria-hidden="true">←</span>
|
||||
<span className={styles.sectionNavBackLabel}>Back</span>
|
||||
<button onClick={scrollToTop} className={styles.sectionNavBack} aria-label="Back to top">
|
||||
<span aria-hidden="true">↑</span>
|
||||
<span className={styles.sectionNavBackLabel}>Top</span>
|
||||
</button>
|
||||
|
||||
{/* Desktop: scrolling section links */}
|
||||
|
||||
@@ -4,6 +4,29 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Standalone back link, sits above the header card on the page background. */
|
||||
.topBack {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
margin: 0 0 0.75rem;
|
||||
padding: 0.25rem 0;
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
color: var(--accent-coral, #e07256);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
line-height: 1.2;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.topBack:hover {
|
||||
color: var(--accent-coral-dark, #c85a3e);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ── Header ──────────────────────────────────────────── */
|
||||
.header {
|
||||
background: var(--bg-card, white);
|
||||
|
||||
@@ -124,6 +124,20 @@ export function SecondarySchoolDetailView({
|
||||
}
|
||||
};
|
||||
|
||||
// Back returns wherever the user came from; deep-links fall back to search
|
||||
// so the button never dead-ends or leaves the site.
|
||||
const handleBack = () => {
|
||||
if (typeof window !== 'undefined' && window.history.length > 1) {
|
||||
router.back();
|
||||
} else {
|
||||
router.push('/search');
|
||||
}
|
||||
};
|
||||
|
||||
const scrollToTop = () => {
|
||||
if (typeof window !== 'undefined') window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
track('school_viewed', {
|
||||
urn: schoolInfo.urn,
|
||||
@@ -200,6 +214,12 @@ export function SecondarySchoolDetailView({
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{/* Standalone back link, above the header — returns wherever the user
|
||||
came from. Scrolls away; the sticky bar keeps a "back to top" control. */}
|
||||
<button type="button" onClick={handleBack} className={styles.topBack}>
|
||||
<span aria-hidden="true">←</span> Back
|
||||
</button>
|
||||
|
||||
{/* ── Header ─────────────────────────────────────── */}
|
||||
<header className={styles.header}>
|
||||
<div className={styles.headerContent}>
|
||||
@@ -324,7 +344,7 @@ export function SecondarySchoolDetailView({
|
||||
{/* ── Sticky section navigation ─────────────────────── */}
|
||||
<nav className={styles.tabNav} aria-label="Page sections">
|
||||
<div className={styles.tabNavInner}>
|
||||
<button onClick={() => router.back()} className={styles.backBtn}>← Back</button>
|
||||
<button onClick={scrollToTop} className={styles.backBtn} aria-label="Back to top">↑ Top</button>
|
||||
{navItems.length > 0 && <div className={styles.tabNavDivider} />}
|
||||
{navItems.map(({ id, label }) => (
|
||||
<a
|
||||
|
||||
Reference in New Issue
Block a user