feat(mobile): section-nav scroll affordance and drop illegible home previews
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 14s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 52s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 14s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 0s

MOB-03: The school-detail section nav (SATs / Admissions / Pupils /
Location / History) overflows on phones (scrollWidth 462 vs clientWidth
356) with no signal that more tabs exist past the right edge. Add a
right-edge mask-image fade at ≤640px, scroll-snap on each link, and
bigger tap targets (min-height 36px, padding bumped). A scroll/resize
listener toggles an .atEnd class that removes the fade once the user
has scrolled to the last tab.

MOB-04: The "What you'll see on every school" cards rendered preview
visuals (mini cascade chart, Ofsted badge, compare table) with text
scaled down to 6–10px — unreadable on a phone. Hide .hiwVisual at
≤640px and let the explanatory text carry each card; tiny visible
text count on the home dropped from 51 to 8.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Tudor Sitaru
2026-05-18 15:35:17 +01:00
parent 2a8ff29ccd
commit 4acfd21883
3 changed files with 76 additions and 3 deletions
+29 -2
View File
@@ -5,7 +5,7 @@
'use client';
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useRouter } from 'next/navigation';
import { useComparison } from '@/hooks/useComparison';
import { PerformanceChart } from './PerformanceChart';
@@ -75,6 +75,29 @@ export function SchoolDetailView({
const isInComparison = isSelected(schoolInfo.urn);
const [activeSection, setActiveSection] = useState<string>('');
const sectionNavRef = useRef<HTMLElement | null>(null);
const [sectionNavAtEnd, setSectionNavAtEnd] = useState(false);
useEffect(() => {
const el = sectionNavRef.current;
if (!el) return;
const update = () => {
const overflow = el.scrollWidth - el.clientWidth;
// No overflow → treat as "at end" so the fade is hidden.
if (overflow <= 1) {
setSectionNavAtEnd(true);
return;
}
setSectionNavAtEnd(el.scrollLeft >= overflow - 2);
};
update();
el.addEventListener('scroll', update, { passive: true });
window.addEventListener('resize', update);
return () => {
el.removeEventListener('scroll', update);
window.removeEventListener('resize', update);
};
}, []);
const latestResults = yearlyData.length > 0 ? yearlyData[yearlyData.length - 1] : null;
@@ -356,7 +379,11 @@ export function SchoolDetailView({
</header>
{/* Sticky Section Navigation */}
<nav className={styles.sectionNav} aria-label="Page sections">
<nav
ref={sectionNavRef}
className={`${styles.sectionNav}${sectionNavAtEnd ? ` ${styles.atEnd}` : ''}`}
aria-label="Page sections"
>
<div className={styles.sectionNavInner}>
<button onClick={() => router.back()} className={styles.sectionNavBack}> Back</button>
{navItems.length > 0 && <div className={styles.sectionNavDivider} />}