feat(school-detail): mobile section nav becomes a menu; Compare shrinks to an icon
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 12s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 48s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 13s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 12s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 48s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 13s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s
Fixes the cramped mobile bar where Back, the carried Compare pill and All ▾ all competed with the scrolling section links: - On mobile the horizontal swipe strip collapses into a single "Section: <current> ▾" button that opens the existing jump-to-section sheet — no fragile horizontal swipe, and it doubles as a "you are here" indicator. - The carried Compare CTA becomes a compact 38px icon on mobile (compare-arrows + "add" badge; flips to a teal check when in the comparison), so it no longer crowds the section control. Desktop keeps the labelled pill. - Back is icon-only on mobile (label hidden), text on desktop. - Desktop is unchanged: horizontal links + All ▾, full CTA in the hero. Both the mobile section menu and the desktop All ▾ open the same sheet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lh7Js5xSetKNzLVr9ArXLF
This commit is contained in:
@@ -291,6 +291,9 @@ export function SchoolDetailView({
|
||||
|
||||
const heroAcademicYear = latestResults ? formatAcademicYear(latestResults.year) : '';
|
||||
|
||||
// Label shown in the mobile "section" menu button — the section in view.
|
||||
const activeNavLabel = (navItems.find((n) => n.id === activeSection) ?? navItems[0])?.label ?? '';
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{/* Header */}
|
||||
@@ -450,8 +453,12 @@ 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}>← Back</button>
|
||||
<button onClick={handleBack} className={styles.sectionNavBack} aria-label="Back">
|
||||
<span aria-hidden="true">←</span>
|
||||
<span className={styles.sectionNavBackLabel}>Back</span>
|
||||
</button>
|
||||
|
||||
{/* Desktop: scrolling section links */}
|
||||
<div
|
||||
ref={sectionLinksRef}
|
||||
className={`${styles.sectionNavLinks}${sectionNavAtEnd ? ` ${styles.atEnd}` : ''}`}
|
||||
@@ -468,51 +475,92 @@ export function SchoolDetailView({
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* The hero's Compare CTA, carried in once it scrolls out of view */}
|
||||
{!heroCtaVisible && (
|
||||
{/* Mobile: a single "section" menu button that opens the jump sheet */}
|
||||
{navItems.length > 0 && (
|
||||
<button
|
||||
onClick={handleComparisonToggle}
|
||||
className={`${styles.sectionNavCompare}${isInComparison ? ` ${styles.sectionNavCompareIn}` : ''}`}
|
||||
type="button"
|
||||
className={styles.sectionNavMenu}
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={sectionsOpen}
|
||||
onClick={() => setSectionsOpen((o) => !o)}
|
||||
>
|
||||
{isInComparison ? '✓ Comparing' : '+ Compare'}
|
||||
<span className={styles.sectionNavMenuCur}>
|
||||
<span className={styles.sectionNavMenuEyebrow}>Section</span>
|
||||
<span className={styles.sectionNavMenuNow}>{activeNavLabel}</span>
|
||||
</span>
|
||||
<span className={styles.sectionNavMenuChev} aria-hidden="true">▾</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{navItems.length > 0 && (
|
||||
<div className={styles.sectionNavAllWrap}>
|
||||
{/* The hero's Compare CTA, carried in once it scrolls out of view.
|
||||
Desktop shows a labelled pill; mobile a compact icon. */}
|
||||
{!heroCtaVisible && (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.sectionNavAll}
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={sectionsOpen}
|
||||
onClick={() => setSectionsOpen((o) => !o)}
|
||||
onClick={handleComparisonToggle}
|
||||
className={`${styles.sectionNavCompare}${isInComparison ? ` ${styles.sectionNavCompareIn}` : ''}`}
|
||||
>
|
||||
All <span aria-hidden="true">▾</span>
|
||||
{isInComparison ? '✓ Comparing' : '+ Compare'}
|
||||
</button>
|
||||
{sectionsOpen && (
|
||||
<>
|
||||
<div className={styles.sectionsBackdrop} onClick={() => setSectionsOpen(false)} />
|
||||
<div className={styles.sectionsPanel} role="menu" aria-label="Jump to section">
|
||||
<div className={styles.sectionsPanelHead}>On this page</div>
|
||||
{navItems.map(({ id, label }) => (
|
||||
<a
|
||||
key={id}
|
||||
href={`#${id}`}
|
||||
role="menuitem"
|
||||
className={`${styles.sectionsItem}${activeSection === id ? ` ${styles.sectionsItemActive}` : ''}`}
|
||||
onClick={() => {
|
||||
setSectionsOpen(false);
|
||||
track('section_nav_used', { section: id, via: 'all_menu' });
|
||||
}}
|
||||
>
|
||||
<span>{label}</span>
|
||||
{activeSection === id && <span className={styles.sectionsTick} aria-hidden="true">✓</span>}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={handleComparisonToggle}
|
||||
className={`${styles.sectionNavCompareIcon}${isInComparison ? ` ${styles.sectionNavCompareIconIn}` : ''}`}
|
||||
aria-label={isInComparison ? 'In comparison' : 'Add to compare'}
|
||||
title={isInComparison ? 'In comparison' : 'Add to compare'}
|
||||
>
|
||||
{isInComparison ? (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<path d="m5 12 5 5 9-11" />
|
||||
</svg>
|
||||
) : (
|
||||
<>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true">
|
||||
<path d="M4 7h13l-3-3" />
|
||||
<path d="M20 17H7l3 3" />
|
||||
</svg>
|
||||
<span className={styles.sectionNavCompareBadge} aria-hidden="true">+</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Desktop: "All ▾" trigger (same sheet as the mobile section menu) */}
|
||||
{navItems.length > 0 && (
|
||||
<button
|
||||
type="button"
|
||||
className={styles.sectionNavAll}
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={sectionsOpen}
|
||||
onClick={() => setSectionsOpen((o) => !o)}
|
||||
>
|
||||
All <span aria-hidden="true">▾</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Shared jump-to-section sheet (dropdown on desktop, bottom sheet on mobile) */}
|
||||
{sectionsOpen && (
|
||||
<>
|
||||
<div className={styles.sectionsBackdrop} onClick={() => setSectionsOpen(false)} />
|
||||
<div className={styles.sectionsPanel} role="menu" aria-label="Jump to section">
|
||||
<div className={styles.sectionsPanelHead}>Jump to section</div>
|
||||
{navItems.map(({ id, label }) => (
|
||||
<a
|
||||
key={id}
|
||||
href={`#${id}`}
|
||||
role="menuitem"
|
||||
className={`${styles.sectionsItem}${activeSection === id ? ` ${styles.sectionsItemActive}` : ''}`}
|
||||
onClick={() => {
|
||||
setSectionsOpen(false);
|
||||
track('section_nav_used', { section: id, via: 'all_menu' });
|
||||
}}
|
||||
>
|
||||
<span>{label}</span>
|
||||
{activeSection === id && <span className={styles.sectionsTick} aria-hidden="true">✓</span>}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user