fix/leaflet-specificity-and-font-assertion
14
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2433101fa0 |
fix(design): resolve the font tokens, and pull form controls and map chrome onto the palette
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m3s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 11s
PR Checks / Build Frontend (no push) (pull_request) Successful in 50s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 1m22s
Staging audit of the Cohort identity found the whole site rendering in Times. Root cause: next/font's variable classes were on <body>, while globals.css declares --font-display/--font-ui/--font-prose on :root as `var(--font-schibsted), ...`. A custom property's var() references resolve on the element that declares it, so at :root --font-schibsted was undefined, --font-display computed to the guaranteed-invalid value, and every font-family referencing it fell back. Nothing threw and the build was green — the only symptom was visual. Verified the mechanism in a browser both ways round: class on <body> gives an empty token and a Times body font; class on <html> resolves to Schibsted Grotesk. The classes now sit on <html>. Two colour escapes from the same audit: * Form controls don't inherit font or colour from their parent, so the omni search input and the map's "Open full map" button rendered in the system font at pure black. Nearly invisible against --text-primary in light mode and completely invisible on the dark ground. Added a base inherit rule. * Leaflet ships its own palette — a #ddd tile backdrop, #333 attribution text and a #0078A8 link blue that was the most saturated colour anywhere on the site. The map chrome now uses tokens; the tiles stay as OSM renders them. Three e2e gates added, because the existing suite passed while the site was entirely in Times: * the font tokens resolve to a non-empty value and the applied family is Schibsted, not a serif fallback * no visible element renders in the browser's default black * every rendered colour comes from the token palette — the manual audit, turned into a gate Also made the og:image check environment-relative: metadataBase pins canonical URLs to the production host, so the absolute URL pointed off-environment when the suite ran against staging. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8ab0ac0a04 |
feat(design): adopt the Cohort identity — new palette, type, mark and dark theme
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m6s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 12s
PR Checks / Build Frontend (no push) (pull_request) Successful in 49s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Failing after 3m13s
Implements the direction agreed from the identity board: Route C ("Cohort")
with the paper ground from C1, the Schibsted Grotesk / Literata pairing from
C2, and the iris accent from C3. Dark theme is in scope from the start rather
than retrofitted.
The audit found three things wrong beyond taste:
* No brand asset set. og:image was absent entirely, so every link shared into
a class WhatsApp group rendered as a bare grey card. apple-touch-icon pointed
at an SVG, which iOS ignores, and the manifest shipped no PNGs, so Android
installs had no icon. The header mark and the favicon had also drifted into
two different logos.
* No colour discipline. --primary and --trend-down were the same coral, so the
main CTA and "below average" shared a hue. 58 distinct hex values were spread
across component CSS, and the chart palette was still Chart.js's stock demo
colours.
* A dark theme that was declared but never built — themeColor announced a dark
variant with no dark styling behind it.
What changed:
Colour now has exactly three jobs that never borrow each other's hues: brand
(iris) for interactive and identity, status (teal/amber) for above/below a
comparison point, and phase for categories. Teal/amber rather than green/red
keeps the above/below signal readable for every form of colour blindness.
Every chromatic literal in component CSS is now a token, and the JS-painted
surfaces (Chart.js, Leaflet) read the tokens through lib/theme so they follow
the theme instead of ignoring it.
The mark is the five-bar cohort spread — the same object as the distribution
strip inside a school row, built from opacity steps so it inverts cleanly.
components/Logo.tsx is the single source; the favicon, apple-icon and share
card all derive from its geometry.
globals.css drops 123 dead global classes left over from the vanilla-JS app
(only the btn family, .skip-link and .main were still referenced), along with
the noise overlay. It also gains prefers-reduced-motion support, which was
missing entirely, and a type scale so the 54 ad-hoc font sizes have somewhere
to converge.
Verified: tsc clean, 159 unit tests pass, production build succeeds and
prerenders /icon.svg, /apple-icon and /opengraph-image. Three e2e journeys
added for the asset set, the themeColor/background match, and the dark theme
actually repainting — all silent failures that nothing on the page reveals.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
1f80571b1f |
feat(analytics): load Umami heatmap recorder
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m4s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 11s
PR Checks / Build Frontend (no push) (pull_request) Successful in 47s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Failing after 42s
Adds the Umami session recorder (recorder.js) that powers heatmaps, alongside the existing analytics tracker. Loaded via next/script with the same data-domains guard as the main tracker so it only fires on the production hostnames — staging runs the same image under a different host and must not feed the heatmap. Cookieless: recorder.js uses Umami's server-side session model (no cookie or local storage on the device), so it adds no new PECR consent trigger. Verified with tsc --noEmit and next build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB |
||
|
|
b5a63e82d4 |
fix(analytics): restrict Umami to production hostnames
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 9m37s
PR Checks / Backend Smoke (pull_request) Successful in 4s
PR Checks / Build Backend (no push) (pull_request) Successful in 11s
PR Checks / Build Frontend (no push) (pull_request) Successful in 48s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Failing after 36s
The same frontend image runs on staging and prod (build once, promote), so a build-time env var can't tell them apart. Umami's data-domains attribute scopes the tracker client-side: events only fire when location.hostname is a production domain, so staging traffic and the E2E journeys never pollute the dashboards. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PqGhF93UrpDNvXBLMjJENL |
||
|
|
62eeee5f7c |
perf: cache aggressively and trim client bundle
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 1m1s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 53s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 2m4s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s
Frontend - Dynamic-import Chart.js components on detail/compare views so Chart.js no longer ships in initial JS. - Drop force-dynamic on home, compare, rankings so internal data fetches reuse Next.js's per-call revalidate cache. - Switch /school/[slug] to ISR with a 7-day revalidate window (school data updates annually). - Preconnect to analytics + postcodes.io; remove redundant defer on the Umami Script tag (afterInteractive already covers it). - Bump images.minimumCacheTTL to 1 year. - Extract HowItWorks and Editorial sections as server components passed to HomeView via slot props so their JSX stays out of the client bundle. Backend - Add GZipMiddleware (min 512 bytes). - Add CacheAndETagMiddleware: per-path Cache-Control with long s-maxage + stale-while-revalidate, ETag generation, and 304 on If-None-Match. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
a7ab624a01 |
feat(analytics): enable Umami's built-in Web Vitals collection
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 12s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 50s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 12s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 0s
Adds data-performance="true" to the Umami script tag. From v3.1.0+, this hooks PerformanceObserver and posts LCP, INP, CLS, FCP and TTFB to the collect endpoint with Google's rating buckets pre-applied. The Umami dashboard then surfaces them in its built-in Performance tab with p50/p75/p95 percentiles and per-page / per-device breakdowns — no custom event taxonomy needed. Requires the analytics instance to be on Umami ≥ v3.1.0. The attribute is a no-op on older versions, so no risk to ship before upgrading the dashboard. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
9133ecdcd4 |
feat(mobile): iOS polish — theme-color, safe-area, dvh, tap-highlight
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 12s
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 12s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s
MOB-19: Add a viewport Viewport export with viewportFit: 'cover' and themeColor entries for light (#faf7f2) / dark (#1a1612), plus the appleWebApp metadata for the home-screen status bar style and title. Manifest's stale #3b82f6 theme_color updated to match brand cream. MOB-20: Apply env(safe-area-inset-*) to the sticky chrome — the top header gets max(padding, inset-left/right) so the logo and tab links clear the notch in landscape; the bottom tab bar already had inset-bottom and now also gets inset-left/right. MOB-21: Replace 100vh with 100dvh in body min-height, modal max-heights, the map view container, and the fullscreen map. Older engines fall back via the duplicated vh declaration. MOB-22: Set -webkit-tap-highlight-color: transparent on body to suppress the iOS Safari grey flash; add a generic touch-pointer :active rule (opacity 0.7) so taps still register visually on plain anchors and bare buttons. Components with their own :active styling are unaffected. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
5eff9af69c |
feat: add secondary school support with KS4 data and metric tooltips
Build and Push Docker Images / Build Frontend (Next.js) (push) Has been cancelled
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Has been cancelled
Build and Push Docker Images / Trigger Portainer Update (push) Has been cancelled
Build and Push Docker Images / Build Backend (FastAPI) (push) Has been cancelled
- Backend: replace INNER JOIN ks2 with UNION ALL (ks2 + ks4) so primary and secondary schools both appear in the main DataFrame - Backend: add /api/national-averages endpoint computing means from live data, replacing the hardcoded NATIONAL_AVG constant on the frontend - Backend: add phase filter param to /api/schools; return phases from /api/filters; fix hardcoded "phase": "Primary" in school detail endpoint - Backend: add KS4 metric definitions (Attainment 8, Progress 8, EBacc, English & Maths pass rates) to METRIC_DEFINITIONS and RANKING_COLUMNS - Frontend: SchoolDetailView is now phase-aware — secondary schools show a GCSE Results section (Att8, P8, E&M, EBacc) instead of SATs; phonics tab hidden for secondary; admissions says Year 7 instead of Year 3; history table shows KS4 columns; chart datasets switch for secondary - Frontend: new MetricTooltip component (CSS-only ⓘ icon) backed by METRIC_EXPLANATIONS — added to RWM, GPS, SEN, EAL, IDACI, progress scores and all KS4 metrics throughout SchoolDetailView and SchoolCard - Frontend: METRIC_EXPLANATIONS extended with KS4 terms (Attainment 8, Progress 8, EBacc) and previously missing terms (SEN, EHCP, EAL, IDACI) - Frontend: SchoolCard expands "RWM" to "Reading, Writing & Maths" and shows Attainment 8 / English & Maths Grade 4+ for secondary schools - Frontend: FilterBar adds Phase dropdown (Primary / Secondary / All-through) - Frontend: HomeView hero copy updated; compact list shows phase-aware metric - Global metadata updated to remove "primary only" framing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
8aca0a7a53 |
feat(ui): site-wide UX/UI audit — unified buttons, tokens, accessibility
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 35s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 1m10s
Build and Push Docker Images / Build Integrator (push) Successful in 57s
Build and Push Docker Images / Build Kestra Init (push) Successful in 31s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 0s
- Add shared button system (.btn-primary/secondary/tertiary/active) in globals.css
- Replace 40+ hardcoded rgba() values with design tokens across all CSS modules
- Add skip link, :focus-visible indicators, and ARIA landmarks
- Standardise button labels ("+ Compare" / "✓ Comparing") across all components
- Add collapse/minimize toggle to ComparisonToast
- Fix heading hierarchy (h3→h2 in ComparisonView)
- Add aria-live on search results, aria-label on trend SVGs
- Add "Search" nav link, fix footer empty section, unify max-widths
- Darken --text-muted for WCAG AA compliance (4.6:1 contrast ratio)
- Net reduction of ~180 lines through button style deduplication
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
ad7380dba5 |
feat(ux): implement UX audit recommendations
- Redesign landing page with unified Omnibox search - Add ComparisonToast for better comparison flow visibility - Add visual 'Added' state to SchoolCard - Add info tooltips to educational metrics - Optimize mobile map view with Bottom Sheet - Standardize distance display to miles |
||
|
|
6a95445f5e |
Add Umami analytics script to Next.js layout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
19e5199443 |
Improve professional appearance: logo, favicon, and remove emoji icons
1. Added original favicon - Copied favicon.svg from original frontend - Added favicon reference to layout metadata - Professional icon with brand colors 2. Updated logo in navigation - Replaced emoji with proper SVG logo from original design - Uses circular target design with crosshairs - Matches brand identity with coral accent color 3. Removed emoji icons throughout app for professional look - Removed 📍 (location pin) from school locations - Removed 🏫 (school building) from school types - Removed 🔢 from URN labels and section headings - Kept meaningful symbols (✓, +) in buttons only - Updated map popup button color to brand coral (#e07256) Components updated: - Navigation: Professional SVG logo - HomeView: Clean location banner - SchoolDetailView: No decorative emojis in metadata - ComparisonView: Text-only school information - SchoolSearchModal: Clean school listings - LeafletMapInner: Professional map popups Result: More polished, professional appearance suitable for educational data platform Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> |
||
|
|
ef4932b553 |
Match original warm editorial design
- Copy complete original styles.css (1900+ lines) to globals.css - Add Google Fonts (DM Sans and Playfair Display) via next/font - Use CSS variables for fonts - Restore warm color palette (#faf7f2 bg, coral/teal accents) - Restore noise overlay texture - Restore all original animations and transitions - Match original card styles, buttons, modals Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> |
||
|
|
ff7f5487e6 |
Complete Next.js migration with SSR and Docker deployment
- Migrate from vanilla JavaScript SPA to Next.js 16 with App Router - Add server-side rendering for all pages (Home, Compare, Rankings) - Create individual school pages with dynamic routing (/school/[urn]) - Implement Chart.js and Leaflet map integrations - Add comprehensive SEO with sitemap, robots.txt, and JSON-LD - Set up Docker multi-service architecture (PostgreSQL, FastAPI, Next.js) - Update CI/CD pipeline to build both backend and frontend images - Fix Dockerfile to include devDependencies for TypeScript compilation - Add Jest testing configuration - Implement performance optimizations (code splitting, caching) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> |