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:
@@ -205,6 +205,7 @@
|
||||
flex: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
padding: 0.3rem 0.625rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
@@ -294,6 +295,119 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ── Mobile: the scrolling links collapse into one "section" menu button ──
|
||||
(hidden on desktop, where the links fit). */
|
||||
.sectionNavMenu {
|
||||
display: none; /* shown only ≤640px */
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
min-height: 38px;
|
||||
padding: 0.34rem 0.7rem;
|
||||
background: var(--bg-secondary, #f3ede4);
|
||||
border: 1px solid var(--border-color, #e5dfd5);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-dm-sans), "DM Sans", sans-serif;
|
||||
color: var(--text-primary, #1a1612);
|
||||
}
|
||||
|
||||
.sectionNavMenuCur {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sectionNavMenuEyebrow {
|
||||
flex: none;
|
||||
font-size: 0.64rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted, #6d685f);
|
||||
}
|
||||
|
||||
.sectionNavMenuNow {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.sectionNavMenuChev {
|
||||
flex: none;
|
||||
color: var(--text-muted, #6d685f);
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
/* Compact icon version of the Compare CTA, used on mobile. */
|
||||
.sectionNavCompareIcon {
|
||||
display: none; /* shown only ≤640px */
|
||||
position: relative;
|
||||
flex: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 9px;
|
||||
border: 1px solid var(--accent-coral, #e07256);
|
||||
background: var(--accent-coral, #e07256);
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.sectionNavCompareIcon svg {
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
}
|
||||
|
||||
.sectionNavCompareBadge {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: -5px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: var(--bg-card, white);
|
||||
color: var(--accent-coral-dark, #c45a3f);
|
||||
border: 1.5px solid var(--accent-coral, #e07256);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.sectionNavCompareIconIn {
|
||||
background: var(--bg-card, white);
|
||||
border-color: var(--accent-teal, #2d7d7d);
|
||||
color: var(--accent-teal, #2d7d7d);
|
||||
}
|
||||
|
||||
/* Swap which controls show at the mobile breakpoint. */
|
||||
@media (max-width: 640px) {
|
||||
.sectionNavLinks,
|
||||
.sectionNavCompare,
|
||||
.sectionNavAll,
|
||||
.sectionNavBackLabel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sectionNavMenu {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.sectionNavCompareIcon {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compare CTA carried into the bar once the hero's button scrolls away. */
|
||||
.sectionNavCompare {
|
||||
flex: none;
|
||||
@@ -333,13 +447,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* "All ▾" jump menu. */
|
||||
.sectionNavAllWrap {
|
||||
flex: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* "All ▾" jump menu (desktop). */
|
||||
.sectionNavAll {
|
||||
flex: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
|
||||
@@ -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,18 +475,58 @@ export function SchoolDetailView({
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* The hero's Compare CTA, carried in once it scrolls out of view */}
|
||||
{/* Mobile: a single "section" menu button that opens the jump sheet */}
|
||||
{navItems.length > 0 && (
|
||||
<button
|
||||
type="button"
|
||||
className={styles.sectionNavMenu}
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={sectionsOpen}
|
||||
onClick={() => setSectionsOpen((o) => !o)}
|
||||
>
|
||||
<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>
|
||||
)}
|
||||
|
||||
{/* The hero's Compare CTA, carried in once it scrolls out of view.
|
||||
Desktop shows a labelled pill; mobile a compact icon. */}
|
||||
{!heroCtaVisible && (
|
||||
<>
|
||||
<button
|
||||
onClick={handleComparisonToggle}
|
||||
className={`${styles.sectionNavCompare}${isInComparison ? ` ${styles.sectionNavCompareIn}` : ''}`}
|
||||
>
|
||||
{isInComparison ? '✓ Comparing' : '+ Compare'}
|
||||
</button>
|
||||
<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 && (
|
||||
<div className={styles.sectionNavAllWrap}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.sectionNavAll}
|
||||
@@ -489,11 +536,14 @@ export function SchoolDetailView({
|
||||
>
|
||||
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}>On this page</div>
|
||||
<div className={styles.sectionsPanelHead}>Jump to section</div>
|
||||
{navItems.map(({ id, label }) => (
|
||||
<a
|
||||
key={id}
|
||||
@@ -512,8 +562,6 @@ export function SchoolDetailView({
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
{/* Ofsted Rating / Report Card */}
|
||||
|
||||
Reference in New Issue
Block a user