# SchoolCompare UX/UI Audit — 2026-07-02
## Method summary
Journey-led walk-through of the **live site** (schoolcompare.co.uk) via Playwright browser tools, at two viewports: **390×844 mobile (primary — 56% of traffic)** and **1440×900 desktop** (720×450 used as the 200%-zoom reflow proxy). Five journeys in traffic order — home→find-school (63% of entries), cold landing on a school page (SEO long tail), building a comparison (27% of views), rankings→shortlist (12%), admissions (5%, light pass) — followed by a cross-cutting cohesion pass comparing computed typography, colour, and component styles across all six page types, verified against `nextjs-app` source to distinguish "token exists but bypassed" from "no token exists".
Every page state visited was scanned with **axe-core 4.10.2** (WCAG 2.2 A/AA rule set) plus manual checks: touch targets ≥24px (2.5.8), keyboard order and focus visibility, and 200% reflow. A finding is included only if it cites a Nielsen heuristic, a WCAG 2.2 AA failure, a mobile-usability standard, or an observed task-flow obstruction — no taste-only findings. Priorities are traffic-weighted using the 30-day analytics baseline: entries `/` 63% / `/compare` 20% / `/rankings` 6%; exits `/` 46% / `/compare` 32% / `/rankings` 13%; 56% mobile. Uplift bands are honest small/moderate/large indications — there is no funnel instrumentation to support percentages.
Full evidence trail: `docs/superpowers/specs/2026-07-02-ux-audit-notes/` (six notes files; screenshots referenced by filename live in the session scratchpad, not committed).
## What works today — keep
These are strengths the fixes below must not regress:
- **Name search is genuinely excellent.** "Welland Primary" → the correct school as sole top result, in 3 taps with instant loads — comfortably under the 15s target. Result cards are information-rich (Ofsted grade+year, RWM % with trend arrow, "+12 pts vs national", pupil count, LA) and adapt correctly to phase (Attainment 8 for secondary, RWM for primary).
- **Search is the unmistakable primary action** — above the fold on both viewports, 70px tap target, plain-English placeholder, content order puts the task first.
- **The primary (KS2) school page explains its data in plain English** — "End-of-primary-school tests taken by Year 6 pupils", the in-place "Why is combined lower?" explainer, legends on subject charts. This is the model the secondary template should copy.
- **Everything is anchored to the national average** ("+14 pts", "National avg 39.1") — a non-specialist can judge good/bad without leaving the page.
- **Graceful, honest missing-data handling** — COVID-cancellation notes, "contact the admissions authority" for absent cut-offs; no broken or empty UI anywhere data is missing.
- **Ofsted sections are clear, current, and sourced** — grade + inspection date, the post-Sept-2024 grading-change note, link to the real Ofsted report.
- **Compare's empty state is instructive and actionable** — "No schools selected" + "+ Add Schools to Compare" opening an in-page search modal; the 20% who enter cold can start immediately. The modal supports efficient multi-add (stays open, clears field) and accepts postcodes.
- **State persistence is strong across the site** — compare selections survive navigation (localStorage) and are URL-encoded/deep-linkable; rankings restores filter, metric, *and* scroll position on back-navigation (the classic mobile task-killer check, passed cleanly).
- **Compare's metric selector carries a plain-English caption** — jargon defined at point of use; the chart legend labels series by colour *and* name (WCAG 1.4.1 satisfied).
- **Accessibility fundamentals are partly in place** — working skip link, logical tab order everywhere, strong 2px orange focus outlines on links/buttons, real `
` semantics on rankings, properly labelled rankings filters, clean heading hierarchy on admissions, no horizontal scroll at 200% reflow on any page tested.
- **Admissions content is well-integrated** — reachable in one tap from anywhere, surfaced proactively on the homepage, with 12 stage-tailored cross-links into Search/Compare/Rankings that read as genuinely contextual.
- **Cohesion anchors:** header/footer pixel-identical on all six pages; coral=primary / teal=secondary held without role collisions; rankings and compare share one phase-tab treatment; the deadline chip is byte-for-byte reused between home and admissions.
## Findings
### P0 — Urgent
- **P0.1 — Colour-contrast failures on every main route, including primary data and primary CTAs** *(merges J1-F4, J2-F4, J3-F6, J4-F1, J5-F1)*
- Evidence: axe `color-contrast` (serious) on **all five routes**: home 6 nodes (active nav tab, `.btn`, how-it-works text) + 3 on results (`.btn-primary`, Ofsted badge); school template **13 nodes** (Back button, "View on map", active metric tab); compare 5 nodes (`.btn-primary`, active phase/nav tabs); rankings **100 nodes on desktop** — the teal metric values (`.valueCell strong`) in every row plus "+ Compare" links; admissions 13 nodes including the **deadline countdown chips**. Screenshots `j4-rankings-desktop.png`, `j5-admissions-mobile.png`.
- Criterion: WCAG 2.2 **1.4.3 Contrast (Minimum), AA** — failing on every main page.
- Argument: this is not chrome-only. On rankings the failing text is the ranking score itself — the entire reason a parent is on the page; on admissions it is the days-until-deadline chip — the page's most load-bearing fact; on home/compare it is the primary CTA styling. The mobile-primary audience reads this outdoors on phones, where low contrast bites everyone, not just low-vision users.
- Recommendation: adjust the failing token values once (`--accent-coral` on light text pairings, the teal-on-cream value colour, tint-background label colours) and re-run axe on all five routes. This is a token-level fix, not per-page work.
- Uplift: **accessibility compliance — large improvement** (clears the single biggest violation class, present on 100% of audited pages); secondary small positive effect on all engagement metrics via legibility for the 56% mobile share.
- **P0.2 — School name (H1) is obscured by the map and the "+ Add to Compare" button on mobile — systemic to the school template** *(J2-F1)*
- Evidence: measured bounding boxes on both audited schools — Castle: map bottom 280px / H1 top 272px / compare-button top 286px / H1 bottom 309px; Welland: same pattern — leaving a ~6px legible sliver of the name. Screenshots `j2-school-mobile-fold.png`, `j2-welland-mobile-full.png`. Desktop is unaffected.
- Criterion: Nielsen #1 (visibility of system status) and #8; task-flow obstruction of cold-landing orientation; defeats the intent of WCAG 1.4.10 (content obscured at the primary viewport).
- Argument: school pages are the SEO landing template. A parent arriving from Google asks one question first — "is this the school I searched for?" — and the element that answers it is illegible on the majority viewport. This is the exact first-screen failure that produces an immediate back-to-Google bounce, on the template whose entire job is converting search traffic.
- Recommendation: restack the mobile hero so map, H1, and compare button occupy non-overlapping space (e.g. compare button below the identity block or docked, map height capped above the H1). One template, both school-detail components (see P2.6).
- Uplift: **school-page bounce-back-to-Google — decrease, moderate** (fixes the first-glance orientation on every SEO landing; the page content behind it is already strong, so retention should follow).
- **P0.3 — Postcode search returns a distance-less, unsorted, mixed-phase list** *(J1-F2)*
- Evidence: `/?search=B91+3` → "8 schools found", sort "Relevance", **no distance on any card**, top result a Secondary school, phases interleaved (secondary/independent/primary/sixth-form) — behaviour consistent with a text match on the postcode column rather than a geographic radius search. Screenshot `j1-postcode-results-mobile.png`. The geolocation "Schools near me" button proves proximity machinery exists but the typed-postcode path isn't wired to it.
- Criterion: Nielsen #2 (match between system and the real world) + task-flow obstruction on the highest-traffic path. The placeholder explicitly invites "School name **or postcode**".
- Argument: a parent typing their postcode expects nearest-first with distances. What they get looks broken — no way to judge which school is closest, led by the wrong phase. Home is 63% of entries and 46% of exits; this is the largest observed task-level leak feeding that exit rate, on the discovery path used by exactly the parents who *don't* already know a school name.
- Recommendation: detect postcode-shaped queries, geocode (postcodes.io is already integrated), sort by distance, and show "X.X miles" on each card — i.e. route the postcode text path through the same logic as "Schools near me". Default the phase filter sensibly or surface it prominently on postcode results.
- Uplift: **home 46% exit rate — decrease, moderate-to-large** (repairs the primary discovery task for the know-my-area-not-my-school segment; large band justified because the failure currently reads as "site is broken" rather than "site lacks a feature").
### P1 — High
- **P1.1 — Filter and Sort selects on home results have no accessible name** *(J1-F3)*
- Evidence: axe `select-name` (impact **critical**, 2 nodes) on `/?search=…` — the phase filter and sort selects. (Borderline P0 by the "clear WCAG failure on a main page" rule; held at P1 because it affects one page state and two controls.)
- Criterion: WCAG 2.2 **4.1.2 Name, Role, Value, Level A**.
- Argument: these are the controls a parent uses to narrow a mixed result set to "Primary" — a screen-reader user hears two anonymous comboboxes on the main journey's results view. Rankings labels its identical selects correctly, so this is an omission, not a pattern.
- Recommendation: associate visible `