From 9447b80bc6bfc802f7dd22266da2dbc7955c73db Mon Sep 17 00:00:00 2001 From: Tudor Date: Mon, 13 Jul 2026 19:13:18 +0100 Subject: [PATCH] docs: compare mockups as frontend design source + rebuild plan Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB --- .../2026-07-13-compare-frontend-rebuild.md | 287 ++++++++ .../specs/mockups/compare-desktop.html | 642 ++++++++++++++++++ .../specs/mockups/compare-mobile.html | 437 ++++++++++++ 3 files changed, 1366 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-13-compare-frontend-rebuild.md create mode 100644 docs/superpowers/specs/mockups/compare-desktop.html create mode 100644 docs/superpowers/specs/mockups/compare-mobile.html diff --git a/docs/superpowers/plans/2026-07-13-compare-frontend-rebuild.md b/docs/superpowers/plans/2026-07-13-compare-frontend-rebuild.md new file mode 100644 index 0000000..dcb165d --- /dev/null +++ b/docs/superpowers/plans/2026-07-13-compare-frontend-rebuild.md @@ -0,0 +1,287 @@ +# Compare Screen Frontend Rebuild Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Rebuild `/compare` in the Next.js app to match the approved mockups — parent-first sections (At a glance / Ofsted / Academics / Getting a place / Who goes there / Explore trends), England-average anchoring with provenance-correct labels, mobile-first measure-first layout — consuming the enriched `/api/compare` payload from PR #34, with e2e journeys updated in the same PR (they are the promotion gate). + +**Architecture:** `ComparisonView` becomes an assembly of section components fed by one enriched fetch. All comprehension rules from the two expert reviews live in a pure, jest-tested module (`lib/compareLogic.ts`) — components stay presentational. The mockups are committed at `docs/superpowers/specs/mockups/compare-desktop.html` and `compare-mobile.html`: **all user-facing copy (labels, tooltips, chips, footnote wording) is taken verbatim from them** — they carry two rounds of education-expert review; do not paraphrase. + +**Tech Stack:** Next.js (app router, SSR page + client view), CSS modules, Chart.js (existing `ComparisonChart`), Jest (`npm test` in `nextjs-app/`), Playwright e2e (`e2e/`). + +## Global Constraints + +- **Never push to `main`.** Branch: `feat/compare-frontend-rebuild`. +- **Copy is expert-reviewed:** take it verbatim from the committed mockups. Binding rules (spec §8): Ofsted scale labels come from the API's `report_card[..].label` (never hardcode area labels beyond the mockups'); official DfE numbers say "England average", computed ones say "state-school average (computed from our dataset)"; the 2021/22 chart gap note says "DfE didn't publish school-level figures for 2021/22"; never derive an overall grade from report-card areas; safeguarding never counts as a graded area; "Latest Ofsted inspection", "EHC plans", "at or above capacity", "Over 1 in 4", "first choice (officially 'first preference')". +- **Mobile-first:** the measure-first stacked layout (mobile mockup) is the base CSS; the desktop label-column grid is the `min-width` enhancement. +- **URL contract unchanged:** `?urns=` (and `metric=` now scoped to Explore trends) keep working; share flow, `useComparison` basket, phase tabs, and `compare_viewed`/`compare_metric_changed` analytics events are preserved. +- **Do not run a local server** (CLAUDE.md); verification = jest + `tsc` + the e2e suite against staging after merge. e2e must pass on **staging data** — remember staging has partial history: assert against the *latest* year, never oldest. +- Existing `/api/compare` consumers elsewhere in the app (SchoolDetail links, toasts) must not break — the response is additive, and this PR only rewrites the compare page's own components. +- **Post-v1 (do not build):** IDACI, attendance section, gender-split/absence tier-2 measures, finance (spec §4). + +--- + +### Task 0: Branch + design sources + +- [ ] `git checkout main && git pull && git checkout -b feat/compare-frontend-rebuild` +- [ ] The mockups and this plan are already in the working tree (`docs/superpowers/specs/mockups/compare-{desktop,mobile}.html`) — commit them: `docs: compare mockups as frontend design source + rebuild plan` + +--- + +### Task 1: Types for the enriched payload + +**Files:** +- Modify: `nextjs-app/lib/types.ts` (extend `SchoolResult`, `ComparisonData`, `ComparisonResponse` — located around lines 293-314) + +**Interfaces (produced for every later task):** + +```ts +export interface ReportCardEntry { code: number; label: string; } + +export interface OfstedBlock { + framework: string | null; + inspection_date: string | null; + inspection_type: string | null; + overall_effectiveness: number | null; + grade_source: 'graded' | 'ungraded_carried_forward' | null; + quality_of_education: number | null; + behaviour_attitudes: number | null; + personal_development: number | null; + leadership_management: number | null; + early_years_provision: number | null; + sixth_form_provision: number | null; + rc_safeguarding_met: boolean | null; + report_card: Record; + ofsted_page_url: string; + report_url: string | null; +} + +export interface CensusBlock { + year: number | null; total_pupils: number | null; + female_pupils: number | null; male_pupils: number | null; + fsm_pct: number | null; eal_pct: number | null; +} + +export interface AdmissionsRow { + year: number; school_phase: string | null; + places_offered: number | null; total_applications: number | null; + first_preference_applications: number | null; first_preference_offers: number | null; + first_preference_offer_pct: number | null; oversubscription_ratio: number | null; + oversubscribed: boolean | null; + total_offers: number | null; second_preference_offers: number | null; + third_preference_offers: number | null; + cross_la_applications: number | null; cross_la_offers: number | null; +} + +export interface DeprivationBlock { + lsoa_code: string | null; idaci_score: number | null; idaci_decile: number | null; +} + +export interface BenchmarkBlock { + eal_pct: number | null; sen_support_pct: number | null; + disadvantaged_pct: number | null; median_pupils: number | null; + disadvantaged_rwm_expected_pct?: number | null; +} + +export interface Benchmarks { + source: string; year: number; + primary: BenchmarkBlock; secondary: BenchmarkBlock; +} + +export interface NationalAverages { + year: number; + primary: Record; + secondary: Record; + by_year: Array<{ year: number; primary: Record; secondary: Record }>; +} +``` + +- [ ] **Step 1:** Add the interfaces above; extend `ComparisonData` with optional `ofsted?: OfstedBlock | null; census?: CensusBlock | null; admissions?: AdmissionsRow | null; admissions_history?: AdmissionsRow[]; deprivation?: DeprivationBlock | null;` and `ComparisonResponse` with `national_averages?: NationalAverages; benchmarks?: Benchmarks;` (optional so the UI degrades on an old backend). Extend `SchoolResult` with the ten new yearly columns (`reading_progress_lower_ci` … `maths_progress_upper_ci`, `writing_working_towards_pct`, `progress_8_banding: string | null`, `attainment_8_disadvantage_gap`, `progress_8_disadvantage_gap`). +- [ ] **Step 2:** `cd nextjs-app && npx tsc --noEmit` → clean. Commit: `feat(compare): types for enriched comparison payload` + +--- + +### Task 2: `lib/compareLogic.ts` — the comprehension rules, jest-tested + +**Files:** +- Create: `nextjs-app/lib/compareLogic.ts` +- Test: `nextjs-app/__tests__/lib/compareLogic.test.ts` + +**Interfaces (produced):** + +```ts +export type Verdict = 'above' | 'close' | 'below'; +export function verdict(value: number, anchor: number, tolerance?: number): Verdict; // default tolerance 2pp + +// Report-card summary per spec §4.2: count graded areas per label (best +// first), NAME any 'Needs attention'/'Urgent improvement' area, safeguarding +// separate, "No areas need attention" reassurance when applicable. +export interface ReportCardSummary { + counts: Array<{ label: string; count: number }>; // best grade first + problems: Array<{ areaLabel: string; label: string }>; // named, never counted-away + safeguarding: 'met' | 'not_met' | null; + allClear: boolean; +} +export function summariseReportCard(ofsted: OfstedBlock): ReportCardSummary; + +// One display model for all three inspection regimes. +export type OfstedDisplay = + | { kind: 'none' } + | { kind: 'graded'; grade: number; gradeLabel: string; carriedForward: false } + | { kind: 'carried_forward'; grade: number; gradeLabel: string; carriedForward: true } + | { kind: 'report_card'; summary: ReportCardSummary }; +export function ofstedDisplay(ofsted: OfstedBlock | null | undefined): OfstedDisplay; +export const OFSTED_LEGACY_GRADES: Record; // 1 Outstanding, 2 Good, 3 Requires improvement, 4 Inadequate + +// Human-readable area label from an rc_ key: 'rc_attendance_behaviour' → +// 'Attendance & behaviour' (mapping table copied from the mockups' area rows). +export function rcAreaLabel(key: string): string; + +// Admissions, one consistent chip metric (first-preference success). +export interface AdmissionsSummary { + firstPrefPct: number | null; + chip: { tone: 'good' | 'warn' | 'neutral'; text: string } | null; // "97% of first choices offered" / "Over 1 in 4 first choices missed out" wording per mockups + interest: string | null; // "Named on 457 forms · 180 places" +} +export function summariseAdmissions(a: AdmissionsRow | null | undefined): AdmissionsSummary; + +// CI-based progress band for historical years (null when no CI published). +export function progressBand(score: number | null, lower: number | null, upper: number | null): + 'above' | 'average' | 'below' | null; // CI entirely >0 → above; entirely <0 → below; straddles → average + +// Dot-strip geometry (used by the DotStrip component; pure for testing). +export interface StripPoint { pos: number; labelAbove: boolean; value: number; schoolIndex: number; } +export function stripPositions(values: Array, min: number, max: number): StripPoint[]; +// pos = (v-min)/(max-min)*100 clamped 0..100; labels within 4% of range of a +// lower neighbour flip above (the mockups' collision nudge). +``` + +- [ ] **Step 1: Failing tests** covering, at minimum: + - `summariseReportCard`: 4 Strong + 2 Expected + 1 Needs-attention + safeguarding met → counts `[Strong standard×4, Expected standard×2]`, `problems=[{areaLabel:'Attendance & behaviour', label:'Needs attention'}]`, `allClear=false`; safeguarding NEVER in counts; all-Expected+met → `allClear=true`; labels come from the input's `.label` (assert the function never invents "Attention needed"). + - `ofstedDisplay`: report_card present → `kind:'report_card'` even if a legacy grade also exists; `grade_source:'ungraded_carried_forward'` → `carriedForward:true`; null → `'none'`. + - `summariseAdmissions`: 73% → warn chip text `Over 1 in 4 first choices missed out`; 97% → good chip `97% of first choices offered`; 100% → `All first choices offered`; interest string `Named on 342 forms · 120 places`; nulls → null chip. + - `progressBand`: (1.2, 0.4, 2.0)→above; (-1.2, -2.0, -0.4)→below; (0.3, -0.5, 1.1)→average; missing CI → null. + - `stripPositions`: 100–120 domain maps 106→30; values 91 and 92 on 0–100 → second label flips above; nulls skipped. + - `verdict`: 87 vs 62 → above; 61 vs 62 → close (within 2pp); 40 vs 62 → below. +- [ ] **Step 2:** `cd nextjs-app && npm test -- compareLogic` → FAIL. **Step 3:** implement. **Step 4:** pass + `tsc` clean. **Step 5:** Commit: `feat(compare): comprehension logic (report cards, admissions, verdicts, strips)` + +--- + +### Task 3: `DotStrip` component + +**Files:** +- Create: `nextjs-app/components/DotStrip.tsx`, `nextjs-app/components/DotStrip.module.css` + +**Interfaces:** + +```ts +export interface DotStripProps { + label: string; + values: Array; // one per school, school order = chart colour order + anchor?: { value: number; label: string } | null; // e.g. {62, "England 62%"} — omit when benchmark absent + min?: number; max?: number; // default 0..100 + unit?: string; // default '%' + tip?: string; // title tooltip on the label + note?: string; // e.g. "(teacher-assessed)" suffix handled by caller in label +} +``` + +- [ ] Render per the mockups' `.strip-row` anatomy: label row, 4px track, England tick + tick label, 16px dots coloured by `CHART_COLORS[index]` with white ring, value labels below (flipped above on collision via `stripPositions`). `role="img"` + `aria-label` enumerating anchor and each school's value (copy the aria pattern from the mockups). CSS module mirrors the mockup styles using the app's CSS variables (`--border-light`, `--text-muted`, etc.). +- [ ] Jest: render with `@testing-library/react` (already configured — see `__tests__/components/SecondarySchoolRow.test.tsx` for the harness pattern): asserts aria-label content, tick present when anchor given, absent otherwise. +- [ ] Commit: `feat(compare): DotStrip with England-average anchor` + +--- + +### Task 4: Section components — At a glance, Ofsted, Getting a place, Who goes there + +**Files:** +- Create: `nextjs-app/components/compare/CompareAtAGlance.tsx` (+ `.module.css`) +- Create: `nextjs-app/components/compare/CompareOfsted.tsx` +- Create: `nextjs-app/components/compare/CompareAdmissions.tsx` +- Create: `nextjs-app/components/compare/CompareCommunity.tsx` +- Create: `nextjs-app/components/compare/compareSections.module.css` (shared measure-first grid) +- Test: `nextjs-app/__tests__/components/CompareOfsted.test.tsx` + +**Shared layout contract (all four):** props `{ schools: School[]; data: Record; benchmarks?: Benchmarks; nationalAverages?: NationalAverages }`. Base CSS is the mobile mockup's measure-first stack (`.measure` card → `.srow` per school with colour dot + short name + value + chip + note); at `min-width: 761px` it becomes the desktop mockup's grid (200px row-label column + one column per school). Section headers use the existing `.section-title` idiom; every section carries its mockup "how" line verbatim. + +**Content per section = the mockups, row for row.** Structure/tone rules already encoded in Task 2's helpers: +- *At a glance*: Latest Ofsted inspection row (badge via `ofstedDisplay`; report-card case renders `ReportCardSummary` chips — counts best-first + named problem chips + safeguarding line); expected-standard row (big % + `verdict` chip vs `national_averages.primary.rwm_expected_pct`, small "England average N%"); Getting a place row (chip from `summariseAdmissions`, note = `interest`); Size row (pupils + "at or above capacity"/"N% full" from census/capacity, vs `benchmarks.*.median_pupils` for "larger/smaller than average" phrasing). +- *Ofsted*: the section's `how` paragraph (regime explanation + non-comparability + "Expected standard" disambiguation) verbatim from the desktop mockup; Result row; Inspected row (date + "4+ years ago" chip when >4y, computed from `inspection_date`); Judgement detail row — **one chip-list grammar for both regimes** (legacy subgrades via `OFSTED_LEGACY_GRADES`; report card via `report_card` labels; "We don't hold area-by-area detail for this inspection" when neither); Ofsted page row linking `ofsted_page_url` ("'s Ofsted page →"). +- *Getting a place*: `how` paragraph (first preference/equal preference/offer-day caveats) verbatim; Interest row; first-choice success row with mini bar; "What this means" row (distance note: "check the school's admission criteria (for most non-faith primaries, distance decides)" only when oversubscribed). +- *Who goes there*: pupils-on-roll (census + capacity), girls/boys, FSM (chip vs `benchmarks` with "state-school average" wording), EAL, SEN (tooltip incl. "EHC plans" + specialist-provision note), faith, ages · nursery, run by (trust name or " council"). + +- [ ] **Step 1:** Failing jest test for `CompareOfsted` (the riskiest): given one graded school, one carried-forward, one report-card school → asserts the three Result cells ("Outstanding" badge; badge + carried-forward marker; "Report card" + no invented overall grade), the chip-list judgement rows, and the comparability note appearing only for the mixed case. +- [ ] **Step 2-4:** Implement all four sections; test passes; `tsc` clean; `npm test` full suite green. +- [ ] **Step 5:** Commit: `feat(compare): at-a-glance, Ofsted, admissions and community sections` + +--- + +### Task 5: `CompareAcademics` — strips + More measures + +**Files:** +- Create: `nextjs-app/components/compare/CompareAcademics.tsx` +- Test: extend `nextjs-app/__tests__/lib/compareLogic.test.ts` with the metric-extraction helper below + +**Interfaces:** +- Add to `compareLogic.ts`: `latestValues(data, urns, metricKey) => Array` (latest non-null yearly value per school) — tested. + +- [ ] Tier 1 strips (always visible), each a `DotStrip` with the England anchor from `national_averages.primary`: RWM expected, Reading, Writing, Maths, "Working at a higher standard than expected" (tooltip: composition sentence from the mockups). Section `how` line: "tests and teacher assessments … writing is assessed by teachers, not tested" verbatim. +- [ ] Tier 2 `
` "More measures — grammar, punctuation & spelling, science, average scaled scores": GPS + Science (teacher-assessed, tooltip verbatim) with anchors from `national_averages` **when present, no tick + honest note when null**; scaled scores (reading/maths/GPS) on `min=100 max=120` with the mockups' window caption. +- [ ] Equity row: disadvantaged pupils' RWM per school + chip vs `benchmarks.primary.disadvantaged_rwm_expected_pct` with the "state-school average" wording and small-cohort tooltip verbatim. +- [ ] Secondary phase variant (when active phase is secondary): tier-1 rows are Attainment 8 (anchor `national_averages.secondary.attainment_8_score`), Progress 8 banding (chip showing `progress_8_banding` verbatim — DfE's own label), grade 5+ English & maths %; tier-2: EBacc entry/APS. Measure-first rows (no strips needed for banding). +- [ ] `npm test` + `tsc`; commit: `feat(compare): academics strips with England anchors and More measures` + +--- + +### Task 6: Trends explorer — England line, gap-honest axis, series bug + +**Files:** +- Modify: `nextjs-app/components/ComparisonChart.tsx` +- Create: `nextjs-app/components/compare/TrendsExplorer.tsx` +- Test: `nextjs-app/__tests__/components/ComparisonChart.test.tsx` + +- [ ] **Step 1 (bug first): root-cause the missing third series** seen on production (3 schools in table, 2 lines on chart). Write a failing jest test: 3 schools whose `yearly_data` year values are floats (`202425.0`) vs the labels array — the suspect is the year-matching in `ComparisonChart.tsx:69` (`years.map(...)` built from school 1 only + strict equality against other schools' years). Fix so every school's series renders and years are the union of all schools' years, sorted. +- [ ] **Step 2:** Add optional `nationalByYear?: Record` prop → dashed grey "England average" dataset (colour `--text-muted`, `borderDash:[5,4]`, no fill, `spanGaps:false`). +- [ ] **Step 3:** Gap honesty: x-axis category labels include 2019/20 and 2020/21 as empty slots (band label "tests cancelled 2019/20–2020/21" via a Chart.js annotation-free approach: two category ticks with all-null data and a subtitle note under the chart, copy verbatim: the chart footnote "DfE didn't publish school-level figures for 2021/22" appears when the metric is a KS2 measure and 2021/22 school values are null while the England value exists). `spanGaps:false` on school datasets so dataset gaps break lines. +- [ ] **Step 4:** `TrendsExplorer` wraps the grouped metric picker (existing optgroup structure and `metrics` from `/api/metrics`, existing analytics event) + the chart + the existing year-by-year table, inside a collapsed-by-default `
` ("Explore trends"). Progress metrics annotate cells with `progressBand` chips for years where CIs exist. +- [ ] Tests pass; commit: `feat(compare): trends explorer with England line; fix missing series` + +--- + +### Task 7: Assemble the new `ComparisonView` + +**Files:** +- Rewrite: `nextjs-app/components/ComparisonView.tsx` (+ its `.module.css`) +- Modify: `nextjs-app/app/compare/page.tsx` metadata description (mention Ofsted/admissions, not just KS2) + +- [ ] Preserve intact: `useComparison` basket seeding/URL sync (lines 76-122 of the current file), share handler, phase tabs + auto-detection, `compare_viewed` analytics, empty states, `SchoolSearchModal`, max-4-visible column scroll. Replace the metric-picker/chart/table body with the section stack: sticky school chip bar (mockup `.school-bar`) → `CompareAtAGlance` → `CompareOfsted` → `CompareAcademics` → `CompareAdmissions` → `CompareCommunity` → `TrendsExplorer`. The page-level `metric` URL param now initialises `TrendsExplorer`'s picker only. +- [ ] Top-of-page subtitle + sources footnote verbatim from the mockups (minus the "Mockup" banner), including the suppression rule sentence and provenance sentence. +- [ ] `npm test` full suite + `tsc` clean. Commit: `feat(compare): parent-first compare screen assembly` + +--- + +### Task 8: e2e journeys (the promotion gate) + +**Files:** +- Modify: `e2e/tests/journeys.spec.ts` (the two compare tests, lines ~141-215; extend, don't delete coverage) + +- [ ] Update 'comparing two schools shows both side by side': after loading `/compare?urns=…` assert the new section headings (`At a glance`, `Ofsted inspection`, `How children do academically`, `Getting a place`, `Who goes there`, `Explore trends`), both school names in the sticky bar, at least one England-average tick label (`text=/England \d+%/`), and one provenance string `state-school average` somewhere (benchmarks row). Data-invariant style — no exact numbers (staging data shifts; use latest-year values only). +- [ ] Update the mobile test: 390px viewport, assert measure-first stacking (a `.measure`-card contains all selected school names within one card) and that the trends chart container scrolls (`overflow-x`). +- [ ] Add a report-card presence-agnostic assertion: the Ofsted section renders either a grade badge or "Report card" without an overall grade — i.e. never both an overall-grade badge AND report-card chips for the same school. +- [ ] Run against staging from the host if reachable (`cd e2e && BASE_URL=https://stx.schoolcompare.co.uk npx playwright test -g "compar"`) — staging still runs the OLD UI until this PR merges, so expect failures locally; the authoritative run is the Stage pipeline post-merge. Still commit only after jest+tsc are green. +- [ ] Commit: `test(e2e): compare journeys for the parent-first redesign` + +--- + +### Task 9: PR + post-merge verification + +- [ ] Full gates: `cd nextjs-app && npm test && npx tsc --noEmit`. +- [ ] Push; open PR via Gitea API (credential-helper basic auth). PR body: before/after summary, link to mockups + spec §4/§8, the copy-verbatim rule, the fixed third-series bug, deploy note (needs PR #34's API on the same environment — merge order: #34 first), and that the e2e suite is the staging gate. +- [ ] Post-merge: watch the Stage pipeline — its e2e run against staging is the real verification. Then the human tests staging and promotes (two-stage model). Update memory: compare redesign shipped to staging. + +--- + +## Out of scope + +- IDACI / attendance / gender-absence / finance (post-v1, spec §4). +- Backend changes of any kind (PR #34 must merge first). +- Chart palette overhaul beyond the England-line addition (`CHART_COLORS` swap to the validated trio is a candidate follow-up, flagged not included — it affects every chart in the app). diff --git a/docs/superpowers/specs/mockups/compare-desktop.html b/docs/superpowers/specs/mockups/compare-desktop.html new file mode 100644 index 0000000..e4c9d06 --- /dev/null +++ b/docs/superpowers/specs/mockups/compare-desktop.html @@ -0,0 +1,642 @@ +Compare screen — proposed redesign + + +
+

Mockup — proposed redesign of /compare. All figures are live production data for three real schools (2024/25 results, 2026/27 admissions round). England averages for test results are official DfE figures; other benchmarks are state-school averages computed from our dataset.

+ +

Compare schools

+

Three schools side by side — inspection results, academics, admissions and community, each anchored against the England average so you can tell at a glance what's typical and what stands out.

+ +
+
+ + Barclay Primary School
Waltham Forest · Academy
+ +
+
+ + Elmhurst Primary School
Newham · Academy
+ +
+
+ + Plumcroft Primary School
Greenwich · Community school
+ +
+ +
+ + +
+

At a glance

+

The short version — each row below is explained in its own section further down.

+
+
Latest Ofsted inspection
+
OutstandingInspected Oct 2021
+
OutstandingInspected Oct 2021
+
+ Report card illustrative +
+ 4 areas Strong standard + 2 areas Expected standard + Attendance & behaviour: Attention needed +
+ Safeguarding met · Nov 2025 +
+ +
Children reaching the expected standard ?
+
87% Above England averageEngland average 62%
+
92% Above England averageEngland average 62%
+
79% Above England averageEngland average 62%
+ +
Getting a place
+
97% of first choices offeredNamed on 457 forms · 180 places
+
73% of first choices offeredNamed on 342 forms · 120 places
+
All first choices offeredNamed on 185 forms · 80 places
+ +
Size
+
1,273 pupilsMuch larger than average
+
980 pupilsMuch larger than average
+
1,056 pupilsMuch larger than average
+
+
+ + +
+

Ofsted inspection

+

Ofsted is the schools inspectorate. It stopped giving a single overall grade in September 2024; inspections between then and November 2025 kept the area-by-area judgements without an overall grade, and from November 2025 new inspections produce a report card rating each area of school life on a five-point scale (Exceptional · Strong standard · Expected standard · Attention needed · Urgent improvement). A report card and an older overall grade aren't directly comparable — Plumcroft's report card below is an illustrative example of the new format, as no school in our dataset has one yet. (Ofsted's "Expected standard" rating is unrelated to the KS2 "expected standard" test measure further down this page.)

+
+
Result
+
OutstandingOverall grade (older-style inspection)
+
OutstandingOverall grade (older-style inspection)
+
Report card illustrativeNew-style inspection — no overall grade is given
+ +
Inspected
+
7 Oct 2021 4+ years ago
+
6 Oct 2021 4+ years ago
+
14 Nov 2025
+ +
Judgement detail ?
+
We don't hold area-by-area detail for this inspection — see Barclay's Ofsted page for the full report.
+
+
+
Quality of educationOutstanding
+
Behaviour & attitudesOutstanding
+
Personal developmentOutstanding
+
Leadership & managementOutstanding
+
+
+
+
+
AchievementStrong standard
+
Curriculum & teachingStrong standard
+
Attendance & behaviourAttention needed
+
Personal developmentStrong standard
+
InclusionExpected standard
+
Leadership & governanceStrong standard
+
Early yearsExpected standard
+
SafeguardingMet
+
+
+ +
Ofsted page ?
+ + + +
+
+ + +
+

How children do academically

+

Results from national tests and teacher assessments at the end of Year 6 (2024/25) — writing is assessed by teachers, not tested. Each line runs from 0–100%; the grey tick marks the England average, so dots to its right are above average.

+
+
+
+ More measures — grammar, punctuation & spelling, science, average scaled scores +
+

The strips show the 100–120 window of the full 80–120 scaled-score range; 100 is the expected standard, and the strip widens if a school averages below it. England ticks for grammar, punctuation & spelling and science aren't in our dataset yet, and the scaled-score England ticks are indicative — official DfE figures for all of these will be loaded before launch.

+
+
+
+ +
+
Trend, 2015/16 to 2024/25 ?
+
Variable, recently 87%
+
Consistently high
+
Improving since 2022/23
+ +
Children from lower-income families ?
+
86% Well above the 46% state-school average
+
93% Well above the 46% state-school average
+
72% Above the 46% state-school average
+
+
+ + +
+

Getting a place

+

From the most recent admissions round (September 2026 entry). "First choice" means families who ranked the school top of their application form — officially a "first preference". Schools never see your ranking: places are decided only by the school's admission criteria, so listing a school lower down never hurts your chances. These are National Offer Day offers — waiting lists and appeals can change the final intake.

+
+
Interest in the school ?
+
Named on 457 forms · 180 places
+
Named on 342 forms · 120 places
+
Named on 185 forms · 80 places
+ +
First-choice families offered a place
+
97%
+
73% Over 1 in 4 first choices missed out
+
100%
+ +
What this means
+
Nearly every family who put Barclay first got a place.
+
More first-choice applications than places — check the school's admission criteria (for most non-faith primaries, distance decides).
+
Every family who put Plumcroft first got a place.
+
+
+ + +
+

Who goes there

+

The school's community, from the latest school census (2025/26). England averages are shown for context — there's no "right" number here.

+
+
Pupils on roll
+
1,273 1,260 places — at or above capacity
+
980 of 996 places (98% full)
+
1,056 1,050 places — at or above capacity
+ +
Girls / boys
+
51% / 49%
+
48% / 52%
+
51% / 49%
+ +
Free school meals ?
+
26% About the state-school average
+
25% About the state-school average
+
30% A little above average
+ +
English as an additional language ?
+
62%
+
84%
+
20%
+ +
Extra learning support (SEN) ?
+
6%
+
8%
+
28% Well above average
+ +
Faith character
+
None
+
None
+
None
+ +
Ages · nursery
+
3–11 · has a nursery
+
3–11 · has a nursery
+
3–11 · has a nursery
+ +
Run by
+
Lion Academy Trust
+
New Vision Trust
+
Greenwich council
+
+
+ + +
+

Explore trends

+

The full year-by-year explorer — every measure from the current compare page lives on here, grouped, each with its England-average line. Three measures are wired up in this mockup; the rest are shown to convey the catalogue.

+
+ Year-by-year trends, 2015/16 to 2024/25 +
+
+ + + School lines break where a year isn't in our dataset. +
+ +
+
+
+
+ +

+ Sources: DfE Compare School Performance (KS2 results), Ofsted inspection outcomes, DfE school admissions data, school census — all from datasets SchoolCompare already collects. England averages for test results are the official DfE national figures; benchmarks for free school meals, language, SEN, school size and disadvantaged pupils' results are computed across all state schools in our dataset. Following DfE practice, figures based on 5 or fewer pupils are suppressed and shown as "no data". This is a static mockup: tooltips and "Add school" are illustrative, and Plumcroft's Ofsted report card is a made-up example of the November 2025 format (its real latest inspection is Good, June 2023) — no school in our dataset has a report card yet. +

+
+ + diff --git a/docs/superpowers/specs/mockups/compare-mobile.html b/docs/superpowers/specs/mockups/compare-mobile.html new file mode 100644 index 0000000..fbb73f1 --- /dev/null +++ b/docs/superpowers/specs/mockups/compare-mobile.html @@ -0,0 +1,437 @@ +Compare screen — mobile mockup + + +
+

Mobile mockup — proposed /compare. Mobile-first layout: measures stack vertically with all schools under each, so nothing needs horizontal swiping. Same live data as the desktop mockup.

+ +

Compare schools

+

Anchored against the England average — the grey tick — so you can tell what's typical at a glance.

+ +
+ Barclay + Elmhurst + Plumcroft + + Add +
+ +

At a glance

+

The short version — each measure is explained in its own section below.

+ +
+
Latest Ofsted inspection
+
BarclayOutstandingOlder-style inspection, Oct 2021
+
ElmhurstOutstandingOlder-style inspection, Oct 2021
+
Plumcroft4 areas Strong standard 2 areas Expected Attendance & behaviour: Attention needed illustrativeNew-style report card, Nov 2025 · safeguarding met · full detail in the Ofsted section below
+
+ +
+
Children reaching the expected standard ?
+
England average: 62%
+
Barclay87% Above average
+
Elmhurst92% Above average
+
Plumcroft79% Above average
+
+ +
+
Getting a place
+
Barclay97% of first choices offeredNamed on 457 forms · 180 places
+
Elmhurst73% of first choices offeredNamed on 342 forms · 120 places
+
PlumcroftAll first choices offeredNamed on 185 forms · 80 places
+
+ +

Ofsted inspection

+

Ofsted stopped giving a single overall grade in September 2024 (inspections until November 2025 kept the area-by-area judgements); from November 2025 new inspections produce a report card rating each area of school life (Exceptional · Strong standard · Expected standard · Attention needed · Urgent improvement). A report card and an older grade aren't directly comparable. Ofsted's "Expected standard" rating is unrelated to the KS2 test measure below.

+ +
+
Latest inspection
+
BarclayOutstanding4+ years ago7 Oct 2021 · we don't hold area-by-area detail for this inspection · Ofsted page →
+
ElmhurstOutstanding 4+ years ago 6 Oct 2021 +
+
Quality of educationOutstanding
+
Behaviour & attitudesOutstanding
+
Personal developmentOutstanding
+
Leadership & managementOutstanding
+
+ Ofsted page → +
+
PlumcroftReport card illustrative 14 Nov 2025 +
+
AchievementStrong standard
+
Curriculum & teachingStrong standard
+
Attendance & behaviourAttention needed
+
Personal developmentStrong standard
+
InclusionExpected standard
+
Leadership & governanceStrong standard
+
Early yearsExpected standard
+
SafeguardingMet
+
+ Ofsted page → +
+
+ +

How children do academically

+

End of Year 6 national tests and teacher assessments (2024/25) — writing is teacher-assessed. Each line runs 0–100%; the grey tick is the England average.

+
+
+ More measures — grammar, punctuation & spelling, science, scaled scores +
+

Strips show the 100–120 window of the full 80–120 scaled-score range; 100 is the expected standard (the strip widens if a school averages below it). England ticks for GPS and science aren't in our dataset yet, and the scaled-score ticks are indicative — official DfE figures will be loaded before launch.

+
+
+ +
+
Children from lower-income families ?
+
State-school average: 46%
+
Barclay86% Well above average
+
Elmhurst93% Well above average
+
Plumcroft72% Above average
+
+ +

Getting a place

+

September 2026 entry. "First choice" = families who ranked the school top of their form (officially a "first preference"). Schools never see your ranking — places go by the admission criteria alone. Figures are National Offer Day offers; waiting lists and appeals can change the final intake.

+
+
First-choice families offered a place
+
Barclay97%Named on 457 forms · 180 places
+
Elmhurst73% Over 1 in 4 missed outNamed on 342 forms · 120 places — check the school's admission criteria (for most non-faith primaries, distance decides)
+
Plumcroft100%Named on 185 forms · 80 places · every first choice offered
+
+ +

Who goes there

+

From the latest school census (2025/26). No "right" numbers here — just context.

+
+
Pupils on roll
+
Barclay1,273At or above capacity · much larger than average · girls 51% / boys 49%
+
Elmhurst98098% full · much larger than average · girls 48% / boys 52%
+
Plumcroft1,056At or above capacity · much larger than average · girls 51% / boys 49%
+
+
+
Free school meals ?
+
State-school average: 25% (our dataset)
+
Barclay26% About average
+
Elmhurst25% About average
+
Plumcroft30% A little above
+
+
+
English as an additional language · extra learning support (SEN) ?
+
BarclayEAL 62% · SEN 6%
+
ElmhurstEAL 84% · SEN 8%
+
PlumcroftEAL 20% · SEN 28% SEN well above avg
+
+
+
Basics
+
BarclayAges 3–11 · nursery · no faith · Lion Academy Trust
+
ElmhurstAges 3–11 · nursery · no faith · New Vision Trust
+
PlumcroftAges 3–11 · nursery · no faith · Greenwich council
+
+ +

Explore trends

+

Every measure from the current compare page lives on here, grouped. Three are wired up in this mockup. School lines break where a year isn't in our dataset.

+
+ +
+
+ +
+
+

← swipe the chart →

+ +

+ Sources: DfE Compare School Performance, Ofsted inspection outcomes, DfE admissions data, school census — all from datasets SchoolCompare already collects. England averages for test results are official DfE figures; FSM, language, SEN, size and disadvantaged-pupil benchmarks are computed across state schools in our dataset. Plumcroft's Ofsted report card is a made-up example of the November 2025 format (its real latest inspection is Good, June 2023). Following DfE practice, figures based on 5 or fewer pupils are suppressed and shown as "no data". Static mockup — tooltips and "+ Add" are illustrative. +

+
+ +