From 4acfd218836cc9505bf1d5789187bc79ce0861c1 Mon Sep 17 00:00:00 2001 From: Tudor Sitaru Date: Mon, 18 May 2026 15:35:17 +0100 Subject: [PATCH] feat(mobile): section-nav scroll affordance and drop illegible home previews MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- nextjs-app/components/HomeView.module.css | 18 +++++++++++ .../components/SchoolDetailView.module.css | 30 +++++++++++++++++- nextjs-app/components/SchoolDetailView.tsx | 31 +++++++++++++++++-- 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/nextjs-app/components/HomeView.module.css b/nextjs-app/components/HomeView.module.css index 6627b8a..3f239a9 100644 --- a/nextjs-app/components/HomeView.module.css +++ b/nextjs-app/components/HomeView.module.css @@ -1003,6 +1003,24 @@ } } +/* On phones, hide the scaled-down preview visuals (text becomes illegible + at this size) and let the explanatory text carry each card. */ +@media (max-width: 640px) { + .hiwVisual { + display: none; + } + .hiwCard { + padding: 1rem 1.1rem; + gap: 0.45rem; + } + .hiwTitle { + font-size: 1.05rem; + } + .hiwDesc { + font-size: 0.9rem; + } +} + /* ── Editorial section ───────────────────────────────── */ .editorial { diff --git a/nextjs-app/components/SchoolDetailView.module.css b/nextjs-app/components/SchoolDetailView.module.css index 23a1564..edc5ff3 100644 --- a/nextjs-app/components/SchoolDetailView.module.css +++ b/nextjs-app/components/SchoolDetailView.module.css @@ -190,6 +190,8 @@ -webkit-overflow-scrolling: touch; scrollbar-width: none; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04); + scroll-snap-type: x proximity; + scroll-padding-inline: 1rem; } .sectionNav::-webkit-scrollbar { @@ -202,6 +204,21 @@ align-items: center; } +/* Right-edge fade so users see there's more to scroll to. */ +@media (max-width: 640px) { + .sectionNav { + -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); + mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); + padding-right: 1.75rem; + } + + /* When scrolled to the end, drop the fade so the last item isn't dimmed. */ + .sectionNav.atEnd { + -webkit-mask-image: none; + mask-image: none; + } +} + .sectionNavBack { display: inline-flex; align-items: center; @@ -232,7 +249,8 @@ } .sectionNavLink { - display: inline-block; + display: inline-flex; + align-items: center; padding: 0.3rem 0.625rem; font-size: 0.75rem; font-weight: 500; @@ -241,6 +259,16 @@ border-radius: 4px; transition: all 0.15s ease; white-space: nowrap; + scroll-snap-align: start; +} + +@media (max-width: 640px) { + .sectionNavLink, + .sectionNavBack { + min-height: 36px; + padding: 0.5rem 0.75rem; + font-size: 0.8125rem; + } } .sectionNavLink:hover { diff --git a/nextjs-app/components/SchoolDetailView.tsx b/nextjs-app/components/SchoolDetailView.tsx index afe84e4..ed18a5e 100644 --- a/nextjs-app/components/SchoolDetailView.tsx +++ b/nextjs-app/components/SchoolDetailView.tsx @@ -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(''); + const sectionNavRef = useRef(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({ {/* Sticky Section Navigation */} -